Repository: prebid/prebid-server
Pull request #3507 (2024-02-15 13:13:50)

Fix go vet composite literals with unkeyed fields
Fix go vet 'composite literals with unkeyed fields' issue by applying `gopls fix -a -w <file.go>` and some reformatting the structure initialization to be multiline with multiple fields. Part of the fix for #3481.

Reference: https://github.com/golang/go/issues/53062


Patch:
diff --git a/.github/workflows/helpers/pull-request-utils.js b/.github/workflows/helpers/pull-request-utils.js
index 8b470618..73c80396 100644
--- a/.github/workflows/helpers/pull-request-utils.js
+++ b/.github/workflows/helpers/pull-request-utils.js
@@ -414,6 +414,7 @@ class userHelper {
     this.owner = input.context.repo.owner
     this.repo = input.context.repo.repo
     this.github = input.github
+    this.user = input.user
   }
 
   /*
@@ -424,9 +425,8 @@ class userHelper {
     const { data } = await this.github.rest.repos.getCollaboratorPermissionLevel({
       owner: this.owner,
       repo: this.repo,
-      username: this.owner,
+      username: this.user,
     })
-    console.log(JSON.stringify(data))
     return data.permission === writePermission || data.permission === adminPermission
   }
 }
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 92d6a589..d3559732 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -39,18 +39,11 @@ jobs:
           result-encoding: string
           script: |
             const utils = require('./.github/workflows/helpers/pull-request-utils.js')
-            const helper = utils.userHelper({github, context})
+            const helper = utils.userHelper({github, context, user: '${{ github.actor }}'})
             const hasPermission = await helper.hasWritePermissions()
             return hasPermission
     outputs:
       hasWritePermission: ${{ steps.check.outputs.result }}
-  
-  debug-step:
-    name: Debug step
-    runs-on: ubuntu-latest
-    steps:
-      - name: Debug
-        run: echo ${{ needs.check-permission.outputs.hasWritePermission }}
 
   build-master:
     name: Build master
@@ -71,7 +64,6 @@ jobs:
   publish-tag:
     name: Publish tag
     needs: build-master
-    if: contains(needs.check-permission.outputs.hasWritePermission, 'true')
     permissions:
       contents: write
     runs-on: ubuntu-latest
diff --git a/adapters/bwx/bwx.go b/adapters/bwx/bwx.go
new file mode 100644
index 00000000..bab1fb7d
--- /dev/null
+++ b/adapters/bwx/bwx.go
@@ -0,0 +1,158 @@
+package bwx
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"text/template"
+
+	"github.com/prebid/openrtb/v20/openrtb2"
+	"github.com/prebid/prebid-server/v2/adapters"
+	"github.com/prebid/prebid-server/v2/config"
+	"github.com/prebid/prebid-server/v2/errortypes"
+	"github.com/prebid/prebid-server/v2/macros"
+	"github.com/prebid/prebid-server/v2/openrtb_ext"
+)
+
+type bidType struct {
+	Type string `json:"type"`
+}
+
+type bidExt struct {
+	Prebid bidType `json:"prebid"`
+}
+
+type adapter struct {
+	endpoint *template.Template
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	tmpl, 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: tmpl,
+	}
+
+	return bidder, nil
+}
+
+func (a *adapter) buildEndpointFromRequest(imp *openrtb2.Imp) (string, error) {
+	var impExt adapters.ExtImpBidder
+	if err := json.Unmarshal(imp.Ext, &impExt); err != nil {
+		return "", &errortypes.BadInput{
+			Message: fmt.Sprintf("Failed to deserialize bidder impression extension: %v", err),
+		}
+	}
+
+	var boldwinxExt openrtb_ext.ExtBWX
+	if err := json.Unmarshal(impExt.Bidder, &boldwinxExt); err != nil {
+		return "", &errortypes.BadInput{
+			Message: fmt.Sprintf("Failed to deserialize BoldwinX extension: %v", err),
+		}
+	}
+
+	endpointParams := macros.EndpointTemplateParams{
+		Host:     boldwinxExt.Env,
+		SourceId: boldwinxExt.Pid,
+	}
+
+	return macros.ResolveMacros(a.endpoint, endpointParams)
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var requests []*adapters.RequestData
+	var errs []error
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+
+	requestCopy := *request
+	for _, imp := range request.Imp {
+		requestCopy.Imp = []openrtb2.Imp{imp}
+
+		endpoint, err := a.buildEndpointFromRequest(&imp)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		requestJSON, err := json.Marshal(requestCopy)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		request := &adapters.RequestData{
+			Method:  http.MethodPost,
+			Body:    requestJSON,
+			Uri:     endpoint,
+			Headers: headers,
+		}
+
+		requests = append(requests, request)
+	}
+
+	return requests, errs
+}
+
+func (a *adapter) MakeBids(openRTBRequest *openrtb2.BidRequest, requestToBidder *adapters.RequestData, bidderRawResponse *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if adapters.IsResponseStatusCodeNoContent(bidderRawResponse) {
+		return nil, nil
+	}
+
+	if err := adapters.CheckResponseStatusCodeForErrors(bidderRawResponse); err != nil {
+		return nil, []error{err}
+	}
+
+	var bidResp openrtb2.BidResponse
+	if err := json.Unmarshal(bidderRawResponse.Body, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+
+	if len(bidResp.SeatBid) == 0 {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Array SeatBid cannot be empty",
+		}}
+	}
+
+	return prepareBidResponse(bidResp.SeatBid)
+}
+
+func prepareBidResponse(seats []openrtb2.SeatBid) (*adapters.BidderResponse, []error) {
+	var errs []error
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(seats))
+
+	for _, seatBid := range seats {
+		for idx, bid := range seatBid.Bid {
+			bidType, err := getMediaTypeForBid(bid)
+			if err != nil {
+				errs = append(errs, err)
+				continue
+			}
+
+			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+				Bid:     &seatBid.Bid[idx],
+				BidType: bidType,
+			})
+		}
+	}
+
+	return bidResponse, errs
+}
+
+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.MarkupNative:
+		return openrtb_ext.BidTypeNative, nil
+	default:
+		return "", fmt.Errorf("failed to parse bid mtype (%d) for impression id \"%s\"", bid.MType, bid.ImpID)
+	}
+}
diff --git a/amp/parse.go b/amp/parse.go
index 54dcc8e0..12663ee9 100644
--- a/amp/parse.go
+++ b/amp/parse.go
@@ -73,7 +73,7 @@ func ReadPolicy(ampParams Params, pbsConfigGDPREnabled bool) (privacy.PolicyWrit
 			warningMsg = validateTCf2ConsentString(ampParams.Consent)
 		}
 	case ConsentUSPrivacy:
-		rv = ccpa.ConsentWriter{ampParams.Consent}
+		rv = ccpa.ConsentWriter{Consent: ampParams.Consent}
 		if ccpa.ValidateConsent(ampParams.Consent) {
 			if parseGdprApplies(ampParams.GdprApplies) == 1 {
 				// Log warning because AMP request comes with both a valid CCPA string and gdpr_applies set to true
@@ -85,7 +85,7 @@ func ReadPolicy(ampParams Params, pbsConfigGDPREnabled bool) (privacy.PolicyWrit
 		}
 	default:
 		if ccpa.ValidateConsent(ampParams.Consent) {
-			rv = ccpa.ConsentWriter{ampParams.Consent}
+			rv = ccpa.ConsentWriter{Consent: ampParams.Consent}
 			if parseGdprApplies(ampParams.GdprApplies) == 1 {
 				warningMsg = "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string"
 			}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 639ea93a..e188d293 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -59,6 +59,7 @@ import (
 	"github.com/prebid/prebid-server/v2/adapters/bmtm"
 	"github.com/prebid/prebid-server/v2/adapters/boldwin"
 	"github.com/prebid/prebid-server/v2/adapters/brave"
+	"github.com/prebid/prebid-server/v2/adapters/bwx"
 	cadentaperturemx "github.com/prebid/prebid-server/v2/adapters/cadent_aperture_mx"
 	"github.com/prebid/prebid-server/v2/adapters/ccx"
 	"github.com/prebid/prebid-server/v2/adapters/coinzilla"
@@ -260,6 +261,7 @@ 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.BidderBWX:               bwx.Builder,
 		openrtb_ext.BidderCadentApertureMX:  cadentaperturemx.Builder,
 		openrtb_ext.BidderCcx:               ccx.Builder,
 		openrtb_ext.BidderCoinzilla:         coinzilla.Builder,
diff --git a/exchange/auction.go b/exchange/auction.go
index c02e9141..94f0121d 100644
--- a/exchange/auction.go
+++ b/exchange/auction.go
@@ -108,7 +108,7 @@ func (d *DebugLog) PutDebugLogError(cache prebid_cache_client.Client, timeout in
 
 func newAuction(seatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, numImps int, preferDeals bool) *auction {
 	winningBids := make(map[string]*entities.PbsOrtbBid, numImps)
-	winningBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid, numImps)
+	allBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid, numImps)
 
 	for bidderName, seatBid := range seatBids {
 		if seatBid != nil {
@@ -118,10 +118,10 @@ func newAuction(seatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, nu
 					winningBids[bid.Bid.ImpID] = bid
 				}
 
-				if bidMap, ok := winningBidsByBidder[bid.Bid.ImpID]; ok {
+				if bidMap, ok := allBidsByBidder[bid.Bid.ImpID]; ok {
 					bidMap[bidderName] = append(bidMap[bidderName], bid)
 				} else {
-					winningBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+					allBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 						bidderName: {bid},
 					}
 				}
@@ -130,8 +130,8 @@ func newAuction(seatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, nu
 	}
 
 	return &auction{
-		winningBids:         winningBids,
-		winningBidsByBidder: winningBidsByBidder,
+		winningBids:     winningBids,
+		allBidsByBidder: allBidsByBidder,
 	}
 }
 
@@ -151,7 +151,7 @@ func isNewWinningBid(bid, wbid *openrtb2.Bid, preferDeals bool) bool {
 func (a *auction) validateAndUpdateMultiBid(adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, preferDeals bool, accountDefaultBidLimit int) {
 	bidsSnipped := false
 	// sort bids for multibid targeting
-	for _, topBidsPerBidder := range a.winningBidsByBidder {
+	for _, topBidsPerBidder := range a.allBidsByBidder {
 		for bidder, topBids := range topBidsPerBidder {
 			sort.Slice(topBids, func(i, j int) bool {
 				return isNewWinningBid(topBids[i].Bid, topBids[j].Bid, preferDeals)
@@ -187,7 +187,7 @@ func (a *auction) validateAndUpdateMultiBid(adapterBids map[openrtb_ext.BidderNa
 
 func (a *auction) setRoundedPrices(targetingData targetData) {
 	roundedPrices := make(map[*entities.PbsOrtbBid]string, 5*len(a.winningBids))
-	for _, topBidsPerImp := range a.winningBidsByBidder {
+	for _, topBidsPerImp := range a.allBidsByBidder {
 		for _, topBidsPerBidder := range topBidsPerImp {
 			for _, topBid := range topBidsPerBidder {
 				roundedPrices[topBid] = GetPriceBucket(*topBid.Bid, targetingData)
@@ -225,7 +225,7 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client,
 	for _, imp := range bidRequest.Imp {
 		expByImp[imp.ID] = imp.Exp
 	}
-	for impID, topBidsPerImp := range a.winningBidsByBidder {
+	for impID, topBidsPerImp := range a.allBidsByBidder {
 		for bidderName, topBidsPerBidder := range topBidsPerImp {
 			for _, topBid := range topBidsPerBidder {
 				isOverallWinner := a.winningBids[impID] == topBid
@@ -394,8 +394,8 @@ func defTTL(bidType openrtb_ext.BidType, defaultTTLs *config.DefaultTTLs) (ttl i
 type auction struct {
 	// winningBids is a map from imp.id to the highest overall CPM bid in that imp.
 	winningBids map[string]*entities.PbsOrtbBid
-	// winningBidsByBidder stores the highest bid on each imp by each bidder.
-	winningBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
+	// allBidsByBidder is map from ImpID to another map that maps bidderName to all bids from that bidder.
+	allBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
 	// roundedPrices stores the price strings rounded for each bid according to the price granularity.
 	roundedPrices map[*entities.PbsOrtbBid]string
 	// cacheIds stores the UUIDs from Prebid Cache for fetching the full bid JSON.
diff --git a/exchange/bidder.go b/exchange/bidder.go
index 96f8ac9b..8b54f984 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -523,8 +523,6 @@ func (bidder *bidderAdapter) doRequest(ctx context.Context, req *adapters.Reques
 }
 
 func (bidder *bidderAdapter) doRequestImpl(ctx context.Context, req *adapters.RequestData, logger util.LogMsg, bidderRequestStartTime time.Time, tmaxAdjustments *TmaxAdjustmentsPreprocessed) *httpCallInfo {
-	var requestBody []byte
-
 	requestBody, err := getRequestBody(req, bidder.config.EndpointCompression)
 	if err != nil {
 		return &httpCallInfo{
@@ -532,7 +530,7 @@ func (bidder *bidderAdapter) doRequestImpl(ctx context.Context, req *adapters.Re
 			err:     err,
 		}
 	}
-	httpReq, err := http.NewRequest(req.Method, req.Uri, bytes.NewBuffer(requestBody))
+	httpReq, err := http.NewRequest(req.Method, req.Uri, requestBody)
 	if err != nil {
 		return &httpCallInfo{
 			request: req,
@@ -746,18 +744,16 @@ func hasShorterDurationThanTmax(ctx bidderTmaxContext, tmaxAdjustments TmaxAdjus
 	return false
 }
 
-func getRequestBody(req *adapters.RequestData, endpointCompression string) ([]byte, error) {
-	var requestBody []byte
-
+func getRequestBody(req *adapters.RequestData, endpointCompression string) (*bytes.Buffer, error) {
 	switch strings.ToUpper(endpointCompression) {
 	case Gzip:
 		// Compress to GZIP
-		var b bytes.Buffer
+		b := bytes.NewBuffer(make([]byte, 0, len(req.Body)))
 
 		w := gzipWriterPool.Get().(*gzip.Writer)
 		defer gzipWriterPool.Put(w)
 
-		w.Reset(&b)
+		w.Reset(b)
 		_, err := w.Write(req.Body)
 		if err != nil {
 			return nil, err
@@ -766,14 +762,13 @@ func getRequestBody(req *adapters.RequestData, endpointCompression string) ([]by
 		if err != nil {
 			return nil, err
 		}
-		requestBody = b.Bytes()
 
 		// Set Header
 		req.Headers.Set("Content-Encoding", "gzip")
 
-		return requestBody, nil
+		return b, nil
 	default:
-		return req.Body, nil
+		return bytes.NewBuffer(req.Body), nil
 	}
 }
 
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 7c604886..003ce559 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -574,7 +574,7 @@ func applyDealSupport(bidRequest *openrtb2.BidRequest, auc *auction, bidCategory
 	errs := []error{}
 	impDealMap := getDealTiers(bidRequest)
 
-	for impID, topBidsPerImp := range auc.winningBidsByBidder {
+	for impID, topBidsPerImp := range auc.allBidsByBidder {
 		impDeal := impDealMap[impID]
 		for bidder, topBidsPerBidder := range topBidsPerImp {
 			bidderNormalized, bidderFound := openrtb_ext.NormalizeBidderName(bidder.String())
diff --git a/exchange/targeting.go b/exchange/targeting.go
index 0047ba8b..d5ce7152 100644
--- a/exchange/targeting.go
+++ b/exchange/targeting.go
@@ -26,6 +26,7 @@ type targetData struct {
 	includeCacheVast          bool
 	includeFormat             bool
 	preferDeals               bool
+	alwaysIncludeDeals        bool
 	// cacheHost and cachePath exist to supply cache host and path as targeting parameters
 	cacheHost string
 	cachePath string
@@ -38,7 +39,7 @@ type targetData struct {
 // it's ok if those stay in the auction. For now, this method implements a very naive cache strategy.
 // In the future, we should implement a more clever retry & backoff strategy to balance the success rate & performance.
 func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMapping map[string]string, truncateTargetAttr *int, multiBidMap map[string]openrtb_ext.ExtMultiBid) {
-	for impId, topBidsPerImp := range auc.winningBidsByBidder {
+	for impId, topBidsPerImp := range auc.allBidsByBidder {
 		overallWinner := auc.winningBids[impId]
 		for originalBidderName, topBidsPerBidder := range topBidsPerImp {
 			targetingBidderCode := originalBidderName
@@ -61,40 +62,42 @@ func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMappi
 
 				isOverallWinner := overallWinner == topBid
 
+				bidHasDeal := len(topBid.Bid.DealID) > 0
+
 				targets := make(map[string]string, 10)
 				if cpm, ok := auc.roundedPrices[topBid]; ok {
-					targData.addKeys(targets, openrtb_ext.HbpbConstantKey, cpm, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbpbConstantKey, cpm, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
-				targData.addKeys(targets, openrtb_ext.HbBidderConstantKey, string(targetingBidderCode), targetingBidderCode, isOverallWinner, truncateTargetAttr)
+				targData.addKeys(targets, openrtb_ext.HbBidderConstantKey, string(targetingBidderCode), targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				if hbSize := makeHbSize(topBid.Bid); hbSize != "" {
-					targData.addKeys(targets, openrtb_ext.HbSizeConstantKey, hbSize, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbSizeConstantKey, hbSize, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				if cacheID, ok := auc.cacheIds[topBid.Bid]; ok {
-					targData.addKeys(targets, openrtb_ext.HbCacheKey, cacheID, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbCacheKey, cacheID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				if vastID, ok := auc.vastCacheIds[topBid.Bid]; ok {
-					targData.addKeys(targets, openrtb_ext.HbVastCacheKey, vastID, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbVastCacheKey, vastID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				if targData.includeFormat {
-					targData.addKeys(targets, openrtb_ext.HbFormatKey, string(topBid.BidType), targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbFormatKey, string(topBid.BidType), targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 
 				if targData.cacheHost != "" {
-					targData.addKeys(targets, openrtb_ext.HbConstantCacheHostKey, targData.cacheHost, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbConstantCacheHostKey, targData.cacheHost, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				if targData.cachePath != "" {
-					targData.addKeys(targets, openrtb_ext.HbConstantCachePathKey, targData.cachePath, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbConstantCachePathKey, targData.cachePath, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 
-				if deal := topBid.Bid.DealID; len(deal) > 0 {
-					targData.addKeys(targets, openrtb_ext.HbDealIDConstantKey, deal, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+				if bidHasDeal {
+					targData.addKeys(targets, openrtb_ext.HbDealIDConstantKey, topBid.Bid.DealID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 
 				if isApp {
-					targData.addKeys(targets, openrtb_ext.HbEnvKey, openrtb_ext.HbEnvKeyApp, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbEnvKey, openrtb_ext.HbEnvKeyApp, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				if len(categoryMapping) > 0 {
-					targData.addKeys(targets, openrtb_ext.HbCategoryDurationKey, categoryMapping[topBid.Bid.ID], targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbCategoryDurationKey, categoryMapping[topBid.Bid.ID], targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				topBid.BidTargets = targets
 			}
@@ -102,7 +105,7 @@ func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMappi
 	}
 }
 
-func (targData *targetData) addKeys(keys map[string]string, key openrtb_ext.TargetingKey, value string, bidderName openrtb_ext.BidderName, overallWinner bool, truncateTargetAttr *int) {
+func (targData *targetData) addKeys(keys map[string]string, key openrtb_ext.TargetingKey, value string, bidderName openrtb_ext.BidderName, overallWinner bool, truncateTargetAttr *int, bidHasDeal bool) {
 	var maxLength int
 	if truncateTargetAttr != nil {
 		maxLength = *truncateTargetAttr
@@ -112,7 +115,7 @@ func (targData *targetData) addKeys(keys map[string]string, key openrtb_ext.Targ
 	} else {
 		maxLength = MaxKeyLength
 	}
-	if targData.includeBidderKeys {
+	if targData.includeBidderKeys || (targData.alwaysIncludeDeals && bidHasDeal) {
 		keys[key.BidderKey(bidderName, maxLength)] = value
 	}
 	if targData.includeWinners && overallWinner {
diff --git a/exchange/utils.go b/exchange/utils.go
index 87358068..676c015a 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -879,6 +879,7 @@ func getExtTargetData(requestExtPrebid *openrtb_ext.ExtRequestPrebid, cacheInstr
 			priceGranularity:          *requestExtPrebid.Targeting.PriceGranularity,
 			mediaTypePriceGranularity: requestExtPrebid.Targeting.MediaTypePriceGranularity,
 			preferDeals:               requestExtPrebid.Targeting.PreferDeals,
+			alwaysIncludeDeals:        requestExtPrebid.Targeting.AlwaysIncludeDeals,
 		}
 	}
 
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 91217f18..173120f7 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -75,6 +75,7 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderBmtm,
 	BidderBoldwin,
 	BidderBrave,
+	BidderBWX,
 	BidderCadentApertureMX,
 	BidderCcx,
 	BidderCoinzilla,
@@ -354,6 +355,7 @@ const (
 	BidderBmtm              BidderName = "bmtm"
 	BidderBoldwin           BidderName = "boldwin"
 	BidderBrave             BidderName = "brave"
+	BidderBWX               BidderName = "bwx"
 	BidderCadentApertureMX  BidderName = "cadent_aperture_mx"
 	BidderCcx               BidderName = "ccx"
 	BidderCoinzilla         BidderName = "coinzilla"
diff --git a/openrtb_ext/imp_bwx.go b/openrtb_ext/imp_bwx.go
new file mode 100644
index 00000000..9181beb0
--- /dev/null
+++ b/openrtb_ext/imp_bwx.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ExtBWX struct {
+	Env string `json:"env"`
+	Pid string `json:"pid"`
+}
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index 90a95a1b..f5b403d6 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -199,6 +199,7 @@ type ExtRequestTargeting struct {
 	DurationRangeSec          []int                     `json:"durationrangesec,omitempty"`
 	PreferDeals               bool                      `json:"preferdeals,omitempty"`
 	AppendBidderNames         bool                      `json:"appendbiddernames,omitempty"`
+	AlwaysIncludeDeals        bool                      `json:"alwaysincludedeals,omitempty"`
 }
 
 type ExtIncludeBrandCategory struct {
diff --git a/static/bidder-info/bwx.yaml b/static/bidder-info/bwx.yaml
new file mode 100644
index 00000000..cf716c38
--- /dev/null
+++ b/static/bidder-info/bwx.yaml
@@ -0,0 +1,19 @@
+endpoint: "http://rtb.boldwin.live?pid={{.SourceId}}&host={{.Host}}&pbs=1"
+maintainer:
+  email: "prebid@bold-win.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
+userSync:
+  # BoldwinX supports user syncing, but requires configuration by the host. contact this
+  # bidder directly at the email address in this file to ask about enabling user sync.
+  supports:
+    - redirect
diff --git a/static/bidder-params/bwx.json b/static/bidder-params/bwx.json
new file mode 100644
index 00000000..873aadad
--- /dev/null
+++ b/static/bidder-params/bwx.json
@@ -0,0 +1,21 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "BoldwinX Adapter Params",
+    "description": "A schema which validates params accepted by the BoldwinX adapter",
+    "type": "object",
+    "properties": {
+      "env": {
+        "type": "string",
+        "description": "BoldwinX environment",
+        "minLength": 1
+      },
+      "pid": {
+        "type": "string",
+        "description": "Unique placement ID",
+        "minLength": 1
+      }
+    },
+    "required": [
+      "pid"
+    ]
+  }


Test patch:
diff --git a/adapters/bwx/bwx_test.go b/adapters/bwx/bwx_test.go
new file mode 100644
index 00000000..4e984008
--- /dev/null
+++ b/adapters/bwx/bwx_test.go
@@ -0,0 +1,34 @@
+package bwx
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/v2/adapters/adapterstest"
+	"github.com/prebid/prebid-server/v2/config"
+	"github.com/prebid/prebid-server/v2/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(
+		openrtb_ext.BidderBWX,
+		config.Adapter{
+			Endpoint: "http://rtb.boldwin.live/?pid={{.SourceId}}&host={{.Host}}&pbs=1",
+		},
+		config.Server{
+			ExternalUrl: "http://hosturl.com",
+			GvlID:       1,
+			DataCenter:  "2",
+		},
+	)
+
+	assert.NoError(t, buildErr)
+	adapterstest.RunJSONBidderTest(t, "bwxtest", bidder)
+}
+
+func TestEndpointTemplateMalformed(t *testing.T) {
+	_, buildErr := Builder(openrtb_ext.BidderBWX, config.Adapter{
+		Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+	assert.Error(t, buildErr)
+}
diff --git a/adapters/bwx/bwxtest/exemplary/banner.json b/adapters/bwx/bwxtest/exemplary/banner.json
new file mode 100644
index 00000000..af82099d
--- /dev/null
+++ b/adapters/bwx/bwxtest/exemplary/banner.json
@@ -0,0 +1,281 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "1",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250,
+          "pos": 0
+        },
+        "ext": {
+          "bidder": {
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      },
+      {
+        "id": "2",
+        "secure": 1,
+        "bidfloor": 0.2,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250,
+          "pos": 4
+        },
+        "ext": {
+          "bidder": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "1",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250,
+                "pos": 0
+              },
+              "ext": {
+                "bidder": {
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "1",
+                  "price": 1.2,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test2</span>",
+                  "adomain": ["test.com"],
+                  "cat": ["IAB1"],
+                  "cid": "cid",
+                  "crid": "crid1",
+                  "w": 300,
+                  "h": 250,
+                  "mtype": 1,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "2",
+              "secure": 1,
+              "bidfloor": 0.2,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250,
+                "pos": 4
+              },
+              "ext": {
+                "bidder": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "2",
+                  "price": 2.4,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test3</span>",
+                  "adomain": ["test.com"],
+                  "cat": ["IAB1"],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "mtype": 1,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "id",
+            "impid": "1",
+            "price": 1.2,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test2</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid1",
+            "w": 300,
+            "h": 250,
+            "mtype": 1,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    },
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "id",
+            "impid": "2",
+            "price": 2.4,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test3</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid",
+            "w": 300,
+            "h": 250,
+            "mtype": 1,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bwx/bwxtest/exemplary/native.json b/adapters/bwx/bwxtest/exemplary/native.json
new file mode 100644
index 00000000..0bba9482
--- /dev/null
+++ b/adapters/bwx/bwxtest/exemplary/native.json
@@ -0,0 +1,164 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "native": {
+          "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+          "ver": "1.1"
+        },
+        "ext": {
+          "bidder": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "native": {
+                "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+                "ver": "1.1"
+              },
+              "ext": {
+                "bidder": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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": "{}",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "mtype": 4,
+                  "ext": {
+                    "prebid": {
+                      "type": "native"
+                    }
+                  }
+                }
+              ],
+              "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": "{}",
+            "adomain": [
+              "test.com"
+            ],
+            "cat": [
+              "IAB1"
+            ],
+            "cid": "cid",
+            "crid": "crid",
+            "mtype": 4,
+            "ext": {
+              "prebid": {
+                "type": "native"
+              }
+            }
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bwx/bwxtest/exemplary/video.json b/adapters/bwx/bwxtest/exemplary/video.json
new file mode 100644
index 00000000..ffe74ee7
--- /dev/null
+++ b/adapters/bwx/bwxtest/exemplary/video.json
@@ -0,0 +1,204 @@
+{
+  "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": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "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": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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,
+                  "ext": {
+                    "prebid": {
+                      "type": "video"
+                    }
+                  }
+                }
+              ],
+              "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,
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bwx/bwxtest/supplemental/bad-response.json b/adapters/bwx/bwxtest/supplemental/bad-response.json
new file mode 100644
index 00000000..8a32dab1
--- /dev/null
+++ b/adapters/bwx/bwxtest/supplemental/bad-response.json
@@ -0,0 +1,106 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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": ""
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bwx/bwxtest/supplemental/empty-mediatype.json b/adapters/bwx/bwxtest/supplemental/empty-mediatype.json
new file mode 100644
index 00000000..ad319e84
--- /dev/null
+++ b/adapters/bwx/bwxtest/supplemental/empty-mediatype.json
@@ -0,0 +1,190 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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": "1",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test1</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "mtype": 1,
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }, {
+                  "id": "id",
+                  "impid": "2",
+                  "price": 1.2,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test2</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "some": "value"
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "failed to parse bid mtype (0) for impression id \"2\"",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency":"USD",
+      "bids":[
+        {
+          "bid": {
+            "id": "id",
+            "impid": "1",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test1</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid",
+            "w": 300,
+            "h": 250,
+            "mtype": 1,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bwx/bwxtest/supplemental/empty-seatbid-0-bid.json b/adapters/bwx/bwxtest/supplemental/empty-seatbid-0-bid.json
new file mode 100644
index 00000000..ce9d7875
--- /dev/null
+++ b/adapters/bwx/bwxtest/supplemental/empty-seatbid-0-bid.json
@@ -0,0 +1,111 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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": [],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [],
+  "expectedBidResponses": [{"currency":"USD","bids":[]}]
+}
diff --git a/adapters/bwx/bwxtest/supplemental/empty-seatbid.json b/adapters/bwx/bwxtest/supplemental/empty-seatbid.json
new file mode 100644
index 00000000..2bb3290f
--- /dev/null
+++ b/adapters/bwx/bwxtest/supplemental/empty-seatbid.json
@@ -0,0 +1,111 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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": "a1580f2f-be6d-11eb-a150-d094662c1c35",
+          "bidid": "359da97d0384d8a14767029c18fd840d",
+          "seatbid": [],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Array SeatBid cannot be empty",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bwx/bwxtest/supplemental/invalid-ext-bidder-object.json b/adapters/bwx/bwxtest/supplemental/invalid-ext-bidder-object.json
new file mode 100644
index 00000000..ed081b79
--- /dev/null
+++ b/adapters/bwx/bwxtest/supplemental/invalid-ext-bidder-object.json
@@ -0,0 +1,49 @@
+{
+  "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": [],
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Failed to deserialize BoldwinX extension: json: cannot unmarshal array into Go value of type openrtb_ext.ExtBWX",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/bwx/bwxtest/supplemental/invalid-ext-object.json b/adapters/bwx/bwxtest/supplemental/invalid-ext-object.json
new file mode 100644
index 00000000..aa215eb3
--- /dev/null
+++ b/adapters/bwx/bwxtest/supplemental/invalid-ext-object.json
@@ -0,0 +1,47 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": ""
+      }
+    ],
+    "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": [],
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Failed to deserialize bidder impression extension: json: cannot unmarshal string into Go value of type adapters.ExtImpBidder",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/bwx/bwxtest/supplemental/invalid-mediatype.json b/adapters/bwx/bwxtest/supplemental/invalid-mediatype.json
new file mode 100644
index 00000000..aff08f53
--- /dev/null
+++ b/adapters/bwx/bwxtest/supplemental/invalid-mediatype.json
@@ -0,0 +1,187 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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": "1",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test1</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "mtype": 1,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }, {
+                  "id": "id",
+                  "impid": "2",
+                  "price": 1.2,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test2</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "failed to parse bid mtype (0) for impression id \"2\"",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency":"USD",
+      "bids":[
+        {
+          "bid": {
+            "id": "id",
+            "impid": "1",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test1</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid",
+            "w": 300,
+            "h": 250,
+            "mtype": 1,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bwx/bwxtest/supplemental/status-204.json b/adapters/bwx/bwxtest/supplemental/status-204.json
new file mode 100644
index 00000000..e89b6e19
--- /dev/null
+++ b/adapters/bwx/bwxtest/supplemental/status-204.json
@@ -0,0 +1,100 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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": 204,
+        "body": {}
+      }
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bwx/bwxtest/supplemental/status-400.json b/adapters/bwx/bwxtest/supplemental/status-400.json
new file mode 100644
index 00000000..20942040
--- /dev/null
+++ b/adapters/bwx/bwxtest/supplemental/status-400.json
@@ -0,0 +1,106 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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": 400,
+        "body": "The Key has a different ad format"
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bwx/bwxtest/supplemental/status-503.json b/adapters/bwx/bwxtest/supplemental/status-503.json
new file mode 100644
index 00000000..3d5a6170
--- /dev/null
+++ b/adapters/bwx/bwxtest/supplemental/status-503.json
@@ -0,0 +1,105 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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": 503
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 503. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bwx/bwxtest/supplemental/unexpected-status.json b/adapters/bwx/bwxtest/supplemental/unexpected-status.json
new file mode 100644
index 00000000..f3d7ecc7
--- /dev/null
+++ b/adapters/bwx/bwxtest/supplemental/unexpected-status.json
@@ -0,0 +1,106 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "boldwinx-stage",
+            "pid": "3163e2c9e034770c0daaa98c7613b573"
+          }
+        }
+      }
+    ],
+    "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": "http://rtb.boldwin.live/?pid=3163e2c9e034770c0daaa98c7613b573&host=boldwinx-stage&pbs=1",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "boldwinx-stage",
+                  "pid": "3163e2c9e034770c0daaa98c7613b573"
+                }
+              }
+            }
+          ],
+          "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,
+        "body": "Access is denied"
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 403. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bwx/params_test.go b/adapters/bwx/params_test.go
new file mode 100644
index 00000000..f51d8630
--- /dev/null
+++ b/adapters/bwx/params_test.go
@@ -0,0 +1,54 @@
+package bwx
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/v2/openrtb_ext"
+)
+
+var validParams = []string{
+	`{"env":"boldwinx-stage", "pid":"123456"}`,
+	`{"pid":"123456"}`,
+}
+
+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.BidderBWX, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected boldwinx params: %s", validParam)
+		}
+	}
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`[]`,
+	`{}`,
+	`{"some": "param"}`,
+	`{"env":"boldwinx-stage"}`,
+	`{"othervalue":"Lorem ipsum"}`,
+	`{"env":"boldwinx-stage", pid:""}`,
+	`{pid:101010}`,
+	`{pid:"valid-pid", env: 0}`,
+}
+
+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.BidderBWX, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
diff --git a/amp/parse_test.go b/amp/parse_test.go
index a377c471..f2f09728 100644
--- a/amp/parse_test.go
+++ b/amp/parse_test.go
@@ -284,18 +284,24 @@ func TestPrivacyReader(t *testing.T) {
 						ampParams: Params{Consent: "1YYY"},
 					},
 					expected: expectedResults{
-						policyWriter: ccpa.ConsentWriter{"1YYY"},
+						policyWriter: ccpa.ConsentWriter{Consent: "1YYY"},
 						warning:      nil,
 					},
 				},
 				{
 					desc: "No consent type, valid CCPA consent string and gdpr_applies set to true: expect a CCPA consent writer and a warning",
 					in: testInput{
-						ampParams: Params{Consent: "1YYY", GdprApplies: &boolTrue},
+						ampParams: Params{
+							Consent:     "1YYY",
+							GdprApplies: &boolTrue,
+						},
 					},
 					expected: expectedResults{
-						policyWriter: ccpa.ConsentWriter{"1YYY"},
-						warning:      &errortypes.Warning{Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+						policyWriter: ccpa.ConsentWriter{Consent: "1YYY"},
+						warning: &errortypes.Warning{
+							Message:     "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string",
+							WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
+						},
 					},
 				},
 				{
@@ -304,8 +310,11 @@ func TestPrivacyReader(t *testing.T) {
 						ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA"},
 					},
 					expected: expectedResults{
-						policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One},
-						warning:      nil,
+						policyWriter: gdpr.ConsentWriter{
+							Consent:    "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
+							RegExtGDPR: &int8One,
+						},
+						warning: nil,
 					},
 				},
 			},
@@ -316,41 +325,63 @@ func TestPrivacyReader(t *testing.T) {
 				{
 					desc: "Unrecognized consent type was specified and invalid consent string provided: expect nil policy writer and a warning",
 					in: testInput{
-						ampParams: Params{ConsentType: 101, Consent: "NOT_CCPA_NOR_GDPR_TCF2"},
+						ampParams: Params{
+							ConsentType: 101,
+							Consent:     "NOT_CCPA_NOR_GDPR_TCF2",
+						},
 					},
 					expected: expectedResults{
 						policyWriter: privacy.NilPolicyWriter{},
-						warning:      &errortypes.Warning{Message: "Consent string 'NOT_CCPA_NOR_GDPR_TCF2' is not recognized as one of the supported formats CCPA or TCF2.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+						warning: &errortypes.Warning{
+							Message:     "Consent string 'NOT_CCPA_NOR_GDPR_TCF2' is not recognized as one of the supported formats CCPA or TCF2.",
+							WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
+						},
 					},
 				},
 				{
 					desc: "Unrecognized consent type specified but query params come with a valid CCPA consent string: expect a CCPA consent writer and no error nor warning",
 					in: testInput{
-						ampParams: Params{ConsentType: 101, Consent: "1YYY"},
+						ampParams: Params{
+							ConsentType: 101,
+							Consent:     "1YYY",
+						},
 					},
 					expected: expectedResults{
-						policyWriter: ccpa.ConsentWriter{"1YYY"},
+						policyWriter: ccpa.ConsentWriter{Consent: "1YYY"},
 						warning:      nil,
 					},
 				},
 				{
 					desc: "Unrecognized consent type, valid CCPA consent string and gdpr_applies set to true: expect a CCPA consent writer and a warning",
 					in: testInput{
-						ampParams: Params{ConsentType: 101, Consent: "1YYY", GdprApplies: &boolTrue},
+						ampParams: Params{
+							ConsentType: 101,
+							Consent:     "1YYY",
+							GdprApplies: &boolTrue,
+						},
 					},
 					expected: expectedResults{
-						policyWriter: ccpa.ConsentWriter{"1YYY"},
-						warning:      &errortypes.Warning{Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+						policyWriter: ccpa.ConsentWriter{Consent: "1YYY"},
+						warning: &errortypes.Warning{
+							Message:     "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string",
+							WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
+						},
 					},
 				},
 				{
 					desc: "Unrecognized consent type, valid TCF2 consent string and gdpr_applies not set: expect GDPR consent writer and no error nor warning",
 					in: testInput{
-						ampParams: Params{ConsentType: 101, Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA"},
+						ampParams: Params{
+							ConsentType: 101,
+							Consent:     "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
+						},
 					},
 					expected: expectedResults{
-						policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One},
-						warning:      nil,
+						policyWriter: gdpr.ConsentWriter{
+							Consent:    "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
+							RegExtGDPR: &int8One,
+						},
+						warning: nil,
 					},
 				},
 			},
@@ -361,51 +392,91 @@ func TestPrivacyReader(t *testing.T) {
 				{
 					desc: "GDPR consent string is invalid, but consent type is TCF2: return a valid GDPR writer and warn about the GDPR string being invalid",
 					in: testInput{
-						ampParams: Params{Consent: "INVALID_GDPR", ConsentType: ConsentTCF2, GdprApplies: nil},
+						ampParams: Params{
+							Consent:     "INVALID_GDPR",
+							ConsentType: ConsentTCF2,
+							GdprApplies: nil,
+						},
 					},
 					expected: expectedResults{
-						policyWriter: gdpr.ConsentWriter{"INVALID_GDPR", &int8One},
-						warning:      &errortypes.Warning{Message: "Consent string 'INVALID_GDPR' is not a valid TCF2 consent string.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+						policyWriter: gdpr.ConsentWriter{
+							Consent:    "INVALID_GDPR",
+							RegExtGDPR: &int8One,
+						},
+						warning: &errortypes.Warning{
+							Message:     "Consent string 'INVALID_GDPR' is not a valid TCF2 consent string.",
+							WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
+						},
 					},
 				},
 				{
 					desc: "GDPR consent string is invalid, consent type is TCF2, gdpr_applies is set to true: return a valid GDPR writer and warn about the GDPR string being invalid",
 					in: testInput{
-						ampParams: Params{Consent: "INVALID_GDPR", ConsentType: ConsentTCF2, GdprApplies: &boolFalse},
+						ampParams: Params{
+							Consent:     "INVALID_GDPR",
+							ConsentType: ConsentTCF2,
+							GdprApplies: &boolFalse,
+						},
 					},
 					expected: expectedResults{
-						policyWriter: gdpr.ConsentWriter{"INVALID_GDPR", &int8Zero},
-						warning:      &errortypes.Warning{Message: "Consent string 'INVALID_GDPR' is not a valid TCF2 consent string.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+						policyWriter: gdpr.ConsentWriter{
+							Consent:    "INVALID_GDPR",
+							RegExtGDPR: &int8Zero,
+						},
+						warning: &errortypes.Warning{
+							Message:     "Consent string 'INVALID_GDPR' is not a valid TCF2 consent string.",
+							WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
+						},
 					},
 				},
 				{
 					desc: "Valid GDPR consent string, gdpr_applies is set to false, return a valid GDPR writer, no warning",
 					in: testInput{
-						ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", ConsentType: ConsentTCF2, GdprApplies: &boolFalse},
+						ampParams: Params{
+							Consent:     "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
+							ConsentType: ConsentTCF2,
+							GdprApplies: &boolFalse,
+						},
 					},
 					expected: expectedResults{
-						policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8Zero},
-						warning:      nil,
+						policyWriter: gdpr.ConsentWriter{
+							Consent:    "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
+							RegExtGDPR: &int8Zero,
+						},
+						warning: nil,
 					},
 				},
 				{
 					desc: "Valid GDPR consent string, gdpr_applies is set to true, return a valid GDPR writer and no warning",
 					in: testInput{
-						ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", ConsentType: ConsentTCF2, GdprApplies: &boolTrue},
+						ampParams: Params{
+							Consent:     "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
+							ConsentType: ConsentTCF2,
+							GdprApplies: &boolTrue,
+						},
 					},
 					expected: expectedResults{
-						policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One},
-						warning:      nil,
+						policyWriter: gdpr.ConsentWriter{
+							Consent:    "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
+							RegExtGDPR: &int8One,
+						},
+						warning: nil,
 					},
 				},
 				{
 					desc: "Valid GDPR consent string, return a valid GDPR writer and no warning",
 					in: testInput{
-						ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", ConsentType: ConsentTCF2},
+						ampParams: Params{
+							Consent:     "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
+							ConsentType: ConsentTCF2,
+						},
 					},
 					expected: expectedResults{
-						policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One},
-						warning:      nil,
+						policyWriter: gdpr.ConsentWriter{
+							Consent:    "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
+							RegExtGDPR: &int8One,
+						},
+						warning: nil,
 					},
 				},
 			},
@@ -416,30 +487,46 @@ func TestPrivacyReader(t *testing.T) {
 				{
 					desc: "CCPA consent string is invalid: return a valid writer a warning about the string being invalid",
 					in: testInput{
-						ampParams: Params{Consent: "XXXX", ConsentType: ConsentUSPrivacy},
+						ampParams: Params{
+							Consent:     "XXXX",
+							ConsentType: ConsentUSPrivacy,
+						},
 					},
 					expected: expectedResults{
-						policyWriter: ccpa.ConsentWriter{"XXXX"},
-						warning:      &errortypes.Warning{Message: "Consent string 'XXXX' is not a valid CCPA consent string.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+						policyWriter: ccpa.ConsentWriter{Consent: "XXXX"},
+						warning: &errortypes.Warning{
+							Message:     "Consent string 'XXXX' is not a valid CCPA consent string.",
+							WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
+						},
 					},
 				},
 				{
 					desc: "Valid CCPA consent string, gdpr_applies is set to true: return a valid GDPR writer and warn about the gdpr_applies value.",
 					in: testInput{
-						ampParams: Params{Consent: "1YYY", ConsentType: ConsentUSPrivacy, GdprApplies: &boolTrue},
+						ampParams: Params{
+							Consent:     "1YYY",
+							ConsentType: ConsentUSPrivacy,
+							GdprApplies: &boolTrue,
+						},
 					},
 					expected: expectedResults{
-						policyWriter: ccpa.ConsentWriter{"1YYY"},
-						warning:      &errortypes.Warning{Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+						policyWriter: ccpa.ConsentWriter{Consent: "1YYY"},
+						warning: &errortypes.Warning{
+							Message:     "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string",
+							WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
+						},
 					},
 				},
 				{
 					desc: "Valid CCPA consent string, return a valid GDPR writer and no warning",
 					in: testInput{
-						ampParams: Params{Consent: "1YYY", ConsentType: ConsentUSPrivacy},
+						ampParams: Params{
+							Consent:     "1YYY",
+							ConsentType: ConsentUSPrivacy,
+						},
 					},
 					expected: expectedResults{
-						policyWriter: ccpa.ConsentWriter{"1YYY"},
+						policyWriter: ccpa.ConsentWriter{Consent: "1YYY"},
 						warning:      nil,
 					},
 				},
@@ -469,19 +556,34 @@ func TestBuildGdprTCF2ConsentWriter(t *testing.T) {
 		expectedWriter gdpr.ConsentWriter
 	}{
 		{
-			desc:           "gdpr_applies not set",
-			inParams:       Params{Consent: consentString},
-			expectedWriter: gdpr.ConsentWriter{consentString, &int8One},
+			desc:     "gdpr_applies not set",
+			inParams: Params{Consent: consentString},
+			expectedWriter: gdpr.ConsentWriter{
+				Consent:    consentString,
+				RegExtGDPR: &int8One,
+			},
 		},
 		{
-			desc:           "gdpr_applies set to false",
-			inParams:       Params{Consent: consentString, GdprApplies: &boolFalse},
-			expectedWriter: gdpr.ConsentWriter{consentString, &int8Zero},
+			desc: "gdpr_applies set to false",
+			inParams: Params{
+				Consent:     consentString,
+				GdprApplies: &boolFalse,
+			},
+			expectedWriter: gdpr.ConsentWriter{
+				Consent:    consentString,
+				RegExtGDPR: &int8Zero,
+			},
 		},
 		{
-			desc:           "gdpr_applies set to true",
-			inParams:       Params{Consent: consentString, GdprApplies: &boolTrue},
-			expectedWriter: gdpr.ConsentWriter{consentString, &int8One},
+			desc: "gdpr_applies set to true",
+			inParams: Params{
+				Consent:     consentString,
+				GdprApplies: &boolTrue,
+			},
+			expectedWriter: gdpr.ConsentWriter{
+				Consent:    consentString,
+				RegExtGDPR: &int8One,
+			},
 		},
 	}
 	for _, tc := range testCases {
diff --git a/exchange/auction_test.go b/exchange/auction_test.go
index 40b1aba9..17224dcd 100644
--- a/exchange/auction_test.go
+++ b/exchange/auction_test.go
@@ -191,7 +191,7 @@ func loadCacheSpec(filename string) (*cacheSpec, error) {
 func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) {
 	var bid *entities.PbsOrtbBid
 	winningBidsByImp := make(map[string]*entities.PbsOrtbBid)
-	winningBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid)
+	allBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid)
 	roundedPrices := make(map[*entities.PbsOrtbBid]string)
 	bidCategory := make(map[string]string)
 
@@ -210,15 +210,15 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) {
 		}
 
 		// Map this bid if it's the highest we've seen from this bidder so far
-		if bidMap, ok := winningBidsByBidder[bid.Bid.ImpID]; ok {
+		if bidMap, ok := allBidsByBidder[bid.Bid.ImpID]; ok {
 			bidMap[pbsBid.Bidder] = append(bidMap[pbsBid.Bidder], bid)
 		} else {
-			winningBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				pbsBid.Bidder: {bid},
 			}
 		}
 
-		for _, topBidsPerBidder := range winningBidsByBidder {
+		for _, topBidsPerBidder := range allBidsByBidder {
 			for _, topBids := range topBidsPerBidder {
 				sort.Slice(topBids, func(i, j int) bool {
 					return isNewWinningBid(topBids[i].Bid, topBids[j].Bid, true)
@@ -263,9 +263,9 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) {
 	}
 
 	testAuction := &auction{
-		winningBids:         winningBidsByImp,
-		winningBidsByBidder: winningBidsByBidder,
-		roundedPrices:       roundedPrices,
+		winningBids:     winningBidsByImp,
+		allBidsByBidder: allBidsByBidder,
+		roundedPrices:   roundedPrices,
 	}
 	evTracking := &eventTracking{
 		accountID:          "TEST_ACC_ID",
@@ -405,7 +405,7 @@ func TestNewAuction(t *testing.T) {
 				winningBids: map[string]*entities.PbsOrtbBid{
 					"imp1": &bid1p230,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p123},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p230},
@@ -433,7 +433,7 @@ func TestNewAuction(t *testing.T) {
 					"imp1": &bid1p230,
 					"imp2": &bid2p144,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p230},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p077},
@@ -462,7 +462,7 @@ func TestNewAuction(t *testing.T) {
 				winningBids: map[string]*entities.PbsOrtbBid{
 					"imp1": &bid1p123,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p123},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p088d},
@@ -486,7 +486,7 @@ func TestNewAuction(t *testing.T) {
 				winningBids: map[string]*entities.PbsOrtbBid{
 					"imp1": &bid1p088d,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p123},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p088d},
@@ -510,7 +510,7 @@ func TestNewAuction(t *testing.T) {
 				winningBids: map[string]*entities.PbsOrtbBid{
 					"imp1": &bid1p166d,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p088d},
@@ -537,7 +537,7 @@ func TestNewAuction(t *testing.T) {
 				winningBids: map[string]*entities.PbsOrtbBid{
 					"imp1": &bid1p166d,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p088d},
@@ -563,7 +563,7 @@ func TestNewAuction(t *testing.T) {
 					"imp1": &bid1p166d,
 					"imp2": &bid2p166,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -645,11 +645,11 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 	}
 
 	type fields struct {
-		winningBids         map[string]*entities.PbsOrtbBid
-		winningBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
-		roundedPrices       map[*entities.PbsOrtbBid]string
-		cacheIds            map[*openrtb2.Bid]string
-		vastCacheIds        map[*openrtb2.Bid]string
+		winningBids     map[string]*entities.PbsOrtbBid
+		allBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
+		roundedPrices   map[*entities.PbsOrtbBid]string
+		cacheIds        map[*openrtb2.Bid]string
+		vastCacheIds    map[*openrtb2.Bid]string
 	}
 	type args struct {
 		adapterBids            map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid
@@ -657,8 +657,8 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 		accountDefaultBidLimit int
 	}
 	type want struct {
-		winningBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
-		adapterBids         map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid
+		allBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
+		adapterBids     map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid
 	}
 	tests := []struct {
 		description string
@@ -673,7 +673,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 					"imp1": &bid1p166d,
 					"imp2": &bid2p166,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p001, &bid1p166d, &bid1p077},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -697,7 +697,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 				preferDeals:            true,
 			},
 			want: want{
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077, &bid1p001},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -724,7 +724,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 					"imp1": &bid1p166d,
 					"imp2": &bid2p166,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p001, &bid1p166d, &bid1p077},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -748,7 +748,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 				preferDeals:            true,
 			},
 			want: want{
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077, &bid1p001},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -775,7 +775,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 					"imp1": &bid1p166d,
 					"imp2": &bid2p166,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p001, &bid1p166d, &bid1p077},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -799,7 +799,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 				preferDeals:            true,
 			},
 			want: want{
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -823,14 +823,14 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
 			a := &auction{
-				winningBids:         tt.fields.winningBids,
-				winningBidsByBidder: tt.fields.winningBidsByBidder,
-				roundedPrices:       tt.fields.roundedPrices,
-				cacheIds:            tt.fields.cacheIds,
-				vastCacheIds:        tt.fields.vastCacheIds,
+				winningBids:     tt.fields.winningBids,
+				allBidsByBidder: tt.fields.allBidsByBidder,
+				roundedPrices:   tt.fields.roundedPrices,
+				cacheIds:        tt.fields.cacheIds,
+				vastCacheIds:    tt.fields.vastCacheIds,
 			}
 			a.validateAndUpdateMultiBid(tt.args.adapterBids, tt.args.preferDeals, tt.args.accountDefaultBidLimit)
-			assert.Equal(t, tt.want.winningBidsByBidder, tt.fields.winningBidsByBidder, tt.description)
+			assert.Equal(t, tt.want.allBidsByBidder, tt.fields.allBidsByBidder, tt.description)
 			assert.Equal(t, tt.want.adapterBids, tt.args.adapterBids, tt.description)
 		})
 	}
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index a1662ab7..9f092b7d 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -3377,11 +3377,11 @@ func TestGetRequestBody(t *testing.T) {
 			if test.endpointCompression == "GZIP" {
 				assert.Equal(t, "gzip", req.Headers.Get("Content-Encoding"))
 
-				decompressedReqBody, err := decompressGzip(requestBody)
+				decompressedReqBody, err := decompressGzip(requestBody.Bytes())
 				assert.NoError(t, err)
 				assert.Equal(t, test.givenReqBody, decompressedReqBody)
 			} else {
-				assert.Equal(t, test.givenReqBody, requestBody)
+				assert.Equal(t, test.givenReqBody, requestBody.Bytes())
 			}
 		})
 	}
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index ee2e0f6e..6cd48198 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -3542,7 +3542,7 @@ func TestApplyDealSupport(t *testing.T) {
 		}
 
 		auc := &auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"imp_id1": {
 					test.in.bidderName: {&bid},
 				},
@@ -3551,8 +3551,8 @@ func TestApplyDealSupport(t *testing.T) {
 
 		dealErrs := applyDealSupport(bidRequest, auc, bidCategory, nil)
 
-		assert.Equal(t, test.expected.hbPbCatDur, bidCategory[auc.winningBidsByBidder["imp_id1"][test.in.bidderName][0].Bid.ID], test.description)
-		assert.Equal(t, test.expected.dealTierSatisfied, auc.winningBidsByBidder["imp_id1"][test.in.bidderName][0].DealTierSatisfied, "expected.dealTierSatisfied=%v when %v", test.expected.dealTierSatisfied, test.description)
+		assert.Equal(t, test.expected.hbPbCatDur, bidCategory[auc.allBidsByBidder["imp_id1"][test.in.bidderName][0].Bid.ID], test.description)
+		assert.Equal(t, test.expected.dealTierSatisfied, auc.allBidsByBidder["imp_id1"][test.in.bidderName][0].DealTierSatisfied, "expected.dealTierSatisfied=%v when %v", test.expected.dealTierSatisfied, test.description)
 		if len(test.expected.dealErr) > 0 {
 			assert.Containsf(t, dealErrs, errors.New(test.expected.dealErr), "Expected error message not found in deal errors")
 		}
@@ -3593,7 +3593,7 @@ func TestApplyDealSupportMultiBid(t *testing.T) {
 					},
 				},
 				auc: &auction{
-					winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+					allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 						"imp_id1": {
 							openrtb_ext.BidderName("appnexus"): {
 								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
@@ -3639,7 +3639,7 @@ func TestApplyDealSupportMultiBid(t *testing.T) {
 					},
 				},
 				auc: &auction{
-					winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+					allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 						"imp_id1": {
 							openrtb_ext.BidderName("appnexus"): {
 								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
@@ -3690,7 +3690,7 @@ func TestApplyDealSupportMultiBid(t *testing.T) {
 					},
 				},
 				auc: &auction{
-					winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+					allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 						"imp_id1": {
 							openrtb_ext.BidderName("appnexus"): {
 								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
@@ -3729,7 +3729,7 @@ func TestApplyDealSupportMultiBid(t *testing.T) {
 			errs := applyDealSupport(tt.args.bidRequest, tt.args.auc, tt.args.bidCategory, tt.args.multiBid)
 			assert.Equal(t, tt.want.errs, errs)
 
-			for impID, topBidsPerImp := range tt.args.auc.winningBidsByBidder {
+			for impID, topBidsPerImp := range tt.args.auc.allBidsByBidder {
 				for bidder, topBidsPerBidder := range topBidsPerImp {
 					for i, topBid := range topBidsPerBidder {
 						assert.Equal(t, tt.want.expectedHbPbCatDur[impID][bidder.String()][i], tt.args.bidCategory[topBid.Bid.ID], tt.name)
diff --git a/exchange/exchangetest/targeting-always-include-deals.json b/exchange/exchangetest/targeting-always-include-deals.json
new file mode 100644
index 00000000..1ffb8088
--- /dev/null
+++ b/exchange/exchangetest/targeting-always-include-deals.json
@@ -0,0 +1,296 @@
+{
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "site": {
+        "page": "test.somepage.com"
+      },
+      "imp": [
+        {
+          "id": "my-imp-id",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
+          },
+          "ext": {
+            "prebid": {
+              "bidder": {
+                "appnexus": {
+                  "placementId": 1
+                },
+                "audienceNetwork": {
+                  "placementId": "some-placement"
+                }
+              }
+            }
+          }
+        },
+        {
+          "id": "imp-id-2",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
+          },
+          "ext": {
+            "prebid": {
+              "bidder": {
+                "appnexus": {
+                  "placementId": 2
+                },
+                "audienceNetwork": {
+                  "placementId": "some-other-placement"
+                }
+              }
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+          "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": false,
+            "alwaysincludedeals": true
+          }
+        }
+      }
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "mockResponse": {
+        "pbsSeatBids": [
+          {
+            "pbsBids": [
+              {
+                "ortbBid": {
+                  "id": "winning-bid",
+                  "impid": "my-imp-id",
+                  "price": 0.71,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-1",
+                  "dealid": "deal-1"
+                },
+                "bidType": "video"
+              },
+              {
+                "ortbBid": {
+                  "id": "losing-bid",
+                  "impid": "my-imp-id",
+                  "price": 0.21,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-2",
+                  "dealid": "deal-2"
+                },
+                "bidType": "video"
+              },
+              {
+                "ortbBid": {
+                  "id": "other-bid",
+                  "impid": "imp-id-2",
+                  "price": 0.61,
+                  "w": 300,
+                  "h": 500,
+                  "crid": "creative-3",
+                  "dealid": "deal-3"
+                },
+                "bidType": "video"
+              }
+            ],
+            "seat": "appnexus"
+          }
+        ]
+      }
+    },
+    "audienceNetwork": {
+      "mockResponse": {
+        "pbsSeatBids": [
+          {
+            "pbsBids": [
+              {
+                "ortbBid": {
+                  "id": "contending-bid",
+                  "impid": "my-imp-id",
+                  "price": 0.51,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-4",
+                  "dealid": "deal-4"
+                },
+                "bidType": "video"
+              },
+              {
+                "ortbBid": {
+                  "id": "losing-bid-aN",
+                  "impid": "imp-id-2",
+                  "price": 0.40,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-5"
+                },
+                "bidType": "video"
+              }
+            ],
+            "seat": "audienceNetwork"
+          }
+        ]
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "audienceNetwork",
+          "bid": [
+            {
+              "id": "contending-bid",
+              "impid": "my-imp-id",
+              "price": 0.51,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-4",
+              "dealid": "deal-4",
+              "ext": {
+                "origbidcpm": 0.51,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "audienceNetwork"
+                  },
+                  "type": "video",
+                  "targeting": {
+                    "hb_bidder_audienceNe": "audienceNetwork",
+                    "hb_cache_host_audien": "www.pbcserver.com",
+                    "hb_cache_path_audien": "/pbcache/endpoint",
+                    "hb_deal_audienceNetw": "deal-4",
+                    "hb_pb_audienceNetwor": "0.50",
+                    "hb_size_audienceNetw": "200x250"
+                  }
+                }
+              }
+            },
+            {
+              "id": "losing-bid-aN",
+              "impid": "imp-id-2",
+              "price": 0.40,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-5",
+              "ext": {
+                "origbidcpm": 0.40,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "audienceNetwork"
+                  },
+                  "type": "video"
+                }
+              }
+            }
+          ]
+        },
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "winning-bid",
+              "impid": "my-imp-id",
+              "price": 0.71,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "dealid": "deal-1",
+              "ext": {
+                "origbidcpm": 0.71,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "type": "video",
+                  "targeting": {
+                    "hb_bidder": "appnexus",
+                    "hb_bidder_appnexus": "appnexus",
+                    "hb_cache_host": "www.pbcserver.com",
+                    "hb_cache_host_appnex": "www.pbcserver.com",
+                    "hb_cache_path": "/pbcache/endpoint",
+                    "hb_cache_path_appnex": "/pbcache/endpoint",
+                    "hb_pb": "0.70",
+                    "hb_pb_appnexus": "0.70",
+                    "hb_deal":"deal-1",
+                    "hb_deal_appnexus":"deal-1",
+                    "hb_size": "200x250",
+                    "hb_size_appnexus": "200x250"
+                  }
+                }
+              }
+            },
+            {
+              "id": "losing-bid",
+              "impid": "my-imp-id",
+              "price": 0.21,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-2",
+              "dealid": "deal-2",
+              "ext": {
+                "origbidcpm": 0.21,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "type": "video"
+                }
+              }
+            },
+            {
+              "id": "other-bid",
+              "impid": "imp-id-2",
+              "price": 0.61,
+              "w": 300,
+              "h": 500,
+              "crid": "creative-3",
+              "dealid": "deal-3",
+              "ext": {
+                "origbidcpm": 0.61,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "type": "video",
+                  "targeting": {
+                    "hb_bidder": "appnexus",
+                    "hb_bidder_appnexus": "appnexus",
+                    "hb_cache_host": "www.pbcserver.com",
+                    "hb_cache_host_appnex": "www.pbcserver.com",
+                    "hb_cache_path": "/pbcache/endpoint",
+                    "hb_cache_path_appnex": "/pbcache/endpoint",
+                    "hb_deal":"deal-3",
+                    "hb_deal_appnexus":"deal-3",
+                    "hb_pb": "0.60",
+                    "hb_pb_appnexus": "0.60",
+                    "hb_size": "300x500",
+                    "hb_size_appnexus": "300x500"
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  }
+}
diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go
index c124178e..e6cc429e 100644
--- a/exchange/targeting_test.go
+++ b/exchange/targeting_test.go
@@ -319,6 +319,11 @@ var bid2p166 *openrtb2.Bid = &openrtb2.Bid{
 	Price: 1.66,
 }
 
+var bid175 *openrtb2.Bid = &openrtb2.Bid{
+	Price:  1.75,
+	DealID: "mydeal2",
+}
+
 var (
 	truncateTargetAttrValue10       int = 10
 	truncateTargetAttrValue5        int = 5
@@ -339,7 +344,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeWinners:   true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -374,7 +379,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeBidderKeys: true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -409,6 +414,54 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 		},
 		TruncateTargetAttr: nil,
 	},
+	{
+		Description: "Targeting with alwaysIncludeDeals",
+		TargetData: targetData{
+			priceGranularity:   lookupPriceGranularity("med"),
+			alwaysIncludeDeals: true,
+		},
+		Auction: auction{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				"ImpId-1": {
+					openrtb_ext.BidderAppnexus: {{
+						Bid:     bid111,
+						BidType: openrtb_ext.BidTypeBanner,
+					}},
+					openrtb_ext.BidderRubicon: {{
+						Bid:     bid175,
+						BidType: openrtb_ext.BidTypeBanner,
+					}},
+					openrtb_ext.BidderPubmatic: {{
+						Bid:     bid123,
+						BidType: openrtb_ext.BidTypeBanner,
+					}},
+				},
+			},
+		},
+		ExpectedPbsBids: map[string]map[openrtb_ext.BidderName][]ExpectedPbsBid{
+			"ImpId-1": {
+				openrtb_ext.BidderAppnexus: []ExpectedPbsBid{
+					{
+						BidTargets: map[string]string{
+							"hb_bidder_appnexus": "appnexus",
+							"hb_pb_appnexus":     "1.10",
+							"hb_deal_appnexus":   "mydeal",
+						},
+					},
+				},
+				openrtb_ext.BidderRubicon: []ExpectedPbsBid{
+					{
+						BidTargets: map[string]string{
+							"hb_bidder_rubicon": "rubicon",
+							"hb_pb_rubicon":     "1.70",
+							"hb_deal_rubicon":   "mydeal2",
+						},
+					},
+				},
+			},
+		},
+		TruncateTargetAttr: nil,
+	},
 	{
 		Description: "Full basic targeting with hd_format",
 		TargetData: targetData{
@@ -418,7 +471,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeFormat:     true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -467,7 +520,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			cachePath:         "cache",
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -520,7 +573,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeBidderKeys: true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -550,7 +603,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeBidderKeys: true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -592,7 +645,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeBidderKeys: true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -634,7 +687,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeBidderKeys: true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -676,7 +729,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeWinners:   true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -711,7 +764,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeWinners:   true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -746,7 +799,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeWinners:   true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -783,7 +836,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeFormat:     true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {
 						{
@@ -918,7 +971,7 @@ func TestSetTargeting(t *testing.T) {
 		auc.setRoundedPrices(test.TargetData)
 		winningBids := make(map[string]*entities.PbsOrtbBid)
 		// Set winning bids from the auction data
-		for imp, bidsByBidder := range auc.winningBidsByBidder {
+		for imp, bidsByBidder := range auc.allBidsByBidder {
 			for _, bids := range bidsByBidder {
 				for _, bid := range bids {
 					if winningBid, ok := winningBids[imp]; ok {
@@ -939,12 +992,12 @@ func TestSetTargeting(t *testing.T) {
 				for i, expected := range expectedTargets {
 					assert.Equal(t,
 						expected.BidTargets,
-						auc.winningBidsByBidder[imp][bidder][i].BidTargets,
+						auc.allBidsByBidder[imp][bidder][i].BidTargets,
 						"Test: %s\nTargeting failed for bidder %s on imp %s.",
 						test.Description,
 						string(bidder),
 						imp)
-					assert.Equal(t, expected.TargetBidderCode, auc.winningBidsByBidder[imp][bidder][i].TargetBidderCode)
+					assert.Equal(t, expected.TargetBidderCode, auc.allBidsByBidder[imp][bidder][i].TargetBidderCode)
 				}
 			}
 		}