Repository: prebid/prebid-server
Pull request #1651 (2021-01-13 03:54:15)

Bubble up GDPR signal/consent errors while applying privacy policies


Patch:
diff --git a/adapters/beachfront/beachfront.go b/adapters/beachfront/beachfront.go
index 72c0ef4b..1f81eda0 100644
--- a/adapters/beachfront/beachfront.go
+++ b/adapters/beachfront/beachfront.go
@@ -24,7 +24,7 @@ const defaultVideoEndpoint = "https://reachms.bfmio.com/bid.json?exchange_id"
 const nurlVideoEndpointSuffix = "&prebidserver"
 
 const beachfrontAdapterName = "BF_PREBID_S2S"
-const beachfrontAdapterVersion = "0.9.0"
+const beachfrontAdapterVersion = "0.9.1"
 
 const minBidFloor = 0.01
 
@@ -202,8 +202,8 @@ func preprocess(request *openrtb.BidRequest) (beachfrontReqs beachfrontRequests,
 	var bannerImps = make([]openrtb.Imp, 0)
 
 	for i := 0; i < len(request.Imp); i++ {
-		if request.Imp[i].Banner != nil && ((request.Imp[i].Banner.Format[0].H != 0 && request.Imp[i].Banner.Format[0].W != 0) ||
-			(request.Imp[i].Banner.H != nil && request.Imp[i].Banner.W != nil)) {
+		if request.Imp[i].Banner != nil && request.Imp[i].Banner.Format != nil &&
+			request.Imp[i].Banner.Format[0].H != 0 && request.Imp[i].Banner.Format[0].W != 0 {
 			bannerImps = append(bannerImps, request.Imp[i])
 		}
 
@@ -232,11 +232,11 @@ func preprocess(request *openrtb.BidRequest) (beachfrontReqs beachfrontRequests,
 		errs = append(errs, videoErrs...)
 
 		for i := 0; i < len(videoList); i++ {
-			if videoList[i].VideoResponseType == "nurl" || videoList[i].VideoResponseType == "both" {
+			if videoList[i].VideoResponseType == "nurl" {
 				beachfrontReqs.NurlVideo = append(beachfrontReqs.NurlVideo, videoList[i])
 			}
 
-			if videoList[i].VideoResponseType == "adm" || videoList[i].VideoResponseType == "both" {
+			if videoList[i].VideoResponseType == "adm" {
 				beachfrontReqs.ADMVideo = append(beachfrontReqs.ADMVideo, videoList[i])
 			}
 		}
@@ -412,7 +412,7 @@ func getVideoRequests(request *openrtb.BidRequest) ([]beachfrontVideoRequest, []
 		if beachfrontExt.VideoResponseType != "" {
 			bfReqs[i].VideoResponseType = beachfrontExt.VideoResponseType
 		} else {
-			bfReqs[i].VideoResponseType = "nurl"
+			bfReqs[i].VideoResponseType = "adm"
 		}
 
 		bfReqs[i].Request = *request
@@ -436,7 +436,7 @@ func getVideoRequests(request *openrtb.BidRequest) ([]beachfrontVideoRequest, []
 
 		}
 
-		if bfReqs[i].Request.Device.DeviceType == 0 {
+		if bfReqs[i].Request.Device != nil && bfReqs[i].Request.Device.DeviceType == 0 {
 			// More fine graned deviceType methods will be added in the future
 			bfReqs[i].Request.Device.DeviceType = fallBackDeviceType(request)
 		}
diff --git a/adapters/dmx/dmx.go b/adapters/dmx/dmx.go
index 24966b82..c66c6bf3 100644
--- a/adapters/dmx/dmx.go
+++ b/adapters/dmx/dmx.go
@@ -31,12 +31,21 @@ type dmxExt struct {
 	Bidder dmxParams `json:"bidder"`
 }
 
+type dmxPubExt struct {
+	Dmx dmxPubExtId `json:"dmx,omitempty"`
+}
+
+type dmxPubExtId struct {
+	Id string `json:"id,omitempty"`
+}
+
 type dmxParams struct {
-	TagId       string `json:"tagid,omitempty"`
-	DmxId       string `json:"dmxid,omitempty"`
-	MemberId    string `json:"memberid,omitempty"`
-	PublisherId string `json:"publisher_id,omitempty"`
-	SellerId    string `json:"seller_id,omitempty"`
+	TagId       string  `json:"tagid,omitempty"`
+	DmxId       string  `json:"dmxid,omitempty"`
+	MemberId    string  `json:"memberid,omitempty"`
+	PublisherId string  `json:"publisher_id,omitempty"`
+	SellerId    string  `json:"seller_id,omitempty"`
+	Bidfloor    float64 `json:"bidfloor,omitempty"`
 }
 
 func UserSellerOrPubId(str1, str2 string) string {
@@ -55,6 +64,7 @@ func (adapter *DmxAdapter) MakeRequests(request *openrtb.BidRequest, req *adapte
 	var anyHasId = false
 	var reqCopy openrtb.BidRequest = *request
 	var dmxReq *openrtb.BidRequest = &reqCopy
+	var dmxRawPubId dmxPubExt
 
 	if request.User == nil {
 		if request.App == nil {
@@ -77,6 +87,16 @@ func (adapter *DmxAdapter) MakeRequests(request *openrtb.BidRequest, req *adapte
 		appPublisherCopy := *request.App.Publisher
 		dmxReq.App = &appCopy
 		dmxReq.App.Publisher = &appPublisherCopy
+		if dmxReq.App.Publisher.ID == "" {
+			dmxReq.App.Publisher.ID = publisherId
+		}
+		dmxRawPubId.Dmx.Id = UserSellerOrPubId(rootExtInfo.Bidder.PublisherId, rootExtInfo.Bidder.MemberId)
+		ext, err := json.Marshal(dmxRawPubId)
+		if err != nil {
+			errs = append(errs, fmt.Errorf("unable to marshal ext, %v", err))
+			return nil, errs
+		}
+		dmxReq.App.Publisher.Ext = ext
 		if dmxReq.App.ID != "" {
 			anyHasId = true
 		}
@@ -90,7 +110,16 @@ func (adapter *DmxAdapter) MakeRequests(request *openrtb.BidRequest, req *adapte
 		dmxReq.Site = &siteCopy
 		dmxReq.Site.Publisher = &sitePublisherCopy
 		if dmxReq.Site.Publisher != nil {
-			dmxReq.Site.Publisher.ID = publisherId
+			if dmxReq.Site.Publisher.ID == "" {
+				dmxReq.Site.Publisher.ID = publisherId
+			}
+			dmxRawPubId.Dmx.Id = UserSellerOrPubId(rootExtInfo.Bidder.PublisherId, rootExtInfo.Bidder.MemberId)
+			ext, err := json.Marshal(dmxRawPubId)
+			if err != nil {
+				errs = append(errs, fmt.Errorf("unable to marshal ext, %v", err))
+				return nil, errs
+			}
+			dmxReq.Site.Publisher.Ext = ext
 		} else {
 			dmxReq.Site.Publisher = &openrtb.Publisher{ID: publisherId}
 		}
@@ -229,6 +258,9 @@ func (adapter *DmxAdapter) MakeBids(request *openrtb.BidRequest, externalRequest
 func fetchParams(params dmxExt, inst openrtb.Imp, ins openrtb.Imp, imps []openrtb.Imp, banner *openrtb.Banner, video *openrtb.Video, intVal int8) []openrtb.Imp {
 	var tempimp openrtb.Imp
 	tempimp = inst
+	if params.Bidder.Bidfloor != 0 {
+		tempimp.BidFloor = params.Bidder.Bidfloor
+	}
 	if params.Bidder.TagId != "" {
 		tempimp.TagID = params.Bidder.TagId
 		tempimp.Secure = &intVal
diff --git a/adapters/emx_digital/emx_digital.go b/adapters/emx_digital/emx_digital.go
index a219481c..e87f0681 100644
--- a/adapters/emx_digital/emx_digital.go
+++ b/adapters/emx_digital/emx_digital.go
@@ -152,8 +152,10 @@ func buildImpVideo(imp *openrtb.Imp) error {
 		}
 	}
 
-	if imp.Video.Protocols != nil {
-		imp.Video.Protocols = cleanProtocol(imp.Video.Protocols)
+	if len(imp.Video.Protocols) > 0 {
+		videoCopy := *imp.Video
+		videoCopy.Protocols = cleanProtocol(imp.Video.Protocols)
+		imp.Video = &videoCopy
 	}
 
 	return nil
diff --git a/config/config.go b/config/config.go
index a35b3c2a..6577c1fb 100755
--- a/config/config.go
+++ b/config/config.go
@@ -216,6 +216,9 @@ func (cfg *GDPR) validate(errs []error) []error {
 	if cfg.AMPException == true {
 		errs = append(errs, fmt.Errorf("gdpr.amp_exception has been discontinued and must be removed from your config. If you need to disable GDPR for AMP, you may do so per-account (gdpr.integration_enabled.amp) or at the host level for the default account (account_defaults.gdpr.integration_enabled.amp)"))
 	}
+	if cfg.TCF1.FetchGVL == true {
+		glog.Warning("gdpr.tcf1.fetch_gvl is deprecated and will be removed in a future version, at which point TCF1 will always use the fallback GVL")
+	}
 	return errs
 }
 
@@ -234,7 +237,7 @@ func (t *GDPRTimeouts) ActiveTimeout() time.Duration {
 
 // TCF1 defines the TCF1 specific configurations for GDPR
 type TCF1 struct {
-	FetchGVL        bool   `mapstructure:"fetch_gvl"`
+	FetchGVL        bool   `mapstructure:"fetch_gvl"` // Deprecated: In a future version TCF1 will always use the fallback GVL
 	FallbackGVLPath string `mapstructure:"fallback_gvl_path"`
 }
 
@@ -587,7 +590,7 @@ func (cfg *Configuration) setDerivedDefaults() {
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/match/bounce/current?version=1&networkId=72582&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderCpmstar, "https://server.cpmstar.com/usersync.aspx?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDatablocks, "https://sync.v5prebid.datablocks.net/s2ssync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D")
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDmx, "https://dmx.districtm.io/s/v1/img/s/10007?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D%24%7Bgdpr%7D%26gdpr_consent%3D%24%7Bgdpr_consent%7D%26uid%3D%24%7Buid%7D")
+	// openrtb_ext.BidderDMX doesn't have a good default.
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDeepintent, "https://match.deepintent.com/usersync/136?id=unk&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddeepintent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5BUID%5D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEmxDigital, "https://cs.emxdgt.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Demx_digital%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEngageBDR, "https://match.bnmla.com/usersync/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dengagebdr%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D")
diff --git a/endpoints/events/vtrack.go b/endpoints/events/vtrack.go
index c4051554..9a4d9c08 100644
--- a/endpoints/events/vtrack.go
+++ b/endpoints/events/vtrack.go
@@ -4,6 +4,12 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"time"
+
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
 	accountService "github.com/prebid/prebid-server/account"
@@ -13,12 +19,6 @@ import (
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/prebid_cache_client"
 	"github.com/prebid/prebid-server/stored_requests"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"sort"
-	"strings"
-	"time"
 )
 
 const (
@@ -230,7 +230,7 @@ func (v *vtrackEndpoint) cachePutObjects(ctx context.Context, req *BidCacheReque
 		}
 
 		if _, ok := biddersAllowingVastUpdate[c.Bidder]; ok && nc.Data != nil {
-			nc.Data = modifyVastXml(v.Cfg.ExternalURL, nc.Data, c.BidID, c.Bidder, accountId, c.Timestamp)
+			nc.Data = ModifyVastXmlJSON(v.Cfg.ExternalURL, nc.Data, c.BidID, c.Bidder, accountId, c.Timestamp)
 		}
 
 		cacheables = append(cacheables, *nc)
@@ -269,32 +269,36 @@ func getAccountId(httpRequest *http.Request) string {
 	return httpRequest.URL.Query().Get(AccountParameter)
 }
 
-// modifyVastXml modifies BidCacheRequest element Vast XML data
-func modifyVastXml(externalUrl string, data json.RawMessage, bidid string, bidder string, accountId string, timestamp int64) json.RawMessage {
-	c := string(data)
-	ci := strings.Index(c, ImpressionCloseTag)
+// ModifyVastXmlString rewrites and returns the string vastXML and a flag indicating if it was modified
+func ModifyVastXmlString(externalUrl, vast, bidid, bidder, accountID string, timestamp int64) (string, bool) {
+	ci := strings.Index(vast, ImpressionCloseTag)
 
 	// no impression tag - pass it as it is
 	if ci == -1 {
-		return data
+		return vast, false
 	}
 
-	vastUrlTracking := GetVastUrlTracking(externalUrl, bidid, bidder, accountId, timestamp)
+	vastUrlTracking := GetVastUrlTracking(externalUrl, bidid, bidder, accountID, timestamp)
 	impressionUrl := "<![CDATA[" + vastUrlTracking + "]]>"
-	oi := strings.Index(c, ImpressionOpenTag)
+	oi := strings.Index(vast, ImpressionOpenTag)
 
 	if ci-oi == len(ImpressionOpenTag) {
-		return json.RawMessage(strings.Replace(c, ImpressionOpenTag, ImpressionOpenTag+impressionUrl, 1))
+		return strings.Replace(vast, ImpressionOpenTag, ImpressionOpenTag+impressionUrl, 1), true
 	}
 
-	return json.RawMessage(strings.Replace(c, ImpressionCloseTag, ImpressionCloseTag+ImpressionOpenTag+impressionUrl+ImpressionCloseTag, 1))
+	return strings.Replace(vast, ImpressionCloseTag, ImpressionCloseTag+ImpressionOpenTag+impressionUrl+ImpressionCloseTag, 1), true
 }
 
-func contains(s []string, e string) bool {
-	if len(s) == 0 {
-		return false
+// ModifyVastXmlJSON modifies BidCacheRequest element Vast XML data
+func ModifyVastXmlJSON(externalUrl string, data json.RawMessage, bidid, bidder, accountId string, timestamp int64) json.RawMessage {
+	var vast string
+	if err := json.Unmarshal(data, &vast); err != nil {
+		// failed to decode json, fall back to string
+		vast = string(data)
 	}
-
-	i := sort.SearchStrings(s, e)
-	return i < len(s) && s[i] == e
+	vast, ok := ModifyVastXmlString(externalUrl, vast, bidid, bidder, accountId, timestamp)
+	if !ok {
+		return data
+	}
+	return json.RawMessage(vast)
 }
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 21fa2c8a..04e0c2cf 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -331,6 +331,10 @@ func (deps *endpointDeps) validateRequest(req *openrtb.BidRequest) []error {
 		if err := validateSChains(bidExt); err != nil {
 			return []error{err}
 		}
+
+		if err := deps.validateEidPermissions(bidExt, aliases); err != nil {
+			return []error{err}
+		}
 	}
 
 	if (req.Site == nil && req.App == nil) || (req.Site != nil && req.App != nil) {
@@ -419,6 +423,51 @@ func validateSChains(req *openrtb_ext.ExtRequest) error {
 	return err
 }
 
+func (deps *endpointDeps) validateEidPermissions(req *openrtb_ext.ExtRequest, aliases map[string]string) error {
+	if req == nil || req.Prebid.Data == nil {
+		return nil
+	}
+
+	uniqueSources := make(map[string]struct{}, len(req.Prebid.Data.EidPermissions))
+	for i, eid := range req.Prebid.Data.EidPermissions {
+		if len(eid.Source) == 0 {
+			return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] missing required field: "source"`, i)
+		}
+
+		if _, exists := uniqueSources[eid.Source]; exists {
+			return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] duplicate entry with field: "source"`, i)
+		}
+		uniqueSources[eid.Source] = struct{}{}
+
+		if len(eid.Bidders) == 0 {
+			return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] missing or empty required field: "bidders"`, i)
+		}
+
+		if err := validateBidders(eid.Bidders, deps.bidderMap, aliases); err != nil {
+			return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] contains %v`, i, err)
+		}
+	}
+
+	return nil
+}
+
+func validateBidders(bidders []string, knownBidders map[string]openrtb_ext.BidderName, knownAliases map[string]string) error {
+	for _, bidder := range bidders {
+		if bidder == "*" {
+			if len(bidders) > 1 {
+				return errors.New(`bidder wildcard "*" mixed with specific bidders`)
+			}
+		} else {
+			_, isCoreBidder := knownBidders[bidder]
+			_, isAlias := knownAliases[bidder]
+			if !isCoreBidder && !isAlias {
+				return fmt.Errorf(`unrecognized bidder "%v"`, bidder)
+			}
+		}
+	}
+	return nil
+}
+
 func (deps *endpointDeps) validateImp(imp *openrtb.Imp, aliases map[string]string, index int) []error {
 	if imp.ID == "" {
 		return []error{fmt.Errorf("request.imp[%d] missing required field: \"id\"", index)}
diff --git a/exchange/auction.go b/exchange/auction.go
index aa36feb2..97f82d3c 100644
--- a/exchange/auction.go
+++ b/exchange/auction.go
@@ -147,7 +147,7 @@ func (a *auction) setRoundedPrices(priceGranularity openrtb_ext.PriceGranularity
 	a.roundedPrices = roundedPrices
 }
 
-func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, targData *targetData, bidRequest *openrtb.BidRequest, ttlBuffer int64, defaultTTLs *config.DefaultTTLs, bidCategory map[string]string, debugLog *DebugLog) []error {
+func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, targData *targetData, evTracking *eventTracking, bidRequest *openrtb.BidRequest, ttlBuffer int64, defaultTTLs *config.DefaultTTLs, bidCategory map[string]string, debugLog *DebugLog) []error {
 	var bids, vast, includeBidderKeys, includeWinners bool = targData.includeCacheBids, targData.includeCacheVast, targData.includeBidderKeys, targData.includeWinners
 	if !((bids || vast) && (includeBidderKeys || includeWinners)) {
 		return nil
@@ -176,7 +176,7 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client,
 		expByImp[imp.ID] = imp.Exp
 	}
 	for _, topBidsPerImp := range a.winningBidsByBidder {
-		for _, topBidPerBidder := range topBidsPerImp {
+		for bidderName, topBidPerBidder := range topBidsPerImp {
 			impID := topBidPerBidder.bid.ImpID
 			isOverallWinner := a.winningBids[impID] == topBidPerBidder
 			if !includeBidderKeys && !isOverallWinner {
@@ -195,6 +195,10 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client,
 			}
 			if bids {
 				if jsonBytes, err := json.Marshal(topBidPerBidder.bid); err == nil {
+					jsonBytes, err = evTracking.modifyBidJSON(topBidPerBidder, bidderName, jsonBytes)
+					if err != nil {
+						errs = append(errs, err)
+					}
 					if useCustomCacheKey {
 						// not allowed if bids is true; log error and cache normally
 						errs = append(errs, errors.New("cannot use custom cache key for non-vast bids"))
@@ -210,8 +214,8 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client,
 				}
 			}
 			if vast && topBidPerBidder.bidType == openrtb_ext.BidTypeVideo {
-				vast := makeVAST(topBidPerBidder.bid)
-				if jsonBytes, err := json.Marshal(vast); err == nil {
+				vastXML := makeVAST(topBidPerBidder.bid)
+				if jsonBytes, err := json.Marshal(vastXML); err == nil {
 					if useCustomCacheKey {
 						toCache = append(toCache, prebid_cache_client.Cacheable{
 							Type:       prebid_cache_client.TypeXML,
diff --git a/exchange/bidder.go b/exchange/bidder.go
index c59d2b4f..e4867b0e 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -58,6 +58,7 @@ type adaptedBidder interface {
 // pbsOrtbBid.bidType will become "response.seatbid[i].bid.ext.prebid.type" in the final OpenRTB response.
 // pbsOrtbBid.bidTargets does not need to be filled out by the Bidder. It will be set later by the exchange.
 // pbsOrtbBid.bidVideo is optional but should be filled out by the Bidder if bidType is video.
+// pbsOrtbBid.bidEvents is set by exchange when event tracking is enabled
 // pbsOrtbBid.dealPriority is optionally provided by adapters and used internally by the exchange to support deal targeted campaigns.
 // pbsOrtbBid.dealTierSatisfied is set to true by exchange.updateHbPbCatDur if deal tier satisfied otherwise it will be set to false
 type pbsOrtbBid struct {
@@ -65,6 +66,7 @@ type pbsOrtbBid struct {
 	bidType           openrtb_ext.BidType
 	bidTargets        map[string]string
 	bidVideo          *openrtb_ext.ExtBidPrebidVideo
+	bidEvents         *openrtb_ext.ExtBidPrebidEvents
 	dealPriority      int
 	dealTierSatisfied bool
 }
diff --git a/exchange/events.go b/exchange/events.go
new file mode 100644
index 00000000..3a05e098
--- /dev/null
+++ b/exchange/events.go
@@ -0,0 +1,115 @@
+package exchange
+
+import (
+	"encoding/json"
+	"time"
+
+	jsonpatch "github.com/evanphx/json-patch"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/analytics"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/endpoints/events"
+	"github.com/prebid/prebid-server/metrics"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// eventTracking has configuration fields needed for adding event tracking to an auction response
+type eventTracking struct {
+	accountID          string
+	enabledForAccount  bool
+	enabledForRequest  bool
+	auctionTimestampMs int64
+	integration        metrics.DemandSource // web app amp
+	bidderInfos        adapters.BidderInfos
+	externalURL        string
+}
+
+// getEventTracking creates an eventTracking object from the different configuration sources
+func getEventTracking(requestExtPrebid *openrtb_ext.ExtRequestPrebid, ts time.Time, account *config.Account, bidderInfos adapters.BidderInfos, externalURL string) *eventTracking {
+	return &eventTracking{
+		accountID:          account.ID,
+		enabledForAccount:  account.EventsEnabled,
+		enabledForRequest:  requestExtPrebid != nil && requestExtPrebid.Events != nil,
+		auctionTimestampMs: ts.UnixNano() / 1e+6,
+		integration:        "", // TODO: add integration support, see #1428
+		bidderInfos:        bidderInfos,
+		externalURL:        externalURL,
+	}
+}
+
+// modifyBidsForEvents adds bidEvents and modifies VAST AdM if necessary.
+func (ev *eventTracking) modifyBidsForEvents(seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid) map[openrtb_ext.BidderName]*pbsOrtbSeatBid {
+	for bidderName, seatBid := range seatBids {
+		modifyingVastXMLAllowed := ev.isModifyingVASTXMLAllowed(bidderName.String())
+		for _, pbsBid := range seatBid.bids {
+			if modifyingVastXMLAllowed {
+				ev.modifyBidVAST(pbsBid, bidderName)
+			}
+			pbsBid.bidEvents = ev.makeBidExtEvents(pbsBid, bidderName)
+		}
+	}
+	return seatBids
+}
+
+// isModifyingVASTXMLAllowed returns true if this bidder config allows modifying VAST XML for event tracking
+func (ev *eventTracking) isModifyingVASTXMLAllowed(bidderName string) bool {
+	return ev.bidderInfos[bidderName].ModifyingVastXmlAllowed && (ev.enabledForAccount || ev.enabledForRequest)
+}
+
+// modifyBidVAST injects event Impression url if needed, otherwise returns original VAST string
+func (ev *eventTracking) modifyBidVAST(pbsBid *pbsOrtbBid, bidderName openrtb_ext.BidderName) {
+	bid := pbsBid.bid
+	if pbsBid.bidType != openrtb_ext.BidTypeVideo || len(bid.AdM) == 0 && len(bid.NURL) == 0 {
+		return
+	}
+	vastXML := makeVAST(bid)
+	if newVastXML, ok := events.ModifyVastXmlString(ev.externalURL, vastXML, bid.ID, bidderName.String(), ev.accountID, ev.auctionTimestampMs); ok {
+		bid.AdM = newVastXML
+	}
+}
+
+// modifyBidJSON injects "wurl" (win) event url if needed, otherwise returns original json
+func (ev *eventTracking) modifyBidJSON(pbsBid *pbsOrtbBid, bidderName openrtb_ext.BidderName, jsonBytes []byte) ([]byte, error) {
+	if !(ev.enabledForAccount || ev.enabledForRequest) || pbsBid.bidType == openrtb_ext.BidTypeVideo {
+		return jsonBytes, nil
+	}
+	var winEventURL string
+	if pbsBid.bidEvents != nil { // All bids should have already been updated with win/imp event URLs
+		winEventURL = pbsBid.bidEvents.Win
+	} else {
+		winEventURL = ev.makeEventURL(analytics.Win, pbsBid, bidderName)
+	}
+	// wurl attribute is not in the schema, so we have to patch
+	patch, err := json.Marshal(map[string]string{"wurl": winEventURL})
+	if err != nil {
+		return jsonBytes, err
+	}
+	modifiedJSON, err := jsonpatch.MergePatch(jsonBytes, patch)
+	if err != nil {
+		return jsonBytes, err
+	}
+	return modifiedJSON, nil
+}
+
+// makeBidExtEvents make the data for bid.ext.prebid.events if needed, otherwise returns nil
+func (ev *eventTracking) makeBidExtEvents(pbsBid *pbsOrtbBid, bidderName openrtb_ext.BidderName) *openrtb_ext.ExtBidPrebidEvents {
+	if !(ev.enabledForAccount || ev.enabledForRequest) || pbsBid.bidType == openrtb_ext.BidTypeVideo {
+		return nil
+	}
+	return &openrtb_ext.ExtBidPrebidEvents{
+		Win: ev.makeEventURL(analytics.Win, pbsBid, bidderName),
+		Imp: ev.makeEventURL(analytics.Imp, pbsBid, bidderName),
+	}
+}
+
+// makeEventURL returns an analytics event url for the requested type (win or imp)
+func (ev *eventTracking) makeEventURL(evType analytics.EventType, pbsBid *pbsOrtbBid, bidderName openrtb_ext.BidderName) string {
+	return events.EventRequestToUrl(ev.externalURL,
+		&analytics.EventRequest{
+			Type:      evType,
+			BidID:     pbsBid.bid.ID,
+			Bidder:    string(bidderName),
+			AccountID: ev.accountID,
+			Timestamp: ev.auctionTimestampMs,
+		})
+}
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 1bd7e61e..99ac8a88 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -52,11 +52,13 @@ type IdFetcher interface {
 
 type exchange struct {
 	adapterMap          map[openrtb_ext.BidderName]adaptedBidder
+	bidderInfo          adapters.BidderInfos
 	me                  metrics.MetricsEngine
 	cache               prebid_cache_client.Client
 	cacheTime           time.Duration
 	gDPR                gdpr.Permissions
 	currencyConverter   *currency.RateConverter
+	externalURL         string
 	UsersyncIfAmbiguous bool
 	privacyConfig       config.Privacy
 	categoriesFetcher   stored_requests.CategoryFetcher
@@ -77,13 +79,15 @@ type bidResponseWrapper struct {
 	bidder       openrtb_ext.BidderName
 }
 
-func NewExchange(adapters map[openrtb_ext.BidderName]adaptedBidder, cache prebid_cache_client.Client, cfg *config.Configuration, metricsEngine metrics.MetricsEngine, gDPR gdpr.Permissions, currencyConverter *currency.RateConverter, categoriesFetcher stored_requests.CategoryFetcher) Exchange {
+func NewExchange(adapters map[openrtb_ext.BidderName]adaptedBidder, cache prebid_cache_client.Client, cfg *config.Configuration, metricsEngine metrics.MetricsEngine, infos adapters.BidderInfos, gDPR gdpr.Permissions, currencyConverter *currency.RateConverter, categoriesFetcher stored_requests.CategoryFetcher) Exchange {
 	return &exchange{
 		adapterMap:          adapters,
+		bidderInfo:          infos,
 		cache:               cache,
 		cacheTime:           time.Duration(cfg.CacheURL.ExpectedTimeMillis) * time.Millisecond,
 		categoriesFetcher:   categoriesFetcher,
 		currencyConverter:   currencyConverter,
+		externalURL:         cfg.ExternalURL,
 		gDPR:                gDPR,
 		me:                  metricsEngine,
 		UsersyncIfAmbiguous: cfg.GDPR.UsersyncIfAmbiguous,
@@ -178,6 +182,9 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 			}
 		}
 
+		evTracking := getEventTracking(&requestExt.Prebid, r.StartTime, &r.Account, e.bidderInfo, e.externalURL)
+		adapterBids = evTracking.modifyBidsForEvents(adapterBids)
+
 		if targData != nil {
 			// A non-nil auction is only needed if targeting is active. (It is used below this block to extract cache keys)
 			auc = newAuction(adapterBids, len(r.BidRequest.Imp), targData.preferDeals)
@@ -187,8 +194,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 				dealErrs := applyDealSupport(r.BidRequest, auc, bidCategory)
 				errs = append(errs, dealErrs...)
 			}
-
-			cacheErrs := auc.doCache(ctx, e.cache, targData, r.BidRequest, 60, &r.Account.CacheTTL, bidCategory, debugLog)
+			cacheErrs := auc.doCache(ctx, e.cache, targData, evTracking, r.BidRequest, 60, &r.Account.CacheTTL, bidCategory, debugLog)
 			if len(cacheErrs) > 0 {
 				errs = append(errs, cacheErrs...)
 			}
@@ -813,6 +819,7 @@ func (e *exchange) makeBid(Bids []*pbsOrtbBid, auc *auction, returnCreative bool
 				Targeting:         thisBid.bidTargets,
 				Type:              thisBid.bidType,
 				Video:             thisBid.bidVideo,
+				Events:            thisBid.bidEvents,
 				DealPriority:      thisBid.dealPriority,
 				DealTierSatisfied: thisBid.dealTierSatisfied,
 			},
diff --git a/exchange/gdpr.go b/exchange/gdpr.go
index f464cac2..e861f948 100644
--- a/exchange/gdpr.go
+++ b/exchange/gdpr.go
@@ -8,24 +8,22 @@ import (
 )
 
 // ExtractGDPR will pull the gdpr flag from an openrtb request
-func extractGDPR(bidRequest *openrtb.BidRequest) gdpr.Signal {
+func extractGDPR(bidRequest *openrtb.BidRequest) (gdpr.Signal, error) {
 	var re regsExt
 	var err error
-	if bidRequest.Regs != nil {
+	if bidRequest.Regs != nil && bidRequest.Regs.Ext != nil {
 		err = json.Unmarshal(bidRequest.Regs.Ext, &re)
 	}
 	if re.GDPR == nil || err != nil {
-		return gdpr.SignalAmbiguous
-	} else {
-		return gdpr.Signal(*re.GDPR)
+		return gdpr.SignalAmbiguous, err
 	}
+	return gdpr.Signal(*re.GDPR), nil
 }
 
 // ExtractConsent will pull the consent string from an openrtb request
-func extractConsent(bidRequest *openrtb.BidRequest) (consent string) {
+func extractConsent(bidRequest *openrtb.BidRequest) (consent string, err error) {
 	var ue userExt
-	var err error
-	if bidRequest.User != nil {
+	if bidRequest.User != nil && bidRequest.User.Ext != nil {
 		err = json.Unmarshal(bidRequest.User.Ext, &ue)
 	}
 	if err != nil {
diff --git a/exchange/utils.go b/exchange/utils.go
index d574c2a6..8d4c0fac 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -75,14 +75,19 @@ func cleanOpenRTBRequests(ctx context.Context,
 		return
 	}
 
-	gdprSignal := extractGDPR(req.BidRequest)
-	consent := extractConsent(req.BidRequest)
+	gdprSignal, err := extractGDPR(req.BidRequest)
+	if err != nil {
+		errs = append(errs, err)
+	}
+	consent, err := extractConsent(req.BidRequest)
+	if err != nil {
+		errs = append(errs, err)
+	}
 	gdprEnforced := gdprSignal == gdpr.SignalYes || (gdprSignal == gdpr.SignalAmbiguous && !usersyncIfAmbiguous)
 
 	ccpaEnforcer, err := extractCCPA(req.BidRequest, privacyConfig, &req.Account, aliases, integrationTypeMap[req.LegacyLabels.RType])
 	if err != nil {
 		errs = append(errs, err)
-		return
 	}
 
 	lmtEnforcer := extractLMT(req.BidRequest, privacyConfig)
@@ -197,15 +202,22 @@ func getAuctionBidderRequests(req AuctionRequest,
 		return nil, []error{err}
 	}
 
+	var errs []error
 	for bidder, imps := range impsByBidder {
 		coreBidder := resolveBidder(bidder, aliases)
 
 		reqCopy := *req.BidRequest
 		reqCopy.Imp = imps
 		reqCopy.Ext = reqExt
+
 		prepareSource(&reqCopy, bidder, sChainsByBidder)
 
-		bidder := BidderRequest{
+		if err := removeUnpermissionedEids(&reqCopy, bidder, requestExt); err != nil {
+			errs = append(errs, fmt.Errorf("unable to enforce request.ext.prebid.data.eidpermissions because %v", err))
+			continue
+		}
+
+		bidderRequest := BidderRequest{
 			BidderName:     openrtb_ext.BidderName(bidder),
 			BidderCoreName: coreBidder,
 			BidRequest:     &reqCopy,
@@ -218,15 +230,16 @@ func getAuctionBidderRequests(req AuctionRequest,
 				AdapterBids: metrics.AdapterBidPresent,
 			},
 		}
-		if hadSync := prepareUser(&reqCopy, bidder.BidderName.String(), coreBidder, explicitBuyerUIDs, req.UserSyncs); !hadSync && req.BidRequest.App == nil {
-			bidder.BidderLabels.CookieFlag = metrics.CookieFlagNo
+
+		if hadSync := prepareUser(&reqCopy, bidder, coreBidder, explicitBuyerUIDs, req.UserSyncs); !hadSync && req.BidRequest.App == nil {
+			bidderRequest.BidderLabels.CookieFlag = metrics.CookieFlagNo
 		} else {
-			bidder.BidderLabels.CookieFlag = metrics.CookieFlagYes
+			bidderRequest.BidderLabels.CookieFlag = metrics.CookieFlagYes
 		}
 
-		bidderRequests = append(bidderRequests, bidder)
+		bidderRequests = append(bidderRequests, bidderRequest)
 	}
-	return bidderRequests, nil
+	return bidderRequests, errs
 }
 
 func getExtJson(req *openrtb.BidRequest, unpackedExt *openrtb_ext.ExtRequest) (json.RawMessage, error) {
@@ -292,7 +305,9 @@ func extractBuyerUIDs(user *openrtb.User) (map[string]string, error) {
 	// as long as user.ext.prebid exists.
 	buyerUIDs := userExt.Prebid.BuyerUIDs
 	userExt.Prebid = nil
-	if userExt.Consent != "" || userExt.DigiTrust != nil {
+
+	// Remarshal (instead of removing) if the ext has other known fields
+	if userExt.Consent != "" || userExt.DigiTrust != nil || len(userExt.Eids) > 0 {
 		if newUserExtBytes, err := json.Marshal(userExt); err != nil {
 			return nil, err
 		} else {
@@ -447,6 +462,100 @@ func copyWithBuyerUID(user *openrtb.User, buyerUID string) *openrtb.User {
 	return user
 }
 
+// removeUnpermissionedEids modifies the request to remove any request.user.ext.eids not permissions for the specific bidder
+func removeUnpermissionedEids(request *openrtb.BidRequest, bidder string, requestExt *openrtb_ext.ExtRequest) error {
+	// ensure request might have eids (as much as we can check before unmarshalling)
+	if request.User == nil || len(request.User.Ext) == 0 {
+		return nil
+	}
+
+	// ensure request has eid permissions to enforce
+	if requestExt == nil || requestExt.Prebid.Data == nil || len(requestExt.Prebid.Data.EidPermissions) == 0 {
+		return nil
+	}
+
+	// low level unmarshal to preserve other request.user.ext values. prebid server is non-destructive.
+	var userExt map[string]json.RawMessage
+	if err := json.Unmarshal(request.User.Ext, &userExt); err != nil {
+		return err
+	}
+
+	eidsJSON, eidsSpecified := userExt["eids"]
+	if !eidsSpecified {
+		return nil
+	}
+
+	var eids []openrtb_ext.ExtUserEid
+	if err := json.Unmarshal(eidsJSON, &eids); err != nil {
+		return err
+	}
+
+	// exit early if there are no eids (empty array)
+	if len(eids) == 0 {
+		return nil
+	}
+
+	// translate eid permissions to a map for quick lookup
+	eidRules := make(map[string][]string)
+	for _, p := range requestExt.Prebid.Data.EidPermissions {
+		eidRules[p.Source] = p.Bidders
+	}
+
+	eidsAllowed := make([]openrtb_ext.ExtUserEid, 0, len(eids))
+	for _, eid := range eids {
+		allowed := false
+		if rule, hasRule := eidRules[eid.Source]; hasRule {
+			for _, ruleBidder := range rule {
+				if ruleBidder == "*" || ruleBidder == bidder {
+					allowed = true
+					break
+				}
+			}
+		} else {
+			allowed = true
+		}
+
+		if allowed {
+			eidsAllowed = append(eidsAllowed, eid)
+		}
+	}
+
+	// exit early if all eids are allowed and nothing needs to be removed
+	if len(eids) == len(eidsAllowed) {
+		return nil
+	}
+
+	// marshal eidsAllowed back to userExt
+	if len(eidsAllowed) == 0 {
+		delete(userExt, "eids")
+	} else {
+		eidsRaw, err := json.Marshal(eidsAllowed)
+		if err != nil {
+			return err
+		}
+		userExt["eids"] = eidsRaw
+	}
+
+	// exit early if userExt is empty
+	if len(userExt) == 0 {
+		setUserExtWithCopy(request, nil)
+		return nil
+	}
+
+	userExtJSON, err := json.Marshal(userExt)
+	if err != nil {
+		return err
+	}
+	setUserExtWithCopy(request, userExtJSON)
+	return nil
+}
+
+func setUserExtWithCopy(request *openrtb.BidRequest, userExtJSON json.RawMessage) {
+	userCopy := *request.User
+	userCopy.Ext = userExtJSON
+	request.User = &userCopy
+}
+
 // resolveBidder returns the known BidderName associated with bidder, if bidder is an alias. If it's not an alias, the bidder is returned.
 func resolveBidder(bidder string, aliases map[string]string) openrtb_ext.BidderName {
 	if coreBidder, ok := aliases[bidder]; ok {
diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go
index 66a3f4ad..de6da7b4 100644
--- a/gdpr/vendorlist-fetching.go
+++ b/gdpr/vendorlist-fetching.go
@@ -95,14 +95,14 @@ func preloadCache(ctx context.Context, client *http.Client, urlMaker func(uint16
 func vendorListURLMaker(vendorListVersion uint16, tcfSpecVersion uint8) string {
 	if tcfSpecVersion == tcf2SpecVersion {
 		if vendorListVersion == 0 {
-			return "https://vendorlist.consensu.org/v2/vendor-list.json"
+			return "https://vendor-list.consensu.org/v2/vendor-list.json"
 		}
-		return "https://vendorlist.consensu.org/v2/archives/vendor-list-v" + strconv.Itoa(int(vendorListVersion)) + ".json"
+		return "https://vendor-list.consensu.org/v2/archives/vendor-list-v" + strconv.Itoa(int(vendorListVersion)) + ".json"
 	}
 	if vendorListVersion == 0 {
-		return "https://vendorlist.consensu.org/vendorlist.json"
+		return "https://vendor-list.consensu.org/vendorlist.json"
 	}
-	return "https://vendorlist.consensu.org/v-" + strconv.Itoa(int(vendorListVersion)) + "/vendorlist.json"
+	return "https://vendor-list.consensu.org/v-" + strconv.Itoa(int(vendorListVersion)) + "/vendorlist.json"
 }
 
 // newOccasionalSaver returns a wrapped version of saveOne() which only activates every few minutes.
diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go
index e500ab00..8f4d7a09 100644
--- a/openrtb_ext/bid.go
+++ b/openrtb_ext/bid.go
@@ -15,13 +15,14 @@ type ExtBid struct {
 // DealPriority represents priority of deal bid. If its non deal bid then value will be 0
 // DealTierSatisfied true represents corresponding bid has satisfied the deal tier
 type ExtBidPrebid struct {
-	Cache             *ExtBidPrebidCache `json:"cache,omitempty"`
-	DealPriority      int                `json:"dealpriority,omitempty"`
-	DealTierSatisfied bool               `json:"dealtiersatisfied,omitempty"`
-	Meta              *ExtBidPrebidMeta  `json:"meta,omitempty"`
-	Targeting         map[string]string  `json:"targeting,omitempty"`
-	Type              BidType            `json:"type"`
-	Video             *ExtBidPrebidVideo `json:"video,omitempty"`
+	Cache             *ExtBidPrebidCache  `json:"cache,omitempty"`
+	DealPriority      int                 `json:"dealpriority,omitempty"`
+	DealTierSatisfied bool                `json:"dealtiersatisfied,omitempty"`
+	Meta              *ExtBidPrebidMeta   `json:"meta,omitempty"`
+	Targeting         map[string]string   `json:"targeting,omitempty"`
+	Type              BidType             `json:"type"`
+	Video             *ExtBidPrebidVideo  `json:"video,omitempty"`
+	Events            *ExtBidPrebidEvents `json:"events,omitempty"`
 }
 
 // ExtBidPrebidCache defines the contract for  bidresponse.seatbid.bid[i].ext.prebid.cache
@@ -58,6 +59,12 @@ type ExtBidPrebidVideo struct {
 	PrimaryCategory string `json:"primary_category"`
 }
 
+// ExtBidPrebidEvents defines the contract for bidresponse.seatbid.bid[i].ext.prebid.events
+type ExtBidPrebidEvents struct {
+	Win string `json:"win,omitempty"`
+	Imp string `json:"imp,omitempty"`
+}
+
 // BidType describes the allowed values for bidresponse.seatbid.bid[i].ext.prebid.type
 type BidType string
 
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index 1199edc7..124e050a 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -20,11 +20,13 @@ type ExtRequestPrebid struct {
 	Aliases              map[string]string         `json:"aliases,omitempty"`
 	BidAdjustmentFactors map[string]float64        `json:"bidadjustmentfactors,omitempty"`
 	Cache                *ExtRequestPrebidCache    `json:"cache,omitempty"`
+	Data                 *ExtRequestPrebidData     `json:"data,omitempty"`
+	Debug                bool                      `json:"debug,omitempty"`
+	Events               json.RawMessage           `json:"events,omitempty"`
 	SChains              []*ExtRequestPrebidSChain `json:"schains,omitempty"`
 	StoredRequest        *ExtStoredRequest         `json:"storedrequest,omitempty"`
-	Targeting            *ExtRequestTargeting      `json:"targeting,omitempty"`
 	SupportDeals         bool                      `json:"supportdeals,omitempty"`
-	Debug                bool                      `json:"debug,omitempty"`
+	Targeting            *ExtRequestTargeting      `json:"targeting,omitempty"`
 
 	// NoSale specifies bidders with whom the publisher has a legal relationship where the
 	// passing of personally identifiable information doesn't constitute a sale per CCPA law.
@@ -289,3 +291,14 @@ var priceGranularityAuto = PriceGranularity{
 		},
 	},
 }
+
+// ExtRequestPrebidData defines Prebid's First Party Data (FPD) and related bid request options.
+type ExtRequestPrebidData struct {
+	EidPermissions []ExtRequestPrebidDataEidPermission `json:"eidpermissions"`
+}
+
+// ExtRequestPrebidDataEidPermission defines a filter rule for filter user.ext.eids
+type ExtRequestPrebidDataEidPermission struct {
+	Source  string   `json:"source"`
+	Bidders []string `json:"bidders"`
+}
diff --git a/router/router.go b/router/router.go
index c398f528..48580f85 100644
--- a/router/router.go
+++ b/router/router.go
@@ -251,7 +251,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 		glog.Fatalf("%v", errs)
 	}
 
-	theExchange := exchange.NewExchange(adapters, cacheClient, cfg, r.MetricsEngine, gdprPerms, rateConvertor, categoriesFetcher)
+	theExchange := exchange.NewExchange(adapters, cacheClient, cfg, r.MetricsEngine, bidderInfos, gdprPerms, rateConvertor, categoriesFetcher)
 
 	openrtbEndpoint, err := openrtb2.NewEndpoint(theExchange, paramsValidator, fetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders)
 	if err != nil {
diff --git a/static/bidder-info/dmx.yaml b/static/bidder-info/dmx.yaml
index d6e54178..3ecb452f 100644
--- a/static/bidder-info/dmx.yaml
+++ b/static/bidder-info/dmx.yaml
@@ -1,5 +1,5 @@
 maintainer:
-  email: "steve@districtm.net"
+  email: "prebid@districtm.net"
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/openx.yaml b/static/bidder-info/openx.yaml
index d16a7d73..96ef8616 100644
--- a/static/bidder-info/openx.yaml
+++ b/static/bidder-info/openx.yaml
@@ -9,3 +9,4 @@ capabilities:
     mediaTypes:
       - banner
       - video
+modifyingVastXmlAllowed: true
diff --git a/static/bidder-params/beachfront.json b/static/bidder-params/beachfront.json
index 61490d12..a7751b27 100644
--- a/static/bidder-params/beachfront.json
+++ b/static/bidder-params/beachfront.json
@@ -28,7 +28,7 @@
     },
     "videoResponseType": {
       "type": "string",
-      "description": "By default the video response will be a nurl URL, but if you want AdM/VAST, set this to 'adm'. If you want both set it to 'both'. Setting it to any other string will have no effect and the default format will be returned."
+      "description": "By default the video response will be an AdM element containing VAST 3.0 markup including tracking, click through, and mediafile elements pointing to mp4, webm or other playable media files or Vpaid media as configured for the exchange at beachfront.io. Optionally, set this to 'nurl' to receive a URI pointing to VAST 3.0 markup which will contain a mediafile pointing to a beachfront neptune javascript video player which will load your video and take care of tracking, etc. Regardless of which format is selected, the id of the returned impression will be the provided impression id (imp[{'id'...},...] in the request) with the format appended. The impression id will be returned unchanged as 'impid'. So if you indicate 'nurl', and an impression id 'someImp', the returned impression will have an 'impid' value of 'someImp', and the 'id' value 'someImpNurlVideo'. This is to differentiate the object in the case that a request includes both video and banner elements. Setting videoResponseType to any other string will have no effect and the default format (AdM) will be returned."
     }
   },
 
diff --git a/static/bidder-params/dmx.json b/static/bidder-params/dmx.json
index 4c0df65e..3c76e507 100644
--- a/static/bidder-params/dmx.json
+++ b/static/bidder-params/dmx.json
@@ -8,13 +8,25 @@
       "type": "string",
       "description": "Represent boost MemberId from districtm UI"
     },
+    "placement_id" : {
+      "type": "string",
+      "description": "memberid replacement / alternative value or equivalent"
+    },
+    "seller_id" : {
+      "type": "string",
+      "description": "Represent DMX Partner when you get onboarded, this is for specific setup BURL vs NURL"
+    },
+    "dmxid": {
+      "type": "string",
+      "description": "Represent the placement ID dmxid equivalent to 'tagid', this value is optional"
+    },
     "tagid": {
       "type": "string",
-      "description": "Represent the placement ID, this value is optional"
+      "description": "Represent the placement ID tagid equivalent to 'dmxid', this value is optional"
     },
     "bidfloor": {
-      "type": "string",
-      "description": "The minimum price acceptable for a bid"
+      "type": "number",
+      "description": "The minimum price acceptable for a bid, this is optional since we do get the one from the original openrtb request"
     }
   },
 
diff --git a/usersync/usersync.go b/usersync/usersync.go
index 2b313a02..236acbe7 100644
--- a/usersync/usersync.go
+++ b/usersync/usersync.go
@@ -17,7 +17,7 @@ type Usersyncer interface {
 
 	// GDPRVendorID returns the ID in the IAB Global Vendor List which refers to this Bidder.
 	//
-	// The Global Vendor list can be found here: https://vendorlist.consensu.org/vendorlist.json
+	// The Global Vendor list can be found here: https://vendor-list.consensu.org/vendorlist.json
 	// Bidders can register for the list here: https://register.consensu.org/
 	//
 	// If you're not on the list, this should return 0. If cookie sync requests have GDPR consent info,


Test patch:
diff --git a/adapters/beachfront/beachfront_test.go b/adapters/beachfront/beachfront_test.go
index dc51e196..1c1c4ff4 100644
--- a/adapters/beachfront/beachfront_test.go
+++ b/adapters/beachfront/beachfront_test.go
@@ -11,8 +11,8 @@ import (
 
 func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderBeachfront, config.Adapter{
-		Endpoint:         `https://display.bfmio.com/prebid_display`,
-		ExtraAdapterInfo: `{"video_endpoint":"https://reachms.bfmio.com/bid.json?exchange_id"}`,
+		Endpoint:         `https://qa.beachrtb.com/prebid_display`,
+		ExtraAdapterInfo: `{"video_endpoint":"https://qa.beachrtb.com/bid.json?exchange_id"}`,
 	})
 
 	if buildErr != nil {
@@ -24,7 +24,7 @@ func TestJsonSamples(t *testing.T) {
 
 func TestExtraInfoDefaultWhenEmpty(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderBeachfront, config.Adapter{
-		Endpoint:         `https://display.bfmio.com/prebid_display`,
+		Endpoint:         `https://qa.beachrtb.com/prebid_display`,
 		ExtraAdapterInfo: ``,
 	})
 
@@ -39,7 +39,7 @@ func TestExtraInfoDefaultWhenEmpty(t *testing.T) {
 
 func TestExtraInfoDefaultWhenNotSpecified(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderBeachfront, config.Adapter{
-		Endpoint:         `https://display.bfmio.com/prebid_display`,
+		Endpoint:         `https://qa.beachrtb.com/prebid_display`,
 		ExtraAdapterInfo: `{"video_endpoint":""}`,
 	})
 
@@ -54,7 +54,7 @@ func TestExtraInfoDefaultWhenNotSpecified(t *testing.T) {
 
 func TestExtraInfoMalformed(t *testing.T) {
 	_, buildErr := Builder(openrtb_ext.BidderBeachfront, config.Adapter{
-		Endpoint:         `https://display.bfmio.com/prebid_display`,
+		Endpoint:         `https://qa.beachrtb.com/prebid_display`,
 		ExtraAdapterInfo: `malformed`,
 	})
 
diff --git a/adapters/beachfront/beachfronttest/exemplary/adm-video-by-default.json b/adapters/beachfront/beachfronttest/exemplary/adm-video-by-default.json
new file mode 100644
index 00000000..d3fa41a2
--- /dev/null
+++ b/adapters/beachfront/beachfronttest/exemplary/adm-video-by-default.json
@@ -0,0 +1,123 @@
+{
+  "mockBidRequest": {
+    "id": "adm-video",
+    "imp": [
+      {
+        "id": "video1",
+        "ext": {
+          "bidder": {
+            "bidfloor": 3.01,
+            "appId": "videoAppId1"
+          }
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "context": "instream",
+          "w": 300,
+          "h": 250
+        }
+      }
+    ],
+    "site": {
+      "page": "https://some.domain.us/some/page.html"
+    },
+    "device":{
+      "ip":"255.255.255.255"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1",
+        "body": {
+          "id": "adm-video",
+          "imp": [
+            {
+              "video": {
+                "w": 300,
+                "h": 250,
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "bidfloor": 3.01,
+              "id": "video1",
+              "secure": 1
+            }
+          ],
+          "site": {
+            "page": "https://some.domain.us/some/page.html",
+            "domain": "some.domain.us"
+          },
+          "cur": [
+            "USD"
+          ],
+          "device":{
+            "devicetype": 2,
+            "ip":"255.255.255.255"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "adm-video",
+          "seatBid": [
+            {
+              "bid": [
+                {
+                  "id": "5fd7c8a6ff2f1f0d42ee6427",
+                  "impid": "video1",
+                  "price": 20,
+                  "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+                  "adid": "1088",
+                  "adomain": [
+                    "beachfront.io"
+                  ],
+                  "cid": "277",
+                  "crid": "532",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "duration": 30
+                  }
+                }
+              ],
+              "seat": "bfio-s-1"
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "video1AdmVideo",
+            "impid": "video1",
+            "price": 20,
+            "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+            "adid": "1088",
+            "adomain": [
+              "beachfront.io"
+            ],
+            "cid": "277",
+            "crid": "532",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "duration": 30
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/beachfront/beachfronttest/exemplary/adm-video-by-explicit-type.json b/adapters/beachfront/beachfronttest/exemplary/adm-video-by-explicit-type.json
new file mode 100644
index 00000000..8c05d65a
--- /dev/null
+++ b/adapters/beachfront/beachfronttest/exemplary/adm-video-by-explicit-type.json
@@ -0,0 +1,124 @@
+{
+  "mockBidRequest": {
+    "id": "adm-video",
+    "imp": [
+      {
+        "id": "video1",
+        "ext": {
+          "bidder": {
+            "videoResponseType": "adm",
+            "bidfloor": 3.01,
+            "appId": "videoAppId1"
+          }
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "context": "instream",
+          "w": 300,
+          "h": 250
+        }
+      }
+    ],
+    "site": {
+      "page": "https://some.domain.us/some/page.html"
+    },
+    "device":{
+      "ip":"255.255.255.255"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1",
+        "body": {
+          "id": "adm-video",
+          "imp": [
+            {
+              "video": {
+                "w": 300,
+                "h": 250,
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "bidfloor": 3.01,
+              "id": "video1",
+              "secure": 1
+            }
+          ],
+          "site": {
+            "page": "https://some.domain.us/some/page.html",
+            "domain": "some.domain.us"
+          },
+          "cur": [
+            "USD"
+          ],
+          "device":{
+            "devicetype": 2,
+            "ip":"255.255.255.255"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "adm-video",
+          "seatBid": [
+            {
+              "bid": [
+                {
+                  "id": "5fd7c8a6ff2f1f0d42ee6427",
+                  "impid": "video1",
+                  "price": 20,
+                  "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+                  "adid": "1088",
+                  "adomain": [
+                    "beachfront.io"
+                  ],
+                  "cid": "277",
+                  "crid": "532",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "duration": 30
+                  }
+                }
+              ],
+              "seat": "bfio-s-1"
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "video1AdmVideo",
+            "impid": "video1",
+            "price": 20,
+            "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+            "adid": "1088",
+            "adomain": [
+              "beachfront.io"
+            ],
+            "cid": "277",
+            "crid": "532",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "duration": 30
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/beachfront/beachfronttest/exemplary/banner-and-adm-video-by-default.json b/adapters/beachfront/beachfronttest/exemplary/banner-and-adm-video-by-default.json
new file mode 100644
index 00000000..bff1b76a
--- /dev/null
+++ b/adapters/beachfront/beachfronttest/exemplary/banner-and-adm-video-by-default.json
@@ -0,0 +1,187 @@
+{
+  "mockBidRequest": {
+    "id": "banner-and-video",
+    "imp": [
+      {
+        "id": "mix1",
+        "ext": {
+          "bidder": {
+            "bidfloor": 0.41,
+            "appIds": {
+              "banner": "bannerAppId1",
+              "video": "videoAppId1"
+            }
+          }
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "context": "instream",
+          "w": 300,
+          "h": 250
+        }
+      }
+    ],
+    "site": {
+      "page": "https://some.domain.us/some/page.html"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/prebid_display",
+        "body": {
+          "slots": [
+            {
+              "slot": "mix1",
+              "id": "bannerAppId1",
+              "bidfloor": 0.41,
+              "sizes": [
+                {
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "domain": "some.domain.us",
+          "page": "https://some.domain.us/some/page.html",
+          "referrer": "",
+          "search": "",
+          "secure": 1,
+          "deviceOs": "",
+          "deviceModel": "",
+          "isMobile": 0,
+          "ua": "",
+          "ip": "",
+          "dnt": 0,
+          "user": {},
+          "adapterName": "BF_PREBID_S2S",
+          "adapterVersion": "0.9.1",
+          "requestId": "banner-and-video"
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "crid": "crid_1",
+            "price": 9.5019655,
+            "w": 300,
+            "h": 250,
+            "slot": "mix1",
+            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>"
+          }
+        ]
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1",
+        "body": {
+          "id": "banner-and-video",
+          "imp": [
+            {
+              "video": {
+                "w": 300,
+                "h": 250,
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "bidfloor": 0.41,
+              "secure": 1,
+              "id": "mix1"
+            }
+          ],
+          "site": {
+            "domain": "some.domain.us",
+            "page": "https://some.domain.us/some/page.html"
+          },
+          "cur": [
+            "USD"
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "5d839458f73decdc1572b7f6",
+                  "impid": "mix1",
+                  "price": 20,
+                  "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+                  "adid": "1088",
+                  "adomain": [
+                    "beachfront.io"
+                  ],
+                  "cid": "277",
+                  "crid": "532",
+                  "w": 300,
+                  "h": 250
+                }
+              ],
+              "seat": "bfb-io-s1"
+            }
+          ],
+          "bidid": "5d839458f73decdc1572b7f6",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "mix1Banner",
+            "impid": "mix1",
+            "price": 9.5019655,
+            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>",
+            "crid": "crid_1",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    },
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "mix1AdmVideo",
+            "impid": "mix1",
+            "price": 20,
+            "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+            "adid": "1088",
+            "adomain": [
+              "beachfront.io"
+            ],
+            "cid": "277",
+            "crid": "532",
+            "w": 300,
+            "h": 250
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/beachfront/beachfronttest/exemplary/banner-and-adm-video-by-explicit-type.json b/adapters/beachfront/beachfronttest/exemplary/banner-and-adm-video-by-explicit-type.json
new file mode 100644
index 00000000..3482f4ef
--- /dev/null
+++ b/adapters/beachfront/beachfronttest/exemplary/banner-and-adm-video-by-explicit-type.json
@@ -0,0 +1,188 @@
+{
+  "mockBidRequest": {
+    "id": "banner-and-video",
+    "imp": [
+      {
+        "id": "mix1",
+        "ext": {
+          "bidder": {
+            "videoResponseType": "adm",
+            "bidfloor": 0.41,
+            "appIds": {
+              "banner": "bannerAppId1",
+              "video": "videoAppId1"
+            }
+          }
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "context": "instream",
+          "w": 300,
+          "h": 250
+        }
+      }
+    ],
+    "site": {
+      "page": "https://some.domain.us/some/page.html"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/prebid_display",
+        "body": {
+          "slots": [
+            {
+              "slot": "mix1",
+              "id": "bannerAppId1",
+              "bidfloor": 0.41,
+              "sizes": [
+                {
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "domain": "some.domain.us",
+          "page": "https://some.domain.us/some/page.html",
+          "referrer": "",
+          "search": "",
+          "secure": 1,
+          "deviceOs": "",
+          "deviceModel": "",
+          "isMobile": 0,
+          "ua": "",
+          "ip": "",
+          "dnt": 0,
+          "user": {},
+          "adapterName": "BF_PREBID_S2S",
+          "adapterVersion": "0.9.1",
+          "requestId": "banner-and-video"
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "crid": "crid_1",
+            "price": 9.5019655,
+            "w": 300,
+            "h": 250,
+            "slot": "mix1",
+            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>"
+          }
+        ]
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1",
+        "body": {
+          "id": "banner-and-video",
+          "imp": [
+            {
+              "video": {
+                "w": 300,
+                "h": 250,
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "bidfloor": 0.41,
+              "secure": 1,
+              "id": "mix1"
+            }
+          ],
+          "site": {
+            "domain": "some.domain.us",
+            "page": "https://some.domain.us/some/page.html"
+          },
+          "cur": [
+            "USD"
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "5d839458f73decdc1572b7f6",
+                  "impid": "mix1",
+                  "price": 20,
+                  "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+                  "adid": "1088",
+                  "adomain": [
+                    "beachfront.io"
+                  ],
+                  "cid": "277",
+                  "crid": "532",
+                  "w": 300,
+                  "h": 250
+                }
+              ],
+              "seat": "bfb-io-s1"
+            }
+          ],
+          "bidid": "5d839458f73decdc1572b7f6",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "mix1Banner",
+            "impid": "mix1",
+            "price": 9.5019655,
+            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>",
+            "crid": "crid_1",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    },
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "mix1AdmVideo",
+            "impid": "mix1",
+            "price": 20,
+            "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+            "adid": "1088",
+            "adomain": [
+              "beachfront.io"
+            ],
+            "cid": "277",
+            "crid": "532",
+            "w": 300,
+            "h": 250
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/beachfront/beachfronttest/exemplary/banner-and-nurl-video.json b/adapters/beachfront/beachfronttest/exemplary/banner-and-nurl-video.json
new file mode 100644
index 00000000..9db714f4
--- /dev/null
+++ b/adapters/beachfront/beachfronttest/exemplary/banner-and-nurl-video.json
@@ -0,0 +1,189 @@
+{
+  "mockBidRequest": {
+    "id": "banner-and-video",
+    "imp": [
+      {
+        "id": "mix1",
+        "ext": {
+          "bidder": {
+            "videoResponseType": "nurl",
+            "bidfloor": 0.41,
+            "appIds": {
+              "banner": "bannerAppId1",
+              "video": "videoAppId1"
+            }
+          }
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "context": "instream",
+          "w": 300,
+          "h": 250
+        }
+      }
+    ],
+    "site": {
+      "page": "https://some.domain.us/some/page.html"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/prebid_display",
+        "body": {
+          "slots": [
+            {
+              "slot": "mix1",
+              "id": "bannerAppId1",
+              "bidfloor": 0.41,
+              "sizes": [
+                {
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "domain": "some.domain.us",
+          "page": "https://some.domain.us/some/page.html",
+          "referrer": "",
+          "search": "",
+          "secure": 1,
+          "deviceOs": "",
+          "deviceModel": "",
+          "isMobile": 0,
+          "ua": "",
+          "ip": "",
+          "dnt": 0,
+          "user": {},
+          "adapterName": "BF_PREBID_S2S",
+          "adapterVersion": "0.9.1",
+          "requestId": "banner-and-video"
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "crid": "crid_1",
+            "price": 9.5019655,
+            "w": 300,
+            "h": 250,
+            "slot": "mix1",
+            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>"
+          }
+        ]
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1&prebidserver",
+        "body": {
+          "id": "banner-and-video",
+          "imp": [
+            {
+              "video": {
+                "w": 300,
+                "h": 250,
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "bidfloor": 0.41,
+              "secure": 1,
+              "id": "mix1"
+            }
+          ],
+          "site": {
+            "domain": "some.domain.us",
+            "page": "https://some.domain.us/some/page.html"
+          },
+          "isPrebid": true,
+          "cur": [
+            "USD"
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "5d839458f73decdc1572b7f6",
+                  "impid": "mix1",
+                  "price": 20,
+                  "nurl": "https://useast.bfmio.com/getBids?aid=bid:70b99087-1b92-4e81-bc42-05c940fd6014:11bc5dd5-7421-4dd8-c926-40fa653bec76:20.0:20.0&v=1&dsp=5d8391a85f35945a70c9ddf0,0.01&i_type=pre",
+                  "crid": "70b99087-1b92-4e81-bc42-05c940fd6014",
+                  "adid": "1088",
+                  "adomain": [
+                    "beachfront.io"
+                  ],
+                  "cid": "277",
+                  "w": 300,
+                  "h": 250
+                }
+              ],
+              "seat": "bfb-io-s1"
+            }
+          ],
+          "bidid": "5d839458f73decdc1572b7f6",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "mix1Banner",
+            "impid": "mix1",
+            "price": 9.5019655,
+            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>",
+            "crid": "crid_1",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    },
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "mix1NurlVideo",
+            "impid": "mix1",
+            "price": 20,
+            "nurl": "https://useast.bfmio.com/getBids?aid=bid:70b99087-1b92-4e81-bc42-05c940fd6014:11bc5dd5-7421-4dd8-c926-40fa653bec76:20.0:20.0&v=1&dsp=5d8391a85f35945a70c9ddf0,0.01&i_type=pre",
+            "adid": "1088",
+            "adomain": [
+              "beachfront.io"
+            ],
+            "cid": "277",
+            "crid": "70b99087-1b92-4e81-bc42-05c940fd6014",
+            "w": 300,
+            "h": 250
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/beachfront/beachfronttest/supplemental/minimal-site-banner.json b/adapters/beachfront/beachfronttest/exemplary/banner.json
similarity index 83%
rename from adapters/beachfront/beachfronttest/supplemental/minimal-site-banner.json
rename to adapters/beachfront/beachfronttest/exemplary/banner.json
index a12f7fd9..1eb17286 100644
--- a/adapters/beachfront/beachfronttest/supplemental/minimal-site-banner.json
+++ b/adapters/beachfront/beachfronttest/exemplary/banner.json
@@ -2,7 +2,7 @@
   "mockBidRequest": {
     "id": "some_test_ad",
     "site": {
-      "page": "test.opposingviews.com"
+      "page": "https://some.domain.us/some/page.html"
     },
     "imp": [
       {
@@ -18,21 +18,22 @@
         "ext": {
           "bidder": {
             "bidfloor": 0.02,
-            "appId": "3b16770b-17af-4d22-daff-9606bdf2c9c3"
+            "appId": "bannerAppId1"
           }
         }
       }
     ]
   },
+
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://display.bfmio.com/prebid_display",
+        "uri": "https://qa.beachrtb.com/prebid_display",
         "body": {
           "slots": [
             {
               "slot": "",
-              "id": "3b16770b-17af-4d22-daff-9606bdf2c9c3",
+              "id": "bannerAppId1",
               "bidfloor": 0.02,
               "sizes": [
                 {
@@ -42,12 +43,12 @@
               ]
             }
           ],
-          "domain": "test.opposingviews.com",
-          "page": "test.opposingviews.com",
-          "requestId": "some_test_ad",
+          "domain": "some.domain.us",
+          "page": "https://some.domain.us/some/page.html",
           "referrer": "",
           "search": "",
-          "secure": 0,
+          "secure": 1,
+          "requestId": "some_test_ad",
           "isMobile": 0,
           "ip": "",
           "deviceModel": "",
@@ -55,7 +56,7 @@
           "dnt": 0,
           "ua": "",
           "adapterName": "BF_PREBID_S2S",
-          "adapterVersion": "0.9.0",
+          "adapterVersion": "0.9.1",
           "user": {}
         }
       },
diff --git a/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json b/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json
deleted file mode 100644
index 6672e2af..00000000
--- a/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json
+++ /dev/null
@@ -1,95 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "some_test_ad",
-    "site": {
-      "page": "https://test.opposingviews.com/i/society/republican-sen-collins-may-change-vote-tax-bill?cb=1234534"
-    },
-    "imp": [
-      {
-        "bidfloor": 0.02,
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            }
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "bidfloor": 0.02,
-            "appId": "3b16770b-17af-4d22-daff-9606bdf2c9c3"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "https://display.bfmio.com/prebid_display",
-        "body": {
-          "slots": [
-            {
-              "slot": "",
-              "id": "3b16770b-17af-4d22-daff-9606bdf2c9c3",
-              "bidfloor": 0.02,
-              "sizes": [
-                {
-                  "w": 300,
-                  "h": 250
-                }
-              ]
-            }
-          ],
-          "domain": "test.opposingviews.com",
-          "page": "https://test.opposingviews.com/i/society/republican-sen-collins-may-change-vote-tax-bill?cb=1234534",
-          "referrer": "",
-          "search": "",
-          "secure": 1,
-          "requestId": "some_test_ad",
-          "isMobile": 0,
-          "ip": "",
-          "deviceModel": "",
-          "deviceOs": "",
-          "dnt": 0,
-          "ua": "",
-          "adapterName": "BF_PREBID_S2S",
-          "adapterVersion": "0.9.0",
-          "user": {}
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": [
-          {
-            "crid": "crid_1",
-            "price": 2.942808,
-            "w": 300,
-            "h": 250,
-            "slot": "div-gpt-ad-1460505748561-0",
-            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>"
-          }
-        ]
-      }
-    }
-  ],
-  "expectedBidResponses": [
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "div-gpt-ad-1460505748561-0Banner",
-            "impid": "div-gpt-ad-1460505748561-0",
-            "price": 2.942808,
-            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>",
-            "crid": "crid_1",
-            "w": 300,
-            "h": 250
-          },
-          "type": "banner"
-        }
-      ]
-    }
-  ]
-}
diff --git a/adapters/beachfront/beachfronttest/exemplary/simple-nurl-video.json b/adapters/beachfront/beachfronttest/exemplary/nurl-video.json
similarity index 71%
rename from adapters/beachfront/beachfronttest/exemplary/simple-nurl-video.json
rename to adapters/beachfront/beachfronttest/exemplary/nurl-video.json
index 0f96d9f2..b9b1881e 100644
--- a/adapters/beachfront/beachfronttest/exemplary/simple-nurl-video.json
+++ b/adapters/beachfront/beachfronttest/exemplary/nurl-video.json
@@ -1,17 +1,14 @@
 {
   "mockBidRequest": {
-    "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-    "tmax": 1000,
-    "source": {
-      "tid": "61b87329-8790-47b7-90dd-c53ae7ce1723"
-    },
+    "id": "nurl-video",
     "imp": [
       {
         "id": "video1",
         "ext": {
           "bidder": {
+            "videoResponseType": "nurl",
             "bidfloor": 0.01,
-            "appId": "11bc5dd5-7421-4dd8-c926-40fa653bec76"
+            "appId": "videoAppId1"
           }
         },
         "video": {
@@ -25,27 +22,19 @@
       }
     ],
     "site": {
-      "publisher": {
-        "id": "publisher1"
-      },
       "page": "http://example.com/whatever/something.html"
     },
-    "user": {
-      "id": "some-user",
-      "buyeruid": "some-buyer"
-    },
     "device": {
-      "devicetype": 2,
-      "ip": "127.0.0.1",
-      "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16"
+      "ip": "255.255.255.255"
     }
   },
+
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://reachms.bfmio.com/bid.json?exchange_id=11bc5dd5-7421-4dd8-c926-40fa653bec76&prebidserver",
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1&prebidserver",
         "body": {
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
+          "id": "nurl-video",
           "isPrebid": true,
           "imp": [
             {
@@ -61,28 +50,16 @@
             }
           ],
           "site": {
-            "publisher": {
-              "id": "publisher1"
-            },
             "page": "http://example.com/whatever/something.html",
             "domain": "example.com"
           },
           "device": {
             "devicetype": 2,
-            "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16",
-            "ip": "192.168.255.255"
+            "ip": "255.255.255.255"
           },
-          "source": {
-            "tid": "61b87329-8790-47b7-90dd-c53ae7ce1723"
-          },
-          "tmax": 1000,
           "cur": [
             "USD"
-          ],
-          "user": {
-            "id": "some-user",
-            "buyeruid": "some-buyer"
-          }
+          ]
         }
       },
       "mockResponse": {
@@ -115,6 +92,7 @@
       }
     }
   ],
+
   "expectedBidResponses": [
     {
       "bids": [
@@ -139,4 +117,4 @@
       ]
     }
   ]
-}
+}
\ No newline at end of file
diff --git a/adapters/beachfront/beachfronttest/exemplary/simple-adm-video.json b/adapters/beachfront/beachfronttest/exemplary/simple-adm-video.json
deleted file mode 100644
index cb1f4c20..00000000
--- a/adapters/beachfront/beachfronttest/exemplary/simple-adm-video.json
+++ /dev/null
@@ -1,150 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-    "tmax": 1000,
-    "source": {
-      "tid": "61b87329-8790-47b7-90dd-c53ae7ce1723"
-    },
-    "imp": [
-      {
-        "id": "video1",
-        "ext": {
-          "bidder": {
-            "videoResponseType": "adm",
-            "bidfloor": 0.01,
-            "appId": "11bc5dd5-7421-4dd8-c926-40fa653bec76"
-          }
-        },
-        "video": {
-          "mimes": [
-            "video/mp4"
-          ],
-          "context": "instream",
-          "w": 300,
-          "h": 250
-        }
-      }
-    ],
-    "site": {
-      "publisher": {
-        "id": "publisher1"
-      },
-      "page": "http://example.com/whatever/something.html"
-    },
-    "user": {
-      "id": "some-user",
-      "buyeruid": "some-buyer"
-    },
-    "device": {
-      "devicetype": 2,
-      "ip": "127.0.0.1",
-      "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16"
-    }
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "https://reachms.bfmio.com/bid.json?exchange_id=11bc5dd5-7421-4dd8-c926-40fa653bec76",
-        "body": {
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "imp": [
-            {
-              "video": {
-                "w": 300,
-                "h": 250,
-                "mimes": [
-                  "video/mp4"
-                ]
-              },
-              "id": "video1",
-              "secure": 0
-            }
-          ],
-          "site": {
-            "publisher": {
-              "id": "publisher1"
-            },
-            "page": "http://example.com/whatever/something.html",
-            "domain": "example.com"
-          },
-          "device": {
-            "devicetype": 2,
-            "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16",
-            "ip": "192.168.255.255"
-          },
-          "source": {
-            "tid": "61b87329-8790-47b7-90dd-c53ae7ce1723"
-          },
-          "tmax": 1000,
-          "cur": [
-            "USD"
-          ],
-          "user": {
-            "id": "some-user",
-            "buyeruid": "some-buyer"
-          }
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "seatBid": [
-            {
-              "bid": [
-                {
-                  "id": "video1AdmVideo",
-                  "impid": "video1",
-                  "price": 20,
-                  "adm": "<?xml version=\"1.0\" encoding=\"utf-8\"?><VAST version=\"2.0\"><Ad id=\"VPAID\"><InLine><AdSystem version=\"2.0\">IO</AdSystem><AdTitle>Neptune</AdTitle><Impression></Impression><Creatives><Creative sequence=\"1\"><Linear><Duration>00:00:15</Duration><MediaFiles><MediaFile delivery=\"progressive\" width=\"640\" height=\"480\" scalable=\"1\" type=\"application/javascript\" apiFramework=\"VPAID\"><![CDATA[http://player-cdn.beachfrontmedia.com/players/neptune/3/17/5/neptune-player.js]]></MediaFile></MediaFiles><AdParameters><![CDATA[{\"dsp\":\"5d8393417cbc8e5aa4fe2202,0.01\",\"inventory_id\":\"bid:95d219a2-6025-44ba-bff8-1dca987beaae:11bc5dd5-7421-4dd8-c926-40fa653bec76:${AUCTION_PRICE}:20.0\",\"request_endpoint\":\"//useast.bfmio.com/getmu\",\"inventory_type\":\"pre\"}]]></AdParameters></Linear></Creative></Creatives></InLine></Ad></VAST>",
-                  "adid": "1088",
-                  "adomain": [
-                    "beachfront.io"
-                  ],
-                  "cid": "277",
-                  "crid": "532",
-                  "w": 300,
-                  "h": 250,
-                  "ext": {
-                    "prebid": {
-                      "type": "video"
-                    }
-                  }
-                }
-              ],
-              "seat": "beachfront"
-            }
-          ]
-        }
-      }
-    }
-  ],
-  "expectedBidResponses": [
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "video1AdmVideo",
-            "impid": "video1",
-            "price": 20,
-            "adm": "<?xml version=\"1.0\" encoding=\"utf-8\"?><VAST version=\"2.0\"><Ad id=\"VPAID\"><InLine><AdSystem version=\"2.0\">IO</AdSystem><AdTitle>Neptune</AdTitle><Impression></Impression><Creatives><Creative sequence=\"1\"><Linear><Duration>00:00:15</Duration><MediaFiles><MediaFile delivery=\"progressive\" width=\"640\" height=\"480\" scalable=\"1\" type=\"application/javascript\" apiFramework=\"VPAID\"><![CDATA[http://player-cdn.beachfrontmedia.com/players/neptune/3/17/5/neptune-player.js]]></MediaFile></MediaFiles><AdParameters><![CDATA[{\"dsp\":\"5d8393417cbc8e5aa4fe2202,0.01\",\"inventory_id\":\"bid:95d219a2-6025-44ba-bff8-1dca987beaae:11bc5dd5-7421-4dd8-c926-40fa653bec76:${AUCTION_PRICE}:20.0\",\"request_endpoint\":\"//useast.bfmio.com/getmu\",\"inventory_type\":\"pre\"}]]></AdParameters></Linear></Creative></Creatives></InLine></Ad></VAST>",
-            "adid": "1088",
-            "adomain": [
-              "beachfront.io"
-            ],
-            "cid": "277",
-            "crid": "532",
-            "w": 300,
-            "h": 250,
-            "ext": {
-              "prebid": {
-                "type": "video"
-              }
-            }
-          },
-          "type": "video"
-        }
-      ]
-    }
-  ]
-}
diff --git a/adapters/beachfront/beachfronttest/exemplary/simple-mix.json b/adapters/beachfront/beachfronttest/exemplary/simple-mix.json
deleted file mode 100644
index cc2b4158..00000000
--- a/adapters/beachfront/beachfronttest/exemplary/simple-mix.json
+++ /dev/null
@@ -1,214 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-    "tmax": 1000,
-    "source": {
-      "tid": "61b87329-8790-47b7-90dd-c53ae7ce1723"
-    },
-    "imp": [
-      {
-        "id": "mix1",
-        "ext": {
-          "bidder": {
-            "videoResponseType": "adm",
-            "bidfloor": 0.41,
-            "appIds": {
-              "banner": "487cf244-5e95-43cb-e7b9-bb540873ca7e",
-              "video": "11bc5dd5-7421-4dd8-c926-40fa653bec76"
-            }
-          }
-        },
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            }
-          ]
-        },
-        "video": {
-          "mimes": [
-            "video/mp4"
-          ],
-          "context": "instream",
-          "w": 300,
-          "h": 250
-        }
-      }
-    ],
-    "site": {
-      "page": "http://example.com/somepage.html"
-    },
-    "user": {
-      "id": "some-user",
-      "buyeruid": "some-buyer"
-    },
-    "device": {
-      "ip": "192.168.255.255",
-      "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16"
-    }
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "https://display.bfmio.com/prebid_display",
-        "body": {
-          "slots": [
-            {
-              "slot": "mix1",
-              "id": "487cf244-5e95-43cb-e7b9-bb540873ca7e",
-              "bidfloor": 0.41,
-              "sizes": [
-                {
-                  "w": 300,
-                  "h": 250
-                }
-              ]
-            }
-          ],
-          "domain": "example.com",
-          "page": "http://example.com/somepage.html",
-          "referrer": "",
-          "search": "",
-          "secure": 0,
-          "deviceOs": "",
-          "deviceModel": "",
-          "isMobile": 0,
-          "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16",
-          "dnt": 0,
-          "user": {
-            "id": "some-user",
-            "buyeruid": "some-buyer"
-          },
-          "adapterName": "BF_PREBID_S2S",
-          "adapterVersion": "0.9.0",
-          "ip": "192.168.255.255",
-          "requestId": "61b87329-8790-47b7-90dd-c53ae7ce1723"
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": [
-          {
-            "crid": "crid_1",
-            "price": 9.5019655,
-            "w": 300,
-            "h": 250,
-            "slot": "mix1",
-            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>"
-          }
-        ]
-      }
-    },
-    {
-      "expectedRequest": {
-        "uri": "https://reachms.bfmio.com/bid.json?exchange_id=11bc5dd5-7421-4dd8-c926-40fa653bec76",
-        "body": {
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "imp": [
-            {
-              "video": {
-                "w": 300,
-                "h": 250,
-                "mimes": [
-                  "video/mp4"
-                ]
-              },
-              "bidfloor": 0.41,
-              "secure": 0,
-              "id": "mix1"
-            }
-          ],
-          "site": {
-            "page": "http://example.com/somepage.html",
-            "domain": "example.com"
-          },
-          "source": {
-            "tid": "61b87329-8790-47b7-90dd-c53ae7ce1723"
-          },
-          "device": {
-            "devicetype": 2,
-            "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16",
-            "ip": "192.168.255.255"
-          },
-          "cur": [
-            "USD"
-          ],
-          "tmax": 1000,
-          "user": {
-            "id": "some-user",
-            "buyeruid": "some-buyer"
-          }
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "seatbid": [
-            {
-              "bid": [
-                {
-                  "id": "5d839458f73decdc1572b7f6",
-                  "impid": "mix1",
-                  "price": 20,
-                  "adm": "<?xml version=\"1.0\" encoding=\"utf-8\"?><VAST version=\"2.0\"><Ad id=\"VPAID\"><InLine><AdSystem version=\"2.0\">IO</AdSystem><AdTitle>Neptune</AdTitle><Impression></Impression><Creatives><Creative sequence=\"1\"><Linear><Duration>00:00:15</Duration><MediaFiles><MediaFile delivery=\"progressive\" width=\"640\" height=\"480\" scalable=\"1\" type=\"application/javascript\" apiFramework=\"VPAID\"><![CDATA[http://player-cdn.beachfrontmedia.com/players/neptune/3/17/5/neptune-player.js]]></MediaFile></MediaFiles><AdParameters><![CDATA[{\"dsp\":\"5d839458f73decdc1572b7f6,0.41\",\"inventory_id\":\"bid:26826cf7-f8bb-4e47-a636-769d83dd9b4f:11bc5dd5-7421-4dd8-c926-40fa653bec76:${AUCTION_PRICE}:20.0\",\"request_endpoint\":\"//useast.bfmio.com/getmu\",\"inventory_type\":\"pre\"}]]></AdParameters></Linear></Creative></Creatives></InLine></Ad></VAST>",
-                  "adid": "1088",
-                  "adomain": [
-                    "beachfront.io"
-                  ],
-                  "cid": "277",
-                  "crid": "532",
-                  "w": 300,
-                  "h": 250
-                }
-              ],
-              "seat": "bfb-io-s1"
-            }
-          ],
-          "bidid": "5d839458f73decdc1572b7f6",
-          "cur": "USD"
-        }
-      }
-    }
-  ],
-  "expectedBidResponses": [
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "mix1Banner",
-            "impid": "mix1",
-            "price": 9.5019655,
-            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>",
-            "crid": "crid_1",
-            "w": 300,
-            "h": 250
-          },
-          "type": "banner"
-        }
-      ]
-    },
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "mix1AdmVideo",
-            "impid": "mix1",
-            "price": 20,
-            "adm": "<?xml version=\"1.0\" encoding=\"utf-8\"?><VAST version=\"2.0\"><Ad id=\"VPAID\"><InLine><AdSystem version=\"2.0\">IO</AdSystem><AdTitle>Neptune</AdTitle><Impression></Impression><Creatives><Creative sequence=\"1\"><Linear><Duration>00:00:15</Duration><MediaFiles><MediaFile delivery=\"progressive\" width=\"640\" height=\"480\" scalable=\"1\" type=\"application/javascript\" apiFramework=\"VPAID\"><![CDATA[http://player-cdn.beachfrontmedia.com/players/neptune/3/17/5/neptune-player.js]]></MediaFile></MediaFiles><AdParameters><![CDATA[{\"dsp\":\"5d839458f73decdc1572b7f6,0.41\",\"inventory_id\":\"bid:26826cf7-f8bb-4e47-a636-769d83dd9b4f:11bc5dd5-7421-4dd8-c926-40fa653bec76:${AUCTION_PRICE}:20.0\",\"request_endpoint\":\"//useast.bfmio.com/getmu\",\"inventory_type\":\"pre\"}]]></AdParameters></Linear></Creative></Creatives></InLine></Ad></VAST>",
-            "adid": "1088",
-            "adomain": [
-              "beachfront.io"
-            ],
-            "cid": "277",
-            "crid": "532",
-            "w": 300,
-            "h": 250
-          },
-          "type": "video"
-        }
-      ]
-    }
-  ]
-}
diff --git a/adapters/beachfront/beachfronttest/supplemental/minimal-banner-empty_array-200.json b/adapters/beachfront/beachfronttest/supplemental/banner-empty_array-200.json
similarity index 75%
rename from adapters/beachfront/beachfronttest/supplemental/minimal-banner-empty_array-200.json
rename to adapters/beachfront/beachfronttest/supplemental/banner-empty_array-200.json
index 01207f22..c90c1215 100644
--- a/adapters/beachfront/beachfronttest/supplemental/minimal-banner-empty_array-200.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-empty_array-200.json
@@ -2,10 +2,11 @@
   "mockBidRequest": {
     "id": "some_test_ad",
     "site": {
-      "page": "https://yourmomshouse.com"
+      "page": "https://some.domain.us/some/page.html"
     },
     "imp": [
       {
+        "id": "dudImp",
         "bidfloor": 0.02,
         "banner": {
           "format": [
@@ -18,7 +19,7 @@
         "ext": {
           "bidder": {
             "bidfloor": 0.02,
-            "appId": "00000000-1111-2222-3333-11111111111"
+            "appId": "dudAppId1"
           }
         }
       }
@@ -28,12 +29,12 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://display.bfmio.com/prebid_display",
+        "uri": "https://qa.beachrtb.com/prebid_display",
         "body": {
           "slots": [
             {
-              "slot": "",
-              "id": "00000000-1111-2222-3333-11111111111",
+              "slot": "dudImp",
+              "id": "dudAppId1",
               "bidfloor": 0.02,
               "sizes": [
                 {
@@ -43,8 +44,8 @@
               ]
             }
           ],
-          "domain": "yourmomshouse.com",
-          "page": "https://yourmomshouse.com",
+          "domain": "some.domain.us",
+          "page": "https://some.domain.us/some/page.html",
           "referrer": "",
           "search": "",
           "secure": 1,
@@ -56,7 +57,7 @@
           "dnt": 0,
           "ua": "",
           "adapterName": "BF_PREBID_S2S",
-          "adapterVersion": "0.9.0",
+          "adapterVersion": "0.9.1",
           "user": {
           }
         }
diff --git a/adapters/beachfront/beachfronttest/supplemental/minimal-mobile-video.json b/adapters/beachfront/beachfronttest/supplemental/minimal-mobile-video.json
deleted file mode 100644
index 5556f6e7..00000000
--- a/adapters/beachfront/beachfronttest/supplemental/minimal-mobile-video.json
+++ /dev/null
@@ -1,119 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "some_test_ad",
-    "app": {
-      "bundle": "_org.prebid.mobile.demoapp",
-      "id": "some-mobile-app"
-    },
-    "imp": [
-      {
-        "id": "video1",
-        "ext": {
-          "bidder": {
-            "bidfloor": 0.01,
-            "appId": "11bc5dd5-7421-4dd8-c926-40fa653bec76",
-            "videoResponseType": "nurl"
-          }
-        },
-        "video": {
-          "mimes": [
-            "video/mp4"
-          ],
-          "w": 300,
-          "h": 250
-        }
-      }
-    ],
-    "device": {
-      "devicetype": 1,
-      "ifa": "bb956625-df13-4568-b60b-eb5cbf76e986"
-    }
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "https://reachms.bfmio.com/bid.json?exchange_id=11bc5dd5-7421-4dd8-c926-40fa653bec76&prebidserver",
-        "body": {
-          "cur": [
-            "USD"
-          ],
-          "device": {
-            "devicetype": 1,
-            "ifa": "bb956625-df13-4568-b60b-eb5cbf76e986"
-          },
-          "id": "some_test_ad",
-          "imp": [
-            {
-              "id": "video1",
-              "secure": 0,
-              "video": {
-                "mimes": [
-                  "video/mp4"
-                ],
-                "h": 250,
-                "w": 300
-              }
-            }
-          ],
-          "isPrebid": true,
-          "app": {
-            "bundle": "_org.prebid.mobile.demoapp",
-            "domain": "prebid.org",
-            "id": "some-mobile-app"
-          }
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "seatBid": [
-            {
-              "bid": [
-                {
-                  "w": 0,
-                  "h": 0,
-                  "id": "0",
-                  "impid": "",
-                  "price": 0.01,
-                  "adid": "",
-                  "nurl": "https://evt.bfmio.com/getmu?aid=bid:0d32aea6-d199-4803-a188-bd7cf7be1e12:11bc5dd5-7421-4dd8-c926-40fa653bec76:0.01:0.01&v=1&dsp=5afda03c7c2d2bfaee55a3f0,0.01&i_type=pre",
-                  "adm": "",
-                  "adomain": [],
-                  "iurl": "",
-                  "cid": "",
-                  "crid": "",
-                  "attr": [],
-                  "ext": {},
-                  "cat": []
-                }
-              ],
-              "seat": "",
-              "group": 0,
-              "ext": {}
-            }
-          ]
-        }
-      }
-    }
-  ],
-  "expectedBidResponses": [
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "video1NurlVideo",
-            "impid": "video1",
-            "price": 0.01,
-            "nurl": "https://evt.bfmio.com/getmu?aid=bid:0d32aea6-d199-4803-a188-bd7cf7be1e12:11bc5dd5-7421-4dd8-c926-40fa653bec76:0.01:0.01&v=1&dsp=5afda03c7c2d2bfaee55a3f0,0.01&i_type=pre",
-            "crid": "0d32aea6-d199-4803-a188-bd7cf7be1e12",
-            "w": 300,
-            "h": 250,
-            "ext":{}
-          },
-          "type": "video"
-        }
-      ]
-    }
-  ]
-}
diff --git a/adapters/beachfront/beachfronttest/supplemental/mobile-banner.json b/adapters/beachfront/beachfronttest/supplemental/mobile-banner.json
deleted file mode 100644
index 17cc7d32..00000000
--- a/adapters/beachfront/beachfronttest/supplemental/mobile-banner.json
+++ /dev/null
@@ -1,128 +0,0 @@
-{
-  "mockBidRequest": {
-    "id":"763e3312-19d5-4b07-a61d-890147e863a1",
-    "source":{
-      "tid":"763e3312-19d5-4b07-a61d-890147e863a1"
-    },
-    "tmax":1000,
-    "imp":[
-      {
-        "id":"div-gpt-ad-1460505748561-0",
-        "ext":{
-          "bidder":{
-            "appId":"3b16770b-17af-4d22-daff-9606bdf2c9c3",
-            "bidfloor":0.01
-          }
-        },
-        "banner":{
-          "format":[
-            {
-              "w":300,
-              "h":250
-            },
-            {
-              "w":300,
-              "h":600
-            }
-          ]
-        }
-      }
-    ],
-    "device":{
-      "make":"Huawei",
-      "model":"Android SDK built for x86",
-      "ua":"Mozilla\/5.0 (Linux; Android 5.1.1; Android SDK built for x86 Build\/LMY48X) AppleWebKit\/537.36 (KHTML, like Gecko) Version\/4.0 Chrome\/39.0.0.0 Mobile Safari\/537.36",
-      "w":360,
-      "h":567,
-      "pxratio":2,
-      "mccmnc":"310-260",
-      "carrier":"Android",
-      "connectiontype":2,
-      "lmt":0,
-      "ifa":"bb956625-df13-4568-b60b-eb5cbf76e986",
-      "os":"android",
-      "osv":"22",
-      "language":"en"
-    },
-    "app":{
-      "id":"bfa84af2-bd16-4d35-96ad-31c6bb888df0",
-      "name":"Demo app",
-      "bundle":"_org.prebid.mobile.demoapp",
-      "domain":"beachfront.com"
-    }
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri" : "https://display.bfmio.com/prebid_display",
-        "body" : {
-          "slots":[
-            {
-              "slot":"div-gpt-ad-1460505748561-0",
-              "id":"3b16770b-17af-4d22-daff-9606bdf2c9c3",
-              "bidfloor":0,
-              "sizes":[
-                {
-                  "w":300,
-                  "h":250
-                },
-                {
-                  "w":300,
-                  "h":600
-                }
-              ]
-            }
-          ],
-          "domain":"beachfront.com",
-          "page":"_org.prebid.mobile.demoapp",
-          "referrer":"",
-          "search":"",
-          "secure":0,
-          "deviceOs":"android",
-          "deviceModel":"Android SDK built for x86",
-          "isMobile":1,
-          "ua":"Mozilla/5.0 (Linux; Android 5.1.1; Android SDK built for x86 Build/LMY48X) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36",
-          "dnt":0,
-          "user":{
-
-          },
-          "adapterName":"BF_PREBID_S2S",
-          "adapterVersion":"0.9.0",
-          "ip":"192.168.255.255",
-          "requestId":"763e3312-19d5-4b07-a61d-890147e863a1"
-        }
-      }
-    ,
-      "mockResponse": {
-        "status": 200,
-        "body": [
-          {
-            "crid":"crid_1",
-            "price":2.942808,
-            "w":300,
-            "h":250,
-            "slot":"div-gpt-ad-1460505748561-0",
-            "adm":"<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>"
-          }
-        ]
-      }
-    }
-  ],
-
-  "expectedBidResponses": [
-    {
-      "bids": [{
-        "bid": {
-          "id": "div-gpt-ad-1460505748561-0Banner",
-          "impid": "div-gpt-ad-1460505748561-0",
-          "price": 2.942808,
-          "adm":"<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>",
-          "crid": "crid_1",
-          "w": 300,
-          "h": 250
-        },
-        "type": "banner"
-      }]
-    }
-  ]
-}
diff --git a/adapters/beachfront/beachfronttest/supplemental/multi-banner.json b/adapters/beachfront/beachfronttest/supplemental/multi-banner.json
deleted file mode 100644
index 1d355dba..00000000
--- a/adapters/beachfront/beachfronttest/supplemental/multi-banner.json
+++ /dev/null
@@ -1,138 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "fail_id",
-    "tmax": 800,
-    "site": {
-      "page": "https://test.opposingviews.com/i/society/republican-sen-collins-may-change-vote-tax-bill?cb=1234534"
-    },
-    "user": {
-      "id": "some-user",
-      "buyeruid": "some-buyer"
-    },
-    "device": {
-      "ip": "127.0.0.1",
-      "model": "MacBookPro",
-      "OS": "macOS Sierra 10.12.6",
-      "dnt": 1,
-      "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16"
-    },
-    "imp": [
-      {
-        "id": "test_slot",
-        "bidfloor": 0.02,
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            }
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "bidfloor": 0.02,
-            "appId": "3b16770b-17af-4d22-daff-9606bdf2c9c3"
-          }
-        }
-      },{
-        "id": "test_slot2",
-        "bidfloor": 0.03,
-        "banner": {
-          "format": [
-            {
-              "w": 600,
-              "h": 450
-            }
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "bidfloor": 0.03,
-            "appId": "3b16770b-17af-4d22-daff-9606bdf2c9c3"
-          }
-        }
-      }
-    ]
-  },
-
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "https://display.bfmio.com/prebid_display",
-        "body": {
-          "slots": [
-            {
-              "slot": "test_slot",
-              "id": "3b16770b-17af-4d22-daff-9606bdf2c9c3",
-              "bidfloor": 0.02,
-              "sizes": [
-                {
-                  "w": 300,
-                  "h": 250
-                }
-              ]
-            }, {
-              "slot": "test_slot2",
-              "bidfloor": 0.03,
-              "id": "3b16770b-17af-4d22-daff-9606bdf2c9c3",
-              "sizes": [
-                {
-                  "h": 450,
-                  "w": 600
-                }
-              ]
-            }
-          ],
-          "domain": "test.opposingviews.com",
-          "page": "https://test.opposingviews.com/i/society/republican-sen-collins-may-change-vote-tax-bill?cb=1234534",
-          "referrer": "",
-          "requestId": "fail_id",
-          "search": "",
-          "secure": 1,
-          "isMobile": 0,
-          "ip": "192.168.255.255",
-          "deviceModel": "MacBookPro",
-          "deviceOs": "macOS Sierra 10.12.6",
-          "dnt": 1,
-          "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16",
-          "adapterName": "BF_PREBID_S2S",
-          "adapterVersion": "0.9.0",
-          "user": {
-            "buyeruid": "some-buyer",
-            "id": "some-user"
-          }
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": [
-          {
-            "crid":"crid_1",
-            "price":2.942808,
-            "w":300,
-            "h":250,
-            "slot":"div-gpt-ad-1460505748561-0",
-            "adm":"<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>"
-          }
-        ]
-      }
-    }
-  ],
-
-  "expectedBidResponses": [
-    {
-      "bids": [{
-        "bid": {
-          "id": "div-gpt-ad-1460505748561-0Banner",
-          "impid": "div-gpt-ad-1460505748561-0",
-          "price": 2.942808,
-          "adm":"<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>",
-          "crid": "crid_1",
-          "w": 300,
-          "h": 250
-        },
-        "type": "banner"
-      }]
-    }
-  ]
-}
diff --git a/adapters/beachfront/beachfronttest/supplemental/multi-video.json b/adapters/beachfront/beachfronttest/supplemental/multi-video.json
deleted file mode 100644
index 8ccd0a8d..00000000
--- a/adapters/beachfront/beachfronttest/supplemental/multi-video.json
+++ /dev/null
@@ -1,235 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-    "tmax": 1000,
-    "source": {
-      "tid": "61b87329-8790-47b7-90dd-c53ae7ce1723"
-    },
-    "imp": [
-      {
-        "id": "multivideo",
-        "ext": {
-          "bidder": {
-            "bidfloor": 3.01,
-            "videoResponseType": "both",
-            "appId": "11bc5dd5-7421-4dd8-c926-40fa653bec76"
-          }
-        },
-        "video": {
-          "mimes": [
-            "video/mp4",
-            "video/webm"
-          ],
-          "context": "instream",
-          "w": 600,
-          "h": 450
-        }
-      }
-    ],
-    "site": {
-      "publisher": {
-        "id": "publisher1"
-      },
-      "page": "http://example.com/whatever"
-    },
-    "user": {
-      "id": "some-user",
-      "buyeruid": "some-buyer"
-    },
-    "device": {
-      "ip": "127.0.0.1",
-      "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16"
-    }
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "https://reachms.bfmio.com/bid.json?exchange_id=11bc5dd5-7421-4dd8-c926-40fa653bec76",
-        "body": {
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "imp": [
-            {
-              "id": "multivideo",
-              "secure": 0,
-              "video": {
-                "mimes": [
-                  "video/mp4",
-                  "video/webm"
-                ],
-                "w": 600,
-                "h": 450
-              },
-              "bidfloor": 3.01
-            }
-          ],
-          "site": {
-            "domain": "example.com",
-            "page": "http://example.com/whatever",
-            "publisher": {
-              "id": "publisher1"
-            }
-          },
-          "device": {
-            "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16",
-            "ip": "192.168.255.255",
-            "devicetype": 2
-          },
-          "user": {
-            "id": "some-user",
-            "buyeruid": "some-buyer"
-          },
-          "tmax": 1000,
-          "cur": [
-            "USD"
-          ],
-          "source": {
-            "tid": "61b87329-8790-47b7-90dd-c53ae7ce1723"
-          }
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "seatbid": [
-            {
-              "bid": [
-                {
-                  "id": "5d8231a4254b2eca804965a3",
-                  "impid": "multivideo",
-                  "price": 20.0,
-                  "adm": "<?xml version=\"1.0\" encoding=\"utf-8\"?><VAST version=\"2.0\"><Ad id=\"VPAID\"><InLine><AdSystem version=\"2.0\">IO</AdSystem><AdTitle>Neptune</AdTitle><Impression></Impression><Creatives><Creative sequence=\"1\"><Linear><Duration>00:00:15</Duration><MediaFiles><MediaFile delivery=\"progressive\" width=\"640\" height=\"480\" scalable=\"1\" type=\"application/javascript\" apiFramework=\"VPAID\"><![CDATA[http://player-cdn.beachfrontmedia.com/players/neptune/3/17/5/neptune-player.js]]></MediaFile></MediaFiles><AdParameters><![CDATA[{\"dsp\":\"5d8231a4254b2eca804965a3,3.01\",\"inventory_id\":\"bid:1680831c-83a2-42a3-a790-a683d780749a:11bc5dd5-7421-4dd8-c926-40fa653bec76:${AUCTION_PRICE}:20.0\",\"request_endpoint\":\"//useast.bfmio.com/getmu\",\"inventory_type\":\"pre\"}]]></AdParameters></Linear></Creative></Creatives></InLine></Ad></VAST>",
-                  "adid": "1088",
-                  "adomain": [
-                    "beachfront.io"
-                  ],
-                  "cid": "277",
-                  "crid": "532",
-                  "w": 600,
-                  "h": 450
-                }
-              ],
-              "seat": "bfb-io-s1"
-            }
-          ],
-          "bidid": "5d8231a4254b2eca804965a3",
-          "cur": "USD"
-        }
-      }
-    },
-    {
-      "expectedRequest": {
-        "uri": "https://reachms.bfmio.com/bid.json?exchange_id=11bc5dd5-7421-4dd8-c926-40fa653bec76&prebidserver",
-        "body": {
-          "isPrebid": true,
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "imp": [
-            {
-              "id": "multivideo",
-              "secure": 0,
-              "video": {
-                "mimes": [
-                  "video/mp4",
-                  "video/webm"
-                ],
-                "w": 600,
-                "h": 450
-              },
-              "bidfloor": 3.01
-            }
-          ],
-          "site": {
-            "domain": "example.com",
-            "page": "http://example.com/whatever",
-            "publisher": {
-              "id": "publisher1"
-            }
-          },
-          "device": {
-            "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16",
-            "ip": "192.168.255.255",
-            "devicetype": 2
-          },
-          "user": {
-            "id": "some-user",
-            "buyeruid": "some-buyer"
-          },
-          "tmax": 1000,
-          "cur": [
-            "USD"
-          ],
-          "source": {
-            "tid": "61b87329-8790-47b7-90dd-c53ae7ce1723"
-          }
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "seatBid": [
-            {
-              "bid": [
-                {
-                  "id": "multivideo",
-                  "price": 20.0,
-                  "nurl": "https://useast.bfmio.com/getBids?aid=bid:b489a745-e577-4fcb-815d-0b6a1e052ec7:11bc5dd5-7421-4dd8-c926-40fa653bec76:20.0:20.0&v=1&dsp=5d8231a48d308d0a1f9b4526,0.61&i_type=pre",
-                  "adomain": [],
-                  "cat": [],
-                  "attr": [],
-                  "w": 0,
-                  "h": 0,
-                  "ext": {}
-                }
-              ],
-              "group": 0,
-              "ext": {}
-            }
-          ]
-        }
-      }
-    }
-  ],
-  "expectedBidResponses": [
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "multivideoAdmVideo",
-            "impid": "multivideo",
-            "price": 20,
-            "adm": "<?xml version=\"1.0\" encoding=\"utf-8\"?><VAST version=\"2.0\"><Ad id=\"VPAID\"><InLine><AdSystem version=\"2.0\">IO</AdSystem><AdTitle>Neptune</AdTitle><Impression></Impression><Creatives><Creative sequence=\"1\"><Linear><Duration>00:00:15</Duration><MediaFiles><MediaFile delivery=\"progressive\" width=\"640\" height=\"480\" scalable=\"1\" type=\"application/javascript\" apiFramework=\"VPAID\"><![CDATA[http://player-cdn.beachfrontmedia.com/players/neptune/3/17/5/neptune-player.js]]></MediaFile></MediaFiles><AdParameters><![CDATA[{\"dsp\":\"5d8231a4254b2eca804965a3,3.01\",\"inventory_id\":\"bid:1680831c-83a2-42a3-a790-a683d780749a:11bc5dd5-7421-4dd8-c926-40fa653bec76:${AUCTION_PRICE}:20.0\",\"request_endpoint\":\"//useast.bfmio.com/getmu\",\"inventory_type\":\"pre\"}]]></AdParameters></Linear></Creative></Creatives></InLine></Ad></VAST>",
-            "adid": "1088",
-            "adomain": [
-              "beachfront.io"
-            ],
-            "cid": "277",
-            "crid": "532",
-            "w": 600,
-            "h": 450
-          },
-          "type": "video"
-        }
-      ],
-      "seat": "beachfront"
-    },
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "multivideoNurlVideo",
-            "impid": "multivideo",
-            "price": 20,
-            "nurl": "https://useast.bfmio.com/getBids?aid=bid:b489a745-e577-4fcb-815d-0b6a1e052ec7:11bc5dd5-7421-4dd8-c926-40fa653bec76:20.0:20.0&v=1&dsp=5d8231a48d308d0a1f9b4526,0.61&i_type=pre",
-            "crid": "b489a745-e577-4fcb-815d-0b6a1e052ec7",
-            "w": 600,
-            "h": 450,
-            "ext": {}
-          },
-          "type": "video"
-        }
-      ],
-      "seat": "beachfront"
-    }
-  ]
-}
diff --git a/adapters/beachfront/beachfronttest/supplemental/six-nine-combo--response-order--banner-adm-nurl--PASS.json b/adapters/beachfront/beachfronttest/supplemental/six-nine-combo--response-order--banner-adm-nurl--PASS.json
new file mode 100644
index 00000000..56af3df3
--- /dev/null
+++ b/adapters/beachfront/beachfronttest/supplemental/six-nine-combo--response-order--banner-adm-nurl--PASS.json
@@ -0,0 +1,465 @@
+{
+  "mockBidRequest":
+  {
+    "id": "six-nine-combo",
+    "imp": [
+      {
+        "id": "ADMVideoWithBannerImp1",
+        "ext": {
+          "bidder": {
+            "videoResponseType": "adm",
+            "bidfloor": 1.01,
+            "appIds": {
+              "banner" : "bannerAppId1",
+              "video" :  "videoAppId1"
+            }
+          }
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 300,
+          "h": 400
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 400
+            }
+          ]
+        }
+      },
+      {
+        "id": "ADMVideoWithBannerImp2",
+        "ext": {
+          "bidder": {
+            "videoResponseType": "adm",
+            "bidfloor": 1.01,
+            "appIds": {
+              "banner" : "bannerAppId2",
+              "video" :  "videoAppId1"
+            }
+          }
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 100,
+          "h": 150
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 100,
+              "h": 150
+            }
+          ]
+        }
+      },
+      {
+        "id": "NURLVideoImp3",
+        "ext": {
+          "bidder": {
+            "videoResponseType": "nurl",
+            "bidfloor": 1.01,
+            "appId": "videoAppId1"
+          }
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 150,
+          "h": 300
+        }
+      },
+      {
+        "id": "RegularBannerImp4",
+        "ext": {
+          "bidder": {
+            "bidfloor": 1.05,
+            "appId" : "bannerAppId3"
+          }
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 1280,
+              "h": 1024
+            }
+          ]
+        }
+      },
+      {
+        "id": "RegularBannerImp5",
+        "ext": {
+          "bidder": {
+            "bidfloor": 1.05,
+            "appId" : "bannerAppId4"
+          }
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 1280,
+              "h": 1024
+            }
+          ]
+        }
+      },
+      {
+        "id": "ADMVideoImp6",
+        "ext": {
+          "bidder": {
+            "videoResponseType": "adm",
+            "bidfloor": 1.01,
+            "appId": "videoAppId1"
+          }
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 100,
+          "h": 150
+        }
+      }
+    ],
+    "site": {
+      "page": "http://example.com/whatever/something.html"
+    }
+  } ,
+  "httpCalls": [
+
+    {
+      "expectedRequest":  {
+        "uri": "https://qa.beachrtb.com/prebid_display",
+        "body": {
+          "slots": [
+            {
+              "slot": "ADMVideoWithBannerImp1",
+              "id": "bannerAppId1",
+              "bidfloor": 1.01,
+              "sizes": [
+                {
+                  "w": 300,
+                  "h": 400
+                }
+              ]
+            },
+            {
+              "slot": "ADMVideoWithBannerImp2",
+              "id": "bannerAppId2",
+              "bidfloor": 1.01,
+              "sizes": [
+                {
+                  "w": 100,
+                  "h": 150
+                }
+              ]
+            },
+            {
+              "slot": "RegularBannerImp4",
+              "id": "bannerAppId3",
+              "bidfloor": 1.05,
+              "sizes": [
+                {
+                  "w": 1280,
+                  "h": 1024
+                }
+              ]
+            },
+            {
+              "slot": "RegularBannerImp5",
+              "id": "bannerAppId4",
+              "bidfloor": 1.05,
+              "sizes": [
+                {
+                  "w": 1280,
+                  "h": 1024
+                }
+              ]
+            }
+          ],
+          "domain": "example.com",
+          "page": "http://example.com/whatever/something.html",
+          "referrer": "",
+          "search": "",
+          "secure": 0,
+          "deviceOs": "",
+          "deviceModel": "",
+          "isMobile": 0,
+          "ua": "",
+          "dnt": 0,
+          "user": {},
+          "adapterName": "BF_PREBID_S2S",
+          "adapterVersion": "0.9.1",
+          "ip": "",
+          "requestId": "six-nine-combo"
+        }
+      },
+      "mockResponse": {
+        "status" : 200,
+        "body": [
+          {
+            "crid": "crid_1",
+            "price": 1.594764,
+            "w": 100,
+            "h": 150,
+            "slot": "ADMVideoWithBannerImp1",
+            "adm": "<div id=\"crid_1\">Buy now!</div>"
+          },
+          {
+            "crid": "crid_2",
+            "price": 1.594764,
+            "w": 100,
+            "h": 150,
+            "slot": "ADMVideoWithBannerImp2",
+            "adm": "<div id=\"crid_2\">Buy now!</div>"
+          },
+          {
+            "crid": "crid_3",
+            "price": 1.501188,
+            "w": 1280,
+            "h": 1024,
+            "slot": "RegularBannerImp4",
+            "adm": "<div id=\"crid_3\">Buy now!</div>"
+          },
+          {
+            "crid": "crid_4",
+            "price": 1.501188,
+            "w": 1280,
+            "h": 1024,
+            "slot": "RegularBannerImp5",
+            "adm": "<div id=\"crid_4\">Buy now!</div>"
+          }
+        ]
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1",
+        "body": {
+          "id": "six-nine-combo",
+          "imp": [
+            {
+              "id": "ADMVideoWithBannerImp1",
+              "secure": 0,
+              "bidfloor": 1.01,
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "w": 300,
+                "h": 400
+              }
+            }
+          ],
+          "site": {
+            "domain": "example.com",
+            "page": "http://example.com/whatever/something.html"
+          },
+          "cur": [
+            "USD"
+          ]
+        }
+      },
+      "mockResponse": {
+        "status" : 200,
+        "body": {
+          "id": "six-nine-combo",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "crid_7",
+                  "impid": "ADMVideoWithBannerImp1",
+                  "price": 0.8,
+                  "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+                  "w": 300,
+                  "h": 400,
+                  "ext": {
+                    "duration": 30
+                  }
+                }
+              ],
+              "seat": "bfb-io-s1"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1",
+        "body": {
+          "id": "six-nine-combo",
+          "imp": [
+            {
+              "id": "ADMVideoWithBannerImp2",
+              "secure": 0,
+              "bidfloor": 1.01,
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "w": 100,
+                "h": 150
+              }
+            }
+          ],
+          "site": {
+            "domain": "example.com",
+            "page": "http://example.com/whatever/something.html"
+          },
+          "cur": [
+            "USD"
+          ]
+        }
+      },
+      "mockResponse": {
+        "status" : 200,
+        "body": {
+          "id": "six-nine-combo",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "crid_7",
+                  "impid": "ADMVideoWithBannerImp2",
+                  "price": 0.8,
+                  "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+                  "w": 300,
+                  "h": 400,
+                  "ext": {
+                    "duration": 30
+                  }
+                }
+              ],
+              "seat": "bfb-io-s1"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1",
+        "body": {
+          "id": "six-nine-combo",
+          "imp": [
+            {
+              "id": "ADMVideoImp6",
+              "bidfloor": 1.01,
+              "secure": 0,
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "w": 100,
+                "h": 150
+              }
+            }
+          ],
+          "site": {
+            "domain": "example.com",
+            "page": "http://example.com/whatever/something.html"
+          },
+          "cur": [
+            "USD"
+          ]
+        }
+      },
+      "mockResponse": {
+        "status" : 200,
+        "body": {
+          "id": "5f4f8790dca51d29347f4dd8",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "crid_8",
+                  "impid": "ADMVideoImp6",
+                  "price": 0.8,
+                  "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+                  "w": 100,
+                  "h": 150,
+                  "ext": {
+                    "duration": 30
+                  }
+                }
+              ],
+              "seat": "bfb-io-s1"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1&prebidserver",
+        "body": {
+          "isPrebid": true,
+          "id": "six-nine-combo",
+          "imp": [
+            {
+              "id": "NURLVideoImp3",
+              "bidfloor": 1.01,
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "w": 150,
+                "h": 300
+              },
+              "secure": 0
+            }
+          ],
+          "site": {
+            "domain": "example.com",
+            "page": "http://example.com/whatever/something.html"
+          },
+          "cur": [
+            "USD"
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
+          "seatBid": [
+            {
+              "bid": [
+                {
+                  "id": "NURLVideoImp3",
+                  "price": 20.0,
+                  "nurl": "https://useast.bfmio.com/getBids?aid=bid:de5b4976-0d2a-4032-874c-35e2b3e3ab95: videoAppId1: 10.0: 10.0&v=1&dsp=5f7da002b6b0ca361fc692b0,0.01&i_type=pre",
+                  "adomain": [
+                  ],
+                  "cat": [
+                  ],
+                  "attr": [
+                  ],
+                  "w": 0,
+                  "h": 0,
+                  "ext": {
+                  }
+                }
+              ],
+              "group": 0,
+              "ext": {
+              }
+            }
+          ]
+        }
+      }
+    }
+  ]
+}
diff --git a/adapters/beachfront/beachfronttest/supplemental/two-four-combo.json b/adapters/beachfront/beachfronttest/supplemental/two-four-combo.json
new file mode 100644
index 00000000..bff475ff
--- /dev/null
+++ b/adapters/beachfront/beachfronttest/supplemental/two-four-combo.json
@@ -0,0 +1,263 @@
+{
+  "mockBidRequest": {
+    "id": "two-four-combo",
+    "imp": [
+      {
+        "id": "ComboADMVideoWithBannerImp1",
+        "ext": {
+          "bidder": {
+            "videoResponseType": "adm",
+            "bidfloor": 1.01,
+            "appIds": {
+              "banner": "bannerAppId1",
+              "video": "videoAppId1"
+            }
+          }
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 300,
+          "h": 400
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 400
+            }
+          ]
+        }
+      },
+      {
+        "id": "ComboNurlVideoWithBannerImp2",
+        "ext": {
+          "bidder": {
+            "videoResponsetype": "nurl",
+            "bidfloor": 1.01,
+            "appIds": {
+              "banner": "bannerAppId2",
+              "video": "videoAppId1"
+            }
+          }
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 100,
+          "h": 150
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 100,
+              "h": 150
+            }
+          ]
+        }
+      }
+    ],
+    "site": {
+      "page": "http://example.com/whatever/something.html"
+    },
+    "device": {
+      "ip": "255.255.255.255"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/prebid_display",
+        "body": {
+          "slots":[
+            {
+              "slot":"ComboADMVideoWithBannerImp1",
+              "id":"bannerAppId1",
+              "bidfloor":1.01,
+              "sizes":[
+                {
+                  "w":300,
+                  "h":400
+                }
+              ]
+            },
+            {
+              "slot":"ComboNurlVideoWithBannerImp2",
+              "id":"bannerAppId2",
+              "bidfloor":1.01,
+              "sizes":[
+                {
+                  "w":100,
+                  "h":150
+                }
+              ]
+            }
+          ],
+          "domain":"example.com",
+          "page":"http://example.com/whatever/something.html",
+          "referrer":"",
+          "search":"",
+          "secure":0,
+          "deviceOs":"",
+          "deviceModel":"",
+          "isMobile":0,
+          "ua":"",
+          "dnt":0,
+          "user":{
+
+          },
+          "adapterName":"BF_PREBID_S2S",
+          "adapterVersion":"0.9.1",
+          "ip":"255.255.255.255",
+          "requestId":"two-four-combo"
+
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+
+        "body": [
+          {
+            "crid": "crid_1",
+            "price": 2.942808,
+            "w": 300,
+            "h": 250,
+            "slot": "div-gpt-ad-1460505748561-0",
+            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>"
+          },
+          {
+            "crid": "crid_1",
+            "price": 2.942808,
+            "w": 300,
+            "h": 250,
+            "slot": "div-gpt-ad-1460505748561-0",
+            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>"
+          }
+        ]
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1",
+        "body": {
+          "id":"two-four-combo",
+          "imp":[
+            {
+              "id":"ComboADMVideoWithBannerImp1",
+              "video":{
+                "mimes":[
+                  "video/mp4"
+                ],
+                "w":300,
+                "h":400
+              },
+              "bidfloor":1.01,
+              "secure":0
+            }
+          ],
+          "site":{
+            "domain":"example.com",
+            "page":"http://example.com/whatever/something.html"
+          },
+          "device":{
+            "devicetype": 2,
+            "ip":"255.255.255.255"
+          },
+          "cur":[
+            "USD"
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id":"two-four-combo",
+          "seatbid":[
+            {
+              "bid":[
+                {
+                  "id":"5fe7ad26d49d461ab0544836",
+                  "impid":"ComboADMVideoWithBannerImp1",
+                  "price":2.5500002,
+                  "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+                  "adid":"22873",
+                  "adomain":[
+                    "nintendo.com"
+                  ],
+                  "cid":"6095",
+                  "crid":"0",
+                  "cat":[
+                    "IAB9-30"
+                  ],
+                  "w":300,
+                  "h":400,
+                  "ext":{
+                    "duration":15
+                  }
+                }
+              ],
+              "seat":"bfb-io-s1"
+            }
+          ],
+          "bidid":"5fe7ad26d49d461ab0544836",
+          "cur":"USD"
+        }
+      }
+    },
+    {
+      "expectedRequest":  {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1&prebidserver",
+        "body": {
+          "isPrebid":true,
+          "id":"two-four-combo",
+          "imp":[
+            {
+              "id":"ComboNurlVideoWithBannerImp2",
+              "video":{
+                "mimes":[
+                  "video/mp4"
+                ],
+                "w":100,
+                "h":150
+              },
+              "bidfloor":1.01,
+              "secure":0
+            }
+          ],
+          "site":{
+            "domain":"example.com",
+            "page":"http://example.com/whatever/something.html"
+          },
+          "device":{
+            "devicetype": 2,
+            "ip":"255.255.255.255"
+          },
+          "cur":[
+            "USD"
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id":"two-four-combo",
+          "seatBid":[
+            {
+              "bid":[
+                {
+                  "id":"ComboNurlVideoWithBannerImp2",
+                  "price":2.4700302,
+                  "nurl":"https://useast.bfmio.com/getmu?aid=bid:747f49a2-7a15-40bd-a43f-b8576eaa0368:2096a4df-d32c-4b7b-b46c-f4b5d717327a:2.5500002:2.5500002&v=1&dsp=5fe7a6ab3f06cc571bf5c897,1.01&i_type=pre",
+                  "w":300,
+                  "h":400
+                }
+              ]
+            }
+          ]
+        }
+      }
+    }
+  ]
+}
diff --git a/adapters/beachfront/beachfronttest/supplemental/unmarshal-error-banner.json b/adapters/beachfront/beachfronttest/supplemental/unmarshal-error-banner.json
index fc1bb910..8d07852b 100644
--- a/adapters/beachfront/beachfronttest/supplemental/unmarshal-error-banner.json
+++ b/adapters/beachfront/beachfronttest/supplemental/unmarshal-error-banner.json
@@ -2,7 +2,7 @@
   "mockBidRequest": {
     "id": "some_test_ad",
     "site": {
-      "page": "https://test.opposingviews.com/i/society/republican-sen-collins-may-change-vote-tax-bill?cb=1234534"
+      "page": "https://some.domain.us/some/page.html"
     },
     "imp": [
       {
@@ -19,7 +19,7 @@
         "ext": {
           "bidder": {
             "bidfloor": 0.02,
-            "appId": 123456789101112
+            "appId": 123456
           }
         }
       }
diff --git a/adapters/beachfront/beachfronttest/supplemental/unmarshal-error-but-another-good-video.json b/adapters/beachfront/beachfronttest/supplemental/unmarshal-error-but-another-good-video.json
deleted file mode 100644
index 963a2adc..00000000
--- a/adapters/beachfront/beachfronttest/supplemental/unmarshal-error-but-another-good-video.json
+++ /dev/null
@@ -1,172 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-    "imp": [
-      {
-        "bidfloor": 0.02,
-        "id": "looser",
-        "video":{
-          "mimes":[
-            "video/mp4"
-          ],
-          "context":"instream",
-          "w":300,
-          "h":250
-        },
-        "ext": {
-          "bidder": {
-            "some": "other thing",
-            "bidfloor": "One MILLION dollars, ha, ha ha, ha, ha ha!",
-            "appId": {
-              "not": "the object",
-              "you": "were looking for"
-            }
-          }
-        }
-      },
-     {
-        "id":"video2",
-        "ext":{
-          "bidder":{
-            "bidfloor":0.01,
-            "appId":"11bc5dd5-7421-4dd8-c926-40fa653bec76"
-          }
-        },
-        "video":{
-          "mimes":[
-            "video/mp4", "video/webm"
-          ],
-          "context":"instream",
-          "w":600,
-          "h":450
-        }
-      }
-    ],
-    "site": {
-      "domain": "example.com",
-      "page": "http://example.com/whatever"
-    },
-    "device": {
-      "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16",
-      "ip": "127.0.0.1",
-      "js": 1
-    },
-    "cur": [
-      "USD"
-    ],
-    "user": {
-      "id": "some-user",
-      "buyeruid": "some-buyer"
-    }
-  },
-
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "https://reachms.bfmio.com/bid.json?exchange_id=11bc5dd5-7421-4dd8-c926-40fa653bec76&prebidserver",
-        "body": {
-          "isPrebid": true,
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "imp": [
-            {
-              "video": {
-                "w": 600,
-                "h": 450,
-                "mimes": [
-                  "video/mp4",
-                  "video/webm"
-                ]
-              },
-              "id": "video2",
-              "secure": 0
-            }
-          ],
-          "site": {
-            "domain": "example.com",
-            "page": "http://example.com/whatever"
-          },
-          "device": {
-            "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16",
-            "ip": "192.168.255.255",
-            "devicetype": 2,
-            "js": 1
-          },
-          "cur": [
-            "USD"
-          ],
-          "user": {
-            "id": "some-user",
-            "buyeruid": "some-buyer"
-          }
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "seatBid": [
-            {
-              "bid": [
-                {
-                  "w": 0,
-                  "h": 0,
-                  "id": "0",
-                  "impid": "",
-                  "price": 0.01,
-                  "adid": "",
-                  "nurl": "https://evt.bfmio.com/getmu?aid=bid:0d32aea6-d199-4803-a188-bd7cf7be1e12:11bc5dd5-7421-4dd8-c926-40fa653bec76:0.01:0.01&v=1&dsp=5afda03c7c2d2bfaee55a3f0,0.01&i_type=pre",
-                  "adm": "",
-                  "adomain": [],
-                  "iurl": "",
-                  "cid": "",
-                  "crid": "",
-                  "attr": [],
-                  "ext": {},
-                  "cat": []
-                }
-              ],
-              "seat": "",
-              "group": 0,
-              "ext": {
-              }
-            }
-          ]
-        }
-      }
-    }
-  ],
-
-
-  "expectedBids": [
-    {
-
-      "bid":[
-        {
-          "id":"61b87329-8790-47b7-90dd-c53ae7ce1723",
-          "impid":"video2",
-          "price":9.605201,
-          "nurl":"https://evt.bfmio.com/getmu?aid=bid:0d32aea6-d199-4803-a188-bd7cf7be1e12:11bc5dd5-7421-4dd8-c926-40fa653bec76:0.01:0.01&v=1&dsp=5afda03c7c2d2bfaee55a3f0,0.01&i_type=pre",
-          "crid":"video10d32aea6-d199-4803-a188-bd7cf7be1e12",
-          "w":1500,
-          "h":1280,
-          "ext":{
-            "prebid":{
-              "type":"video"
-            },
-            "bidder":{
-
-            }
-          }
-        }
-      ]
-    }
-  ],
-
-
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "ignoring imp id=looser, error while decoding extImpBeachfront, err: json: cannot unmarshal string into Go struct field ExtImpBeachfront.bidfloor of type float64",
-      "comparison": "literal"
-    }
-  ]
-}
diff --git a/adapters/beachfront/beachfronttest/supplemental/unmarshal-error-video.json b/adapters/beachfront/beachfronttest/supplemental/unmarshal-error-video.json
index 3082ff20..73ac2032 100644
--- a/adapters/beachfront/beachfronttest/supplemental/unmarshal-error-video.json
+++ b/adapters/beachfront/beachfronttest/supplemental/unmarshal-error-video.json
@@ -2,7 +2,7 @@
   "mockBidRequest": {
     "id": "some_test_ad",
     "site": {
-      "page": "https://example.com/whatever"
+      "page": "https://some.domain.us/some/page.html"
     },
     "imp": [
       {
diff --git a/adapters/dmx/dmxtest/exemplary/imp-populated-banner.json b/adapters/dmx/dmxtest/exemplary/imp-populated-banner.json
index f2b30bb4..b39b722d 100644
--- a/adapters/dmx/dmxtest/exemplary/imp-populated-banner.json
+++ b/adapters/dmx/dmxtest/exemplary/imp-populated-banner.json
@@ -49,7 +49,12 @@
             "id":"ed6207cefff74c14878963566683c070",
             "name":"Skout - iOS Match Buy",
             "publisher":{
-              "id":"10400"
+              "id":"10400",
+              "ext": {
+                "dmx": {
+                  "id": "10400"
+                }
+              }
             },
             "storeurl":"https://itunes.apple.com/app/id302324249"
           },
diff --git a/adapters/dmx/dmxtest/exemplary/simple-app.json b/adapters/dmx/dmxtest/exemplary/simple-app.json
index a2d57163..3c226f5e 100644
--- a/adapters/dmx/dmxtest/exemplary/simple-app.json
+++ b/adapters/dmx/dmxtest/exemplary/simple-app.json
@@ -49,7 +49,12 @@
             "id":"ed6207cefff74c14878963566683c070",
             "name":"Skout - iOS Match Buy",
             "publisher":{
-              "id":"10400"
+              "id":"10400",
+              "ext": {
+                "dmx": {
+                  "id": "10400"
+                }
+              }
             },
             "storeurl":"https://itunes.apple.com/app/id302324249"
           },
diff --git a/adapters/dmx/params_test.go b/adapters/dmx/params_test.go
new file mode 100644
index 00000000..0e5250b1
--- /dev/null
+++ b/adapters/dmx/params_test.go
@@ -0,0 +1,53 @@
+package dmx
+
+import (
+	"encoding/json"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"testing"
+)
+
+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.BidderDmx, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected dmx params: %s", validParam)
+		}
+	}
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderDmx, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema was not supposed to be valid: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"memberid": "anyMemberId", "placement_id":"anyPlacementId", "seller_id":"anySellerId", "dmxid":"anyDmxid", "tagid": "25251", "bidfloor": 0.01}`,
+	`{"memberid": "anyMemberId", "seller_id": "anySellerId", "dmxid": "anyDmxid", "tagid": "25251", "bidfloor": 0.01}`,
+	`{"memberid": "anyMemberId", "placement_id": "anyPlacementId", "dmxid": "anyDmxid", "tagid": "25251", "bidfloor": 0.01}`,
+	`{"memberid": "anyMemberId", "placement_id": "anyPlacementId", "seller_id": "anySellerId", "tagid": "25251", "bidfloor": 0.01}`,
+	`{"memberid": "anyMemberId", "placement_id": "anyPlacementId", "seller_id": "anySellerId", "dmxid": "anyDmxid", "bidfloor": 0.01}`,
+	`{"memberid": "anyMemberId", "placement_id": "anyPlacementId", "seller_id": "anySellerId", "dmxid": "anyDmxid", "tagid": "25251"}`,
+	`{"memberid": "anyMemberId"}`,
+}
+
+var invalidParams = []string{
+	`null`,
+	`nil`,
+	``,
+	`[]`,
+	`true`,
+	`{}`,
+	// Invalid because missing "memberid"
+	`{"placement_id":"anyPlacementId", "seller_id":"anySellerId", "dmxid":"anyDmxid", "tagid": "25251", "bidfloor": 0.01}`,
+	// Invalid because "memberid" not a string
+	`{"memberid": 5}`,
+	// Invalid since dmxid need to be a string
+	`{"memberid": "1222", "dmxid": 432423}`,
+	//
+	// ...more invalid param scenarios
+	//
+}
diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go
index 51f35316..b42e45b6 100644
--- a/endpoints/openrtb2/auction_benchmark_test.go
+++ b/endpoints/openrtb2/auction_benchmark_test.go
@@ -77,6 +77,7 @@ func BenchmarkOpenrtbEndpoint(b *testing.B) {
 		nil,
 		&config.Configuration{},
 		newTestMetrics(),
+		infos,
 		gdpr.AlwaysAllow{},
 		currency.NewRateConverter(&http.Client{}, "", time.Duration(0)),
 		empty_fetcher.EmptyFetcher{},
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index ade3c14a..413d7995 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -1440,19 +1440,19 @@ func TestCurrencyTrunc(t *testing.T) {
 
 	ui := uint64(1)
 	req := openrtb.BidRequest{
-		ID: "someID",
+		ID: "anyRequestID",
 		Imp: []openrtb.Imp{
 			{
-				ID: "imp-ID",
+				ID: "anyImpID",
 				Banner: &openrtb.Banner{
 					W: &ui,
 					H: &ui,
 				},
-				Ext: json.RawMessage("{\"appnexus\": {\"placementId\": 5667}}"),
+				Ext: json.RawMessage(`{"appnexus": {"placementId": 5667}}`),
 			},
 		},
 		Site: &openrtb.Site{
-			ID: "myID",
+			ID: "anySiteID",
 		},
 		Cur: []string{"USD", "EUR"},
 	}
@@ -1484,10 +1484,10 @@ func TestCCPAInvalid(t *testing.T) {
 
 	ui := uint64(1)
 	req := openrtb.BidRequest{
-		ID: "someID",
+		ID: "anyRequestID",
 		Imp: []openrtb.Imp{
 			{
-				ID: "imp-ID",
+				ID: "anyImpID",
 				Banner: &openrtb.Banner{
 					W: &ui,
 					H: &ui,
@@ -1496,10 +1496,10 @@ func TestCCPAInvalid(t *testing.T) {
 			},
 		},
 		Site: &openrtb.Site{
-			ID: "myID",
+			ID: "anySiteID",
 		},
 		Regs: &openrtb.Regs{
-			Ext: json.RawMessage(`{"us_privacy":"invalid by length"}`),
+			Ext: json.RawMessage(`{"us_privacy": "invalid by length"}`),
 		},
 	}
 
@@ -1532,10 +1532,10 @@ func TestNoSaleInvalid(t *testing.T) {
 
 	ui := uint64(1)
 	req := openrtb.BidRequest{
-		ID: "someID",
+		ID: "anyRequestID",
 		Imp: []openrtb.Imp{
 			{
-				ID: "imp-ID",
+				ID: "anyImpID",
 				Banner: &openrtb.Banner{
 					W: &ui,
 					H: &ui,
@@ -1544,12 +1544,12 @@ func TestNoSaleInvalid(t *testing.T) {
 			},
 		},
 		Site: &openrtb.Site{
-			ID: "myID",
+			ID: "anySiteID",
 		},
 		Regs: &openrtb.Regs{
-			Ext: json.RawMessage(`{"us_privacy":"1NYN"}`),
+			Ext: json.RawMessage(`{"us_privacy": "1NYN"}`),
 		},
-		Ext: json.RawMessage(`{"prebid":{"nosale":["*", "appnexus"]}}`),
+		Ext: json.RawMessage(`{"prebid": {"nosale": ["*", "appnexus"]} }`),
 	}
 
 	errL := deps.validateRequest(&req)
@@ -1583,10 +1583,10 @@ func TestValidateSourceTID(t *testing.T) {
 
 	ui := uint64(1)
 	req := openrtb.BidRequest{
-		ID: "someID",
+		ID: "anyRequestID",
 		Imp: []openrtb.Imp{
 			{
-				ID: "imp-ID",
+				ID: "anyImpID",
 				Banner: &openrtb.Banner{
 					W: &ui,
 					H: &ui,
@@ -1595,10 +1595,7 @@ func TestValidateSourceTID(t *testing.T) {
 			},
 		},
 		Site: &openrtb.Site{
-			ID: "myID",
-		},
-		Regs: &openrtb.Regs{
-			Ext: json.RawMessage(`{"us_privacy":"invalid by length"}`),
+			ID: "anySiteID",
 		},
 	}
 
@@ -1627,10 +1624,10 @@ func TestSChainInvalid(t *testing.T) {
 
 	ui := uint64(1)
 	req := openrtb.BidRequest{
-		ID: "someID",
+		ID: "anyRequestID",
 		Imp: []openrtb.Imp{
 			{
-				ID: "imp-ID",
+				ID: "anyImpID",
 				Banner: &openrtb.Banner{
 					W: &ui,
 					H: &ui,
@@ -1639,17 +1636,14 @@ func TestSChainInvalid(t *testing.T) {
 			},
 		},
 		Site: &openrtb.Site{
-			ID: "myID",
-		},
-		Regs: &openrtb.Regs{
-			Ext: json.RawMessage(`{"us_privacy":"abcd"}`),
+			ID: "anySiteID",
 		},
 		Ext: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":1}],"ver":"1.0"}}]}}`),
 	}
 
 	errL := deps.validateRequest(&req)
 
-	expectedError := fmt.Errorf("request.ext.prebid.schains contains multiple schains for bidder appnexus; it must contain no more than one per bidder.")
+	expectedError := errors.New("request.ext.prebid.schains contains multiple schains for bidder appnexus; it must contain no more than one per bidder.")
 	assert.ElementsMatch(t, errL, []error{expectedError})
 }
 
@@ -1828,6 +1822,260 @@ func TestValidateAndFillSourceTID(t *testing.T) {
 	}
 }
 
+func TestEidPermissionsInvalid(t *testing.T) {
+	deps := &endpointDeps{
+		&nobidExchange{},
+		newParamsValidator(t),
+		&mockStoredReqFetcher{},
+		empty_fetcher.EmptyFetcher{},
+		empty_fetcher.EmptyFetcher{},
+		&config.Configuration{},
+		newTestMetrics(),
+		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		map[string]string{},
+		false,
+		[]byte{},
+		openrtb_ext.BuildBidderMap(),
+		nil,
+		nil,
+		hardcodedResponseIPValidator{response: true},
+	}
+
+	ui := uint64(1)
+	req := openrtb.BidRequest{
+		ID: "anyRequestID",
+		Imp: []openrtb.Imp{
+			{
+				ID: "anyImpID",
+				Banner: &openrtb.Banner{
+					W: &ui,
+					H: &ui,
+				},
+				Ext: json.RawMessage(`{"appnexus": {"placementId": 5667}}`),
+			},
+		},
+		Site: &openrtb.Site{
+			ID: "anySiteID",
+		},
+		Ext: json.RawMessage(`{"prebid": {"data": {"eidpermissions": [{"source":"a", "bidders":[]}]} } }`),
+	}
+
+	errL := deps.validateRequest(&req)
+
+	expectedError := errors.New(`request.ext.prebid.data.eidpermissions[0] missing or empty required field: "bidders"`)
+	assert.ElementsMatch(t, errL, []error{expectedError})
+}
+
+func TestValidateEidPermissions(t *testing.T) {
+	knownBidders := map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")}
+	knownAliases := map[string]string{"b": "b"}
+
+	testCases := []struct {
+		description   string
+		request       *openrtb_ext.ExtRequest
+		expectedError error
+	}{
+		{
+			description:   "Valid - Nil ext",
+			request:       nil,
+			expectedError: nil,
+		},
+		{
+			description:   "Valid - Empty ext",
+			request:       &openrtb_ext.ExtRequest{},
+			expectedError: nil,
+		},
+		{
+			description:   "Valid - Nil ext.prebid.data",
+			request:       &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{}},
+			expectedError: nil,
+		},
+		{
+			description:   "Valid - Empty ext.prebid.data",
+			request:       &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{}}},
+			expectedError: nil,
+		},
+		{
+			description:   "Valid - Nil ext.prebid.data.eidpermissions",
+			request:       &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: nil}}},
+			expectedError: nil,
+		},
+		{
+			description:   "Valid - None",
+			request:       &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{}}}},
+			expectedError: nil,
+		},
+		{
+			description: "Valid - One",
+			request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "sourceA", Bidders: []string{"a"}},
+			}}}},
+			expectedError: nil,
+		},
+		{
+			description: "Valid - Many",
+			request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "sourceA", Bidders: []string{"a"}},
+				{Source: "sourceB", Bidders: []string{"a"}},
+			}}}},
+			expectedError: nil,
+		},
+		{
+			description: "Invalid - Missing Source",
+			request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "sourceA", Bidders: []string{"a"}},
+				{Bidders: []string{"a"}},
+			}}}},
+			expectedError: errors.New(`request.ext.prebid.data.eidpermissions[1] missing required field: "source"`),
+		},
+		{
+			description: "Invalid - Duplicate Source",
+			request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "sourceA", Bidders: []string{"a"}},
+				{Source: "sourceA", Bidders: []string{"a"}},
+			}}}},
+			expectedError: errors.New(`request.ext.prebid.data.eidpermissions[1] duplicate entry with field: "source"`),
+		},
+		{
+			description: "Invalid - Missing Bidders - Nil",
+			request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "sourceA", Bidders: []string{"a"}},
+				{Source: "sourceB"},
+			}}}},
+			expectedError: errors.New(`request.ext.prebid.data.eidpermissions[1] missing or empty required field: "bidders"`),
+		},
+		{
+			description: "Invalid - Missing Bidders - Empty",
+			request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "sourceA", Bidders: []string{"a"}},
+				{Source: "sourceB", Bidders: []string{}},
+			}}}},
+			expectedError: errors.New(`request.ext.prebid.data.eidpermissions[1] missing or empty required field: "bidders"`),
+		},
+		{
+			description: "Invalid - Invalid Bidders",
+			request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "sourceA", Bidders: []string{"a"}},
+				{Source: "sourceB", Bidders: []string{"z"}},
+			}}}},
+			expectedError: errors.New(`request.ext.prebid.data.eidpermissions[1] contains unrecognized bidder "z"`),
+		},
+	}
+
+	endpoint := &endpointDeps{bidderMap: knownBidders}
+	for _, test := range testCases {
+		result := endpoint.validateEidPermissions(test.request, knownAliases)
+		assert.Equal(t, test.expectedError, result, test.description)
+	}
+}
+
+func TestValidateBidders(t *testing.T) {
+	testCases := []struct {
+		description   string
+		bidders       []string
+		knownBidders  map[string]openrtb_ext.BidderName
+		knownAliases  map[string]string
+		expectedError error
+	}{
+		{
+			description:   "Valid - No Bidders",
+			bidders:       []string{},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: nil,
+		},
+		{
+			description:   "Valid - All Bidders",
+			bidders:       []string{"*"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: nil,
+		},
+		{
+			description:   "Valid - One Core Bidder",
+			bidders:       []string{"a"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: nil,
+		},
+		{
+			description:   "Valid - Many Core Bidders",
+			bidders:       []string{"a", "b"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a"), "b": openrtb_ext.BidderName("b")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: nil,
+		},
+		{
+			description:   "Valid - One Alias Bidder",
+			bidders:       []string{"c"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: nil,
+		},
+		{
+			description:   "Valid - Many Alias Bidders",
+			bidders:       []string{"c", "d"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c", "d": "d"},
+			expectedError: nil,
+		},
+		{
+			description:   "Valid - Mixed Core + Alias Bidders",
+			bidders:       []string{"a", "c"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: nil,
+		},
+		{
+			description:   "Invalid - Unknown Bidder",
+			bidders:       []string{"z"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: errors.New(`unrecognized bidder "z"`),
+		},
+		{
+			description:   "Invalid - Unknown Bidder Case Sensitive",
+			bidders:       []string{"A"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: errors.New(`unrecognized bidder "A"`),
+		},
+		{
+			description:   "Invalid - Unknown Bidder With Known Bidders",
+			bidders:       []string{"a", "c", "z"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: errors.New(`unrecognized bidder "z"`),
+		},
+		{
+			description:   "Invalid - All Bidders With Known Bidder",
+			bidders:       []string{"*", "a"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: errors.New(`bidder wildcard "*" mixed with specific bidders`),
+		},
+		{
+			description:   "Invalid - Returns First Error - All Bidders",
+			bidders:       []string{"*", "z"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: errors.New(`bidder wildcard "*" mixed with specific bidders`),
+		},
+		{
+			description:   "Invalid - Returns First Error - Unknown Bidder",
+			bidders:       []string{"z", "*"},
+			knownBidders:  map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")},
+			knownAliases:  map[string]string{"c": "c"},
+			expectedError: errors.New(`unrecognized bidder "z"`),
+		},
+	}
+
+	for _, test := range testCases {
+		result := validateBidders(test.bidders, test.knownBidders, test.knownAliases)
+		assert.Equal(t, test.expectedError, result, test.description)
+	}
+}
+
 // nobidExchange is a well-behaved exchange which always bids "no bid".
 type nobidExchange struct {
 	gotRequest *openrtb.BidRequest
diff --git a/exchange/auction_test.go b/exchange/auction_test.go
index cdd6a6ca..7e8a8a8c 100644
--- a/exchange/auction_test.go
+++ b/exchange/auction_test.go
@@ -6,9 +6,9 @@ import (
 	"encoding/xml"
 	"fmt"
 	"io/ioutil"
+	"path/filepath"
 	"regexp"
 	"strconv"
-	"strings"
 	"testing"
 
 	"github.com/prebid/prebid-server/config"
@@ -100,57 +100,21 @@ func TestBuildCacheString(t *testing.T) {
 // TestCacheJSON executes tests for all the *.json files in cachetest.
 // customcachekey.json test here verifies custom cache key not used for non-vast video
 func TestCacheJSON(t *testing.T) {
-	if specFiles, err := ioutil.ReadDir("./cachetest"); err == nil {
-		for _, specFile := range specFiles {
-			fileName := "./cachetest/" + specFile.Name()
-			fileDisplayName := "exchange/cachetest/" + specFile.Name()
-			specData, err := loadCacheSpec(fileName)
-			if err != nil {
-				t.Fatalf("Failed to load contents of file %s: %v", fileDisplayName, err)
+	for _, dir := range []string{"cachetest", "customcachekeytest", "impcustomcachekeytest", "eventscachetest"} {
+		if specFiles, err := ioutil.ReadDir(dir); err == nil {
+			for _, specFile := range specFiles {
+				fileName := filepath.Join(dir, specFile.Name())
+				fileDisplayName := "exchange/" + fileName
+				t.Run(fileDisplayName, func(t *testing.T) {
+					specData, err := loadCacheSpec(fileName)
+					if assert.NoError(t, err, "Failed to load contents of file %s: %v", fileDisplayName, err) {
+						runCacheSpec(t, fileDisplayName, specData)
+					}
+				})
 			}
-
-			runCacheSpec(t, fileDisplayName, specData)
-		}
-	} else {
-		t.Fatalf("Failed to read contents of directory exchange/cachetest/: %v", err)
-	}
-}
-
-// TestCacheJSON executes tests for all the *.json files in customcachekeytest.
-// customcachekey.json test here verifies custom cache key is used for vast video
-func TestCustomCacheKeyJSON(t *testing.T) {
-	if specFiles, err := ioutil.ReadDir("./customcachekeytest"); err == nil {
-		for _, specFile := range specFiles {
-			fileName := "./customcachekeytest/" + specFile.Name()
-			fileDisplayName := "exchange/customcachekeytest/" + specFile.Name()
-			specData, err := loadCacheSpec(fileName)
-			if err != nil {
-				t.Fatalf("Failed to load contents of file %s: %v", fileDisplayName, err)
-			}
-
-			runCacheSpec(t, fileDisplayName, specData)
-		}
-	} else {
-		t.Fatalf("Failed to read contents of directory exchange/customcachekeytest/: %v", err)
-	}
-}
-
-// TestMultiImpCache executes multi-Imp test cases found in *.json files in
-// impcustomcachekeytest.
-func TestCustomCacheKeyMultiImp(t *testing.T) {
-	if specFiles, err := ioutil.ReadDir("./impcustomcachekeytest"); err == nil {
-		for _, specFile := range specFiles {
-			fileName := "./impcustomcachekeytest/" + specFile.Name()
-			fileDisplayName := "exchange/impcustomcachekeytest/" + specFile.Name()
-			multiImpSpecData, err := loadCacheSpec(fileName)
-			if err != nil {
-				t.Fatalf("Failed to load contents of file %s: %v", fileDisplayName, err)
-			}
-
-			runCacheSpec(t, fileDisplayName, multiImpSpecData)
+		} else {
+			t.Fatalf("Failed to read contents of directory exchange/%s: %v", dir, err)
 		}
-	} else {
-		t.Fatalf("Failed to read contents of directory exchange/customcachekeytest/: %v", err)
 	}
 }
 
@@ -169,9 +133,8 @@ func loadCacheSpec(filename string) (*cacheSpec, error) {
 	return &spec, nil
 }
 
-// runCacheSpec has been modified to handle multi-Imp and multi-bid Json test files,
-// it cycles through the bids found in the test cases hardcoded in json files and
-// finds the highest bid of every Imp.
+// runCacheSpec cycles through the bids found in the json test cases and
+// finds the highest bid of every Imp, then tests doCache() with resulting auction object
 func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) {
 	var bid *pbsOrtbBid
 	winningBidsByImp := make(map[string]*pbsOrtbBid)
@@ -245,7 +208,14 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) {
 		winningBidsByBidder: winningBidsByBidder,
 		roundedPrices:       roundedPrices,
 	}
-	_ = testAuction.doCache(ctx, cache, targData, &specData.BidRequest, 60, &specData.DefaultTTLs, bidCategory, &specData.DebugLog)
+	evTracking := &eventTracking{
+		accountID:          "TEST_ACC_ID",
+		enabledForAccount:  specData.EventsDataEnabledForAccount,
+		enabledForRequest:  specData.EventsDataEnabledForRequest,
+		externalURL:        "http://localhost",
+		auctionTimestampMs: 1234567890,
+	}
+	_ = testAuction.doCache(ctx, cache, targData, evTracking, &specData.BidRequest, 60, &specData.DefaultTTLs, bidCategory, &specData.DebugLog)
 
 	if len(specData.ExpectedCacheables) > len(cache.items) {
 		t.Errorf("%s:  [CACHE_ERROR] Less elements were cached than expected \n", fileDisplayName)
@@ -254,27 +224,43 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) {
 	} else { // len(specData.ExpectedCacheables) == len(cache.items)
 		// We cached the exact number of elements we expected, now we compare them side by side in n^2
 		var matched int = 0
-		var formattedExpectedData string
-		for i := 0; i < len(specData.ExpectedCacheables); i++ {
-			if specData.ExpectedCacheables[i].Type == prebid_cache_client.TypeJSON {
-				ExpectedData := strings.Replace(string(specData.ExpectedCacheables[i].Data), "\\", "", -1)
-				ExpectedData = strings.Replace(ExpectedData, " ", "", -1)
-				formattedExpectedData = ExpectedData[1 : len(ExpectedData)-1]
-			} else {
-				formattedExpectedData = string(specData.ExpectedCacheables[i].Data)
+		for i, expectedCacheable := range specData.ExpectedCacheables {
+			found := false
+			var expectedData interface{}
+			if err := json.Unmarshal(expectedCacheable.Data, &expectedData); err != nil {
+				t.Fatalf("Failed to decode expectedCacheables[%d].value: %v", i, err)
 			}
-			for j := 0; j < len(cache.items); j++ {
-				if formattedExpectedData == string(cache.items[j].Data) &&
-					specData.ExpectedCacheables[i].TTLSeconds == cache.items[j].TTLSeconds &&
-					specData.ExpectedCacheables[i].Type == cache.items[j].Type &&
-					len(specData.ExpectedCacheables[i].Key) <= len(cache.items[j].Key) &&
-					specData.ExpectedCacheables[i].Key == cache.items[j].Key[:len(specData.ExpectedCacheables[i].Key)] {
-					matched++
+			if s, ok := expectedData.(string); ok && expectedCacheable.Type == prebid_cache_client.TypeJSON {
+				// decode again if we have pre-encoded json string values
+				if err := json.Unmarshal([]byte(s), &expectedData); err != nil {
+					t.Fatalf("Failed to re-decode expectedCacheables[%d].value :%v", i, err)
 				}
 			}
+			for j, cachedItem := range cache.items {
+				var actualData interface{}
+				if err := json.Unmarshal(cachedItem.Data, &actualData); err != nil {
+					t.Fatalf("Failed to decode actual cache[%d].value: %s", j, err)
+				}
+				if assert.ObjectsAreEqual(expectedData, actualData) &&
+					expectedCacheable.TTLSeconds == cachedItem.TTLSeconds &&
+					expectedCacheable.Type == cachedItem.Type &&
+					len(expectedCacheable.Key) <= len(cachedItem.Key) &&
+					expectedCacheable.Key == cachedItem.Key[:len(expectedCacheable.Key)] {
+					found = true
+					cache.items = append(cache.items[:j], cache.items[j+1:]...) // remove matched item
+					break
+				}
+			}
+			if found {
+				matched++
+			} else {
+				t.Errorf("%s: [CACHE_ERROR] Did not see expected cacheable #%d: type=%s, ttl=%d, value=%s", fileDisplayName, i, expectedCacheable.Type, expectedCacheable.TTLSeconds, string(expectedCacheable.Data))
+			}
 		}
 		if matched != len(specData.ExpectedCacheables) {
-			t.Errorf("%s: [CACHE_ERROR] One or more keys were not cached as we expected \n", fileDisplayName)
+			for i, item := range cache.items {
+				t.Errorf("%s: [CACHE_ERROR] Got unexpected cached item #%d: type=%s, ttl=%d, value=%s", fileDisplayName, i, item.Type, item.TTLSeconds, string(item.Data))
+			}
 			t.FailNow()
 		}
 	}
@@ -508,6 +494,8 @@ type cacheSpec struct {
 	TargetDataIncludeBidderKeys bool                            `json:"targetDataIncludeBidderKeys"`
 	TargetDataIncludeCacheBids  bool                            `json:"targetDataIncludeCacheBids"`
 	TargetDataIncludeCacheVast  bool                            `json:"targetDataIncludeCacheVast"`
+	EventsDataEnabledForAccount bool                            `json:"eventsDataEnabledForAccount"`
+	EventsDataEnabledForRequest bool                            `json:"eventsDataEnabledForRequest"`
 	DebugLog                    DebugLog                        `json:"debugLog,omitempty"`
 }
 
diff --git a/exchange/events_test.go b/exchange/events_test.go
new file mode 100644
index 00000000..1d72739d
--- /dev/null
+++ b/exchange/events_test.go
@@ -0,0 +1,138 @@
+package exchange
+
+import (
+	"testing"
+
+	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+)
+
+func Test_eventsData_makeBidExtEvents(t *testing.T) {
+	type args struct {
+		enabledForAccount bool
+		enabledForRequest bool
+		bidType           openrtb_ext.BidType
+	}
+	tests := []struct {
+		name string
+		args args
+		want *openrtb_ext.ExtBidPrebidEvents
+	}{
+		{
+			name: "banner: events enabled for request, disabled for account",
+			args: args{enabledForAccount: false, enabledForRequest: true, bidType: openrtb_ext.BidTypeBanner},
+			want: &openrtb_ext.ExtBidPrebidEvents{
+				Win: "http://localhost/event?t=win&b=BID-1&a=123456&bidder=openx&ts=1234567890",
+				Imp: "http://localhost/event?t=imp&b=BID-1&a=123456&bidder=openx&ts=1234567890",
+			},
+		},
+		{
+			name: "banner: events enabled for account, disabled for request",
+			args: args{enabledForAccount: true, enabledForRequest: false, bidType: openrtb_ext.BidTypeBanner},
+			want: &openrtb_ext.ExtBidPrebidEvents{
+				Win: "http://localhost/event?t=win&b=BID-1&a=123456&bidder=openx&ts=1234567890",
+				Imp: "http://localhost/event?t=imp&b=BID-1&a=123456&bidder=openx&ts=1234567890",
+			},
+		},
+		{
+			name: "banner: events disabled for account and request",
+			args: args{enabledForAccount: false, enabledForRequest: false, bidType: openrtb_ext.BidTypeBanner},
+			want: nil,
+		},
+		{
+			name: "video: events enabled for account and request",
+			args: args{enabledForAccount: true, enabledForRequest: true, bidType: openrtb_ext.BidTypeVideo},
+			want: nil,
+		},
+		{
+			name: "video: events disabled for account and request",
+			args: args{enabledForAccount: false, enabledForRequest: false, bidType: openrtb_ext.BidTypeVideo},
+			want: nil,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			evData := &eventTracking{
+				enabledForAccount:  tt.args.enabledForAccount,
+				enabledForRequest:  tt.args.enabledForRequest,
+				accountID:          "123456",
+				auctionTimestampMs: 1234567890,
+				externalURL:        "http://localhost",
+			}
+			bid := &pbsOrtbBid{bid: &openrtb.Bid{ID: "BID-1"}, bidType: tt.args.bidType}
+			assert.Equal(t, tt.want, evData.makeBidExtEvents(bid, openrtb_ext.BidderOpenx))
+		})
+	}
+}
+
+func Test_eventsData_modifyBidJSON(t *testing.T) {
+	type args struct {
+		enabledForAccount bool
+		enabledForRequest bool
+		bidType           openrtb_ext.BidType
+	}
+	tests := []struct {
+		name      string
+		args      args
+		jsonBytes []byte
+		want      []byte
+	}{
+		{
+			name:      "banner: events enabled for request, disabled for account",
+			args:      args{enabledForAccount: false, enabledForRequest: true, bidType: openrtb_ext.BidTypeBanner},
+			jsonBytes: []byte(`{"ID": "something"}`),
+			want:      []byte(`{"ID": "something", "wurl": "http://localhost/event?t=win&b=BID-1&a=123456&bidder=openx&ts=1234567890"}`),
+		},
+		{
+			name:      "banner: events enabled for account, disabled for request",
+			args:      args{enabledForAccount: true, enabledForRequest: false, bidType: openrtb_ext.BidTypeBanner},
+			jsonBytes: []byte(`{"ID": "something"}`),
+			want:      []byte(`{"ID": "something", "wurl": "http://localhost/event?t=win&b=BID-1&a=123456&bidder=openx&ts=1234567890"}`),
+		},
+		{
+			name:      "banner: events disabled for account and request",
+			args:      args{enabledForAccount: false, enabledForRequest: false, bidType: openrtb_ext.BidTypeBanner},
+			jsonBytes: []byte(`{"ID": "something"}`),
+			want:      []byte(`{"ID": "something"}`),
+		},
+		{
+			name:      "video: events disabled for account and request",
+			args:      args{enabledForAccount: false, enabledForRequest: false, bidType: openrtb_ext.BidTypeVideo},
+			jsonBytes: []byte(`{"ID": "something"}`),
+			want:      []byte(`{"ID": "something"}`),
+		},
+		{
+			name:      "video: events enabled for account and request",
+			args:      args{enabledForAccount: true, enabledForRequest: true, bidType: openrtb_ext.BidTypeVideo},
+			jsonBytes: []byte(`{"ID": "something"}`),
+			want:      []byte(`{"ID": "something"}`),
+		},
+		{
+			name:      "banner: broken json expected to fail patching",
+			args:      args{enabledForAccount: true, enabledForRequest: true, bidType: openrtb_ext.BidTypeBanner},
+			jsonBytes: []byte(`broken json`),
+			want:      nil,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			evData := &eventTracking{
+				enabledForAccount:  tt.args.enabledForAccount,
+				enabledForRequest:  tt.args.enabledForRequest,
+				accountID:          "123456",
+				auctionTimestampMs: 1234567890,
+				externalURL:        "http://localhost",
+			}
+			bid := &pbsOrtbBid{bid: &openrtb.Bid{ID: "BID-1"}, bidType: tt.args.bidType}
+			modifiedJSON, err := evData.modifyBidJSON(bid, openrtb_ext.BidderOpenx, tt.jsonBytes)
+			if tt.want != nil {
+				assert.NoError(t, err, "Unexpected error")
+				assert.JSONEq(t, string(tt.want), string(modifiedJSON))
+			} else {
+				assert.Error(t, err)
+				assert.Equal(t, string(tt.jsonBytes), string(modifiedJSON), "Expected original json on failure to modify")
+			}
+		})
+	}
+}
diff --git a/exchange/eventscachetest/json-account-off-request-off.json b/exchange/eventscachetest/json-account-off-request-off.json
new file mode 100644
index 00000000..9d7af624
--- /dev/null
+++ b/exchange/eventscachetest/json-account-off-request-off.json
@@ -0,0 +1,49 @@
+{
+    "bidRequest": {
+        "imp": [{
+            "id":  "oneImp"
+        }]
+    },
+    "pbsBids": [{
+        "bid":{
+            "id": "bidOne",
+            "impid": "oneImp",
+            "price": 7.64,
+            "exp":   600
+        },
+        "bidType": "banner",
+        "bidder": "appnexus"
+    }, {
+        "bid": {
+            "id": "bidTwo",
+            "impid": "oneImp",
+            "price": 5.64
+        },
+        "bidType": "banner",
+        "bidder": "pubmatic"
+    }],
+    "expectedCacheables": [
+        {
+            "type": "json",
+            "ttlseconds": 660,
+            "value":{ "id": "bidOne", "impid": "oneImp", "price": 7.64, "exp": 600}
+        }, {
+            "type": "json",
+            "ttlseconds": 360,
+            "value": { "id": "bidTwo", "impid": "oneImp", "price": 5.64}
+        }
+    ],
+    "defaultTTLs": {
+        "banner": 300,
+        "video": 3600,
+        "audio": 1800,
+        "native": 300
+    },
+    "targetDataIncludeWinners":true,
+    "targetDataIncludeBidderKeys":true,
+    "targetDataIncludeCacheBids":true,
+    "targetDataIncludeCacheVast":false,
+    "eventsDataEnabledForAccount": false,
+    "eventsDataEnabledForRequest": false
+}
+
diff --git a/exchange/eventscachetest/json-account-off-request-on.json b/exchange/eventscachetest/json-account-off-request-on.json
new file mode 100644
index 00000000..3c69c499
--- /dev/null
+++ b/exchange/eventscachetest/json-account-off-request-on.json
@@ -0,0 +1,49 @@
+{
+    "bidRequest": {
+        "imp": [{
+            "id":  "oneImp"
+        }]
+    },
+    "pbsBids": [{
+        "bid":{
+            "id": "bidOne",
+            "impid": "oneImp",
+            "price": 7.64,
+            "exp":   600
+        },
+        "bidType": "banner",
+        "bidder": "appnexus"
+    }, {
+        "bid": {
+            "id": "bidTwo",
+            "impid": "oneImp",
+            "price": 5.64
+        },
+        "bidType": "banner",
+        "bidder": "pubmatic"
+    }],
+    "expectedCacheables": [
+        {
+            "type": "json",
+            "ttlseconds": 660,
+            "value":{ "id": "bidOne", "impid": "oneImp", "price": 7.64, "exp": 600, "wurl": "http://localhost/event?t=win&b=bidOne&a=TEST_ACC_ID&bidder=appnexus&ts=1234567890"}
+        }, {
+            "type": "json",
+            "ttlseconds": 360,
+            "value": { "id": "bidTwo", "impid": "oneImp", "price": 5.64, "wurl": "http://localhost/event?t=win&b=bidTwo&a=TEST_ACC_ID&bidder=pubmatic&ts=1234567890"}
+        }
+    ],
+    "defaultTTLs": {
+        "banner": 300,
+        "video": 3600,
+        "audio": 1800,
+        "native": 300
+    },
+    "targetDataIncludeWinners":true,
+    "targetDataIncludeBidderKeys":true,
+    "targetDataIncludeCacheBids":true,
+    "targetDataIncludeCacheVast":false,
+    "eventsDataEnabledForAccount": false,
+    "eventsDataEnabledForRequest": true
+}
+
diff --git a/exchange/eventscachetest/json-account-on-request-off.json b/exchange/eventscachetest/json-account-on-request-off.json
new file mode 100644
index 00000000..e193c0b8
--- /dev/null
+++ b/exchange/eventscachetest/json-account-on-request-off.json
@@ -0,0 +1,49 @@
+{
+    "bidRequest": {
+        "imp": [{
+            "id":  "oneImp"
+        }]
+    },
+    "pbsBids": [{
+        "bid":{
+            "id": "bidOne",
+            "impid": "oneImp",
+            "price": 7.64,
+            "exp":   600
+        },
+        "bidType": "banner",
+        "bidder": "appnexus"
+    }, {
+        "bid": {
+            "id": "bidTwo",
+            "impid": "oneImp",
+            "price": 5.64
+        },
+        "bidType": "banner",
+        "bidder": "pubmatic"
+    }],
+    "expectedCacheables": [
+        {
+            "type": "json",
+            "ttlseconds": 660,
+            "value":{ "id": "bidOne", "impid": "oneImp", "price": 7.64, "exp": 600, "wurl": "http://localhost/event?t=win&b=bidOne&a=TEST_ACC_ID&bidder=appnexus&ts=1234567890"}
+        }, {
+            "type": "json",
+            "ttlseconds": 360,
+            "value": { "id": "bidTwo", "impid": "oneImp", "price": 5.64, "wurl": "http://localhost/event?t=win&b=bidTwo&a=TEST_ACC_ID&bidder=pubmatic&ts=1234567890"}
+        }
+    ],
+    "defaultTTLs": {
+        "banner": 300,
+        "video": 3600,
+        "audio": 1800,
+        "native": 300
+    },
+    "targetDataIncludeWinners":true,
+    "targetDataIncludeBidderKeys":true,
+    "targetDataIncludeCacheBids":true,
+    "targetDataIncludeCacheVast":false,
+    "eventsDataEnabledForAccount": true,
+    "eventsDataEnabledForRequest": false
+}
+
diff --git a/exchange/eventscachetest/json-account-on-request-on.json b/exchange/eventscachetest/json-account-on-request-on.json
new file mode 100644
index 00000000..807c517b
--- /dev/null
+++ b/exchange/eventscachetest/json-account-on-request-on.json
@@ -0,0 +1,49 @@
+{
+    "bidRequest": {
+        "imp": [{
+            "id":  "oneImp"
+        }]
+    },
+    "pbsBids": [{
+        "bid":{
+            "id": "bidOne",
+            "impid": "oneImp",
+            "price": 7.64,
+            "exp":   600
+        },
+        "bidType": "banner",
+        "bidder": "appnexus"
+    }, {
+        "bid": {
+            "id": "bidTwo",
+            "impid": "oneImp",
+            "price": 5.64
+        },
+        "bidType": "banner",
+        "bidder": "pubmatic"
+    }],
+    "expectedCacheables": [
+        {
+            "type": "json",
+            "ttlseconds": 660,
+            "value": { "id": "bidOne", "impid": "oneImp", "price": 7.64, "exp": 600, "wurl": "http://localhost/event?t=win&b=bidOne&a=TEST_ACC_ID&bidder=appnexus&ts=1234567890"}
+        }, {
+            "type": "json",
+            "ttlseconds": 360,
+            "value": { "id": "bidTwo", "impid": "oneImp", "price": 5.64, "wurl": "http://localhost/event?t=win&b=bidTwo&a=TEST_ACC_ID&bidder=pubmatic&ts=1234567890"}
+        }
+    ],
+    "defaultTTLs": {
+        "banner": 300,
+        "video": 3600,
+        "audio": 1800,
+        "native": 300
+    },
+    "targetDataIncludeWinners":true,
+    "targetDataIncludeBidderKeys":true,
+    "targetDataIncludeCacheBids":true,
+    "targetDataIncludeCacheVast":false,
+    "eventsDataEnabledForAccount": true,
+    "eventsDataEnabledForRequest": true
+}
+
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index c013f587..416875a1 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -59,7 +59,7 @@ func TestNewExchange(t *testing.T) {
 	}
 
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	e := NewExchange(adapters, nil, cfg, &metricsConf.DummyMetricsEngine{}, gdpr.AlwaysAllow{}, currencyConverter, nilCategoryFetcher{}).(*exchange)
+	e := NewExchange(adapters, nil, cfg, &metricsConf.DummyMetricsEngine{}, biddersInfo, gdpr.AlwaysAllow{}, currencyConverter, nilCategoryFetcher{}).(*exchange)
 	for _, bidderName := range knownAdapters {
 		if _, ok := e.adapterMap[bidderName]; !ok {
 			t.Errorf("NewExchange produced an Exchange without bidder %s", bidderName)
@@ -103,7 +103,7 @@ func TestCharacterEscape(t *testing.T) {
 	}
 
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	e := NewExchange(adapters, nil, cfg, &metricsConf.DummyMetricsEngine{}, gdpr.AlwaysAllow{}, currencyConverter, nilCategoryFetcher{}).(*exchange)
+	e := NewExchange(adapters, nil, cfg, &metricsConf.DummyMetricsEngine{}, biddersInfo, gdpr.AlwaysAllow{}, currencyConverter, nilCategoryFetcher{}).(*exchange)
 
 	/* 	3) Build all the parameters e.buildBidResponse(ctx.Background(), liveA... ) needs */
 	//liveAdapters []openrtb_ext.BidderName,
@@ -529,7 +529,7 @@ func TestGetBidCacheInfoEndToEnd(t *testing.T) {
 	}
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	pbc := pbc.NewClient(&http.Client{}, &cfg.CacheURL, &cfg.ExtCacheURL, testEngine)
-	e := NewExchange(adapters, pbc, cfg, &metricsConf.DummyMetricsEngine{}, gdpr.AlwaysAllow{}, currencyConverter, nilCategoryFetcher{}).(*exchange)
+	e := NewExchange(adapters, pbc, cfg, &metricsConf.DummyMetricsEngine{}, biddersInfo, gdpr.AlwaysAllow{}, currencyConverter, nilCategoryFetcher{}).(*exchange)
 	/* 	3) Build all the parameters e.buildBidResponse(ctx.Background(), liveA... ) needs */
 	liveAdapters := []openrtb_ext.BidderName{bidderName}
 
@@ -878,7 +878,7 @@ func TestBidResponseCurrency(t *testing.T) {
 	}
 
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	e := NewExchange(adapters, nil, cfg, &metricsConf.DummyMetricsEngine{}, gdpr.AlwaysAllow{}, currencyConverter, nilCategoryFetcher{}).(*exchange)
+	e := NewExchange(adapters, nil, cfg, &metricsConf.DummyMetricsEngine{}, biddersInfo, gdpr.AlwaysAllow{}, currencyConverter, nilCategoryFetcher{}).(*exchange)
 
 	liveAdapters := make([]openrtb_ext.BidderName, 1)
 	liveAdapters[0] = "appnexus"
@@ -1059,7 +1059,7 @@ func TestRaceIntegration(t *testing.T) {
 		UserSyncs:  &emptyUsersync{},
 	}
 
-	ex := NewExchange(adapters, &wellBehavedCache{}, cfg, &metricsConf.DummyMetricsEngine{}, gdpr.AlwaysAllow{}, currencyConverter, &nilCategoryFetcher{}).(*exchange)
+	ex := NewExchange(adapters, &wellBehavedCache{}, cfg, &metricsConf.DummyMetricsEngine{}, biddersInfo, gdpr.AlwaysAllow{}, currencyConverter, &nilCategoryFetcher{}).(*exchange)
 	_, err := ex.HoldAuction(context.Background(), auctionRequest, nil)
 	if err != nil {
 		t.Errorf("HoldAuction returned unexpected error: %v", err)
@@ -1149,7 +1149,7 @@ func TestPanicRecovery(t *testing.T) {
 	}
 
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	e := NewExchange(adapters, nil, cfg, &metricsConf.DummyMetricsEngine{}, gdpr.AlwaysAllow{}, currencyConverter, nilCategoryFetcher{}).(*exchange)
+	e := NewExchange(adapters, nil, cfg, &metricsConf.DummyMetricsEngine{}, biddersInfo, gdpr.AlwaysAllow{}, currencyConverter, nilCategoryFetcher{}).(*exchange)
 
 	chBids := make(chan *bidResponseWrapper, 1)
 	panicker := func(bidderRequest BidderRequest, conversions currency.Conversions) {
@@ -1241,7 +1241,7 @@ func TestPanicRecoveryHighLevel(t *testing.T) {
 		t.Errorf("Failed to create a category Fetcher: %v", error)
 	}
 
-	e := NewExchange(adapters, &mockCache{}, cfg, &metricsConf.DummyMetricsEngine{}, gdpr.AlwaysAllow{}, currencyConverter, categoriesFetcher).(*exchange)
+	e := NewExchange(adapters, &mockCache{}, cfg, &metricsConf.DummyMetricsEngine{}, biddersInfo, gdpr.AlwaysAllow{}, currencyConverter, categoriesFetcher).(*exchange)
 
 	e.adapterMap[openrtb_ext.BidderBeachfront] = panicingAdapter{}
 	e.adapterMap[openrtb_ext.BidderAppnexus] = panicingAdapter{}
@@ -1342,12 +1342,12 @@ func TestExchangeJSON(t *testing.T) {
 		for _, specFile := range specFiles {
 			fileName := "./exchangetest/" + specFile.Name()
 			fileDisplayName := "exchange/exchangetest/" + specFile.Name()
-			specData, err := loadFile(fileName)
-			if err != nil {
-				t.Fatalf("Failed to load contents of file %s: %v", fileDisplayName, err)
-			}
-
-			runSpec(t, fileDisplayName, specData)
+			t.Run(fileDisplayName, func(t *testing.T) {
+				specData, err := loadFile(fileName)
+				if assert.NoError(t, err, "Failed to load contents of file %s: %v", fileDisplayName, err) {
+					runSpec(t, fileDisplayName, specData)
+				}
+			})
 		}
 	}
 }
@@ -1403,8 +1403,14 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 
 	auctionRequest := AuctionRequest{
 		BidRequest: &spec.IncomingRequest.OrtbRequest,
-		Account:    config.Account{},
-		UserSyncs:  mockIdFetcher(spec.IncomingRequest.Usersyncs),
+		Account: config.Account{
+			ID:            "testaccount",
+			EventsEnabled: spec.EventsEnabled,
+		},
+		UserSyncs: mockIdFetcher(spec.IncomingRequest.Usersyncs),
+	}
+	if spec.StartTime > 0 {
+		auctionRequest.StartTime = time.Unix(0, spec.StartTime*1e+6)
 	}
 
 	bid, err := ex.HoldAuction(context.Background(), auctionRequest, debugLog)
@@ -1482,30 +1488,33 @@ func extractResponseTimes(t *testing.T, context string, bid *openrtb.BidResponse
 }
 
 func newExchangeForTests(t *testing.T, filename string, expectations map[string]*bidderSpec, aliases map[string]string, privacyConfig config.Privacy) Exchange {
-	adapters := make(map[openrtb_ext.BidderName]adaptedBidder)
+	bidderAdapters := make(map[openrtb_ext.BidderName]adaptedBidder, len(expectations))
+	bidderInfos := make(adapters.BidderInfos, len(expectations))
 	for _, bidderName := range openrtb_ext.CoreBidderNames() {
 		if spec, ok := expectations[string(bidderName)]; ok {
-			adapters[bidderName] = &validatingBidder{
+			bidderAdapters[bidderName] = &validatingBidder{
 				t:             t,
 				fileName:      filename,
 				bidderName:    string(bidderName),
 				expectations:  map[string]*bidderRequest{string(bidderName): spec.ExpectedRequest},
 				mockResponses: map[string]bidderResponse{string(bidderName): spec.MockResponse},
 			}
+			bidderInfos[string(bidderName)] = adapters.BidderInfo{ModifyingVastXmlAllowed: spec.ModifyingVastXmlAllowed}
 		}
 	}
+
 	for alias, coreBidder := range aliases {
 		if spec, ok := expectations[alias]; ok {
-			if bidder, ok := adapters[openrtb_ext.BidderName(coreBidder)]; ok {
+			if bidder, ok := bidderAdapters[openrtb_ext.BidderName(coreBidder)]; ok {
 				bidder.(*validatingBidder).expectations[alias] = spec.ExpectedRequest
 				bidder.(*validatingBidder).mockResponses[alias] = spec.MockResponse
 			} else {
-				adapters[openrtb_ext.BidderName(coreBidder)] = &validatingBidder{
+				bidderAdapters[openrtb_ext.BidderName(coreBidder)] = &validatingBidder{
 					t:             t,
 					fileName:      filename,
 					bidderName:    coreBidder,
-					expectations:  map[string]*bidderRequest{coreBidder: spec.ExpectedRequest},
-					mockResponses: map[string]bidderResponse{coreBidder: spec.MockResponse},
+					expectations:  map[string]*bidderRequest{alias: spec.ExpectedRequest},
+					mockResponses: map[string]bidderResponse{alias: spec.MockResponse},
 				}
 			}
 		}
@@ -1517,7 +1526,7 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 	}
 
 	return &exchange{
-		adapterMap:          adapters,
+		adapterMap:          bidderAdapters,
 		me:                  metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.CoreBidderNames()),
 		cache:               &wellBehavedCache{},
 		cacheTime:           0,
@@ -1526,6 +1535,8 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 		UsersyncIfAmbiguous: privacyConfig.GDPR.UsersyncIfAmbiguous,
 		privacyConfig:       privacyConfig,
 		categoriesFetcher:   categoriesFetcher,
+		bidderInfo:          bidderInfos,
+		externalURL:         "http://localhost",
 	}
 }
 
@@ -1611,10 +1622,10 @@ func TestCategoryMapping(t *testing.T) {
 	bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1}
 	bid4 := openrtb.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 40.0000, Cat: cats4, W: 1, H: 1}
 
-	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, 0, false}
-	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, 0, false}
-	bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
+	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, 0, false}
+	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, nil, 0, false}
+	bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
 
 	innerBids := []*pbsOrtbBid{
 		&bid1_1,
@@ -1666,10 +1677,10 @@ func TestCategoryMappingNoIncludeBrandCategory(t *testing.T) {
 	bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1}
 	bid4 := openrtb.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 40.0000, Cat: cats4, W: 1, H: 1}
 
-	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, 0, false}
-	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, 0, false}
-	bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, 0, false}
+	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, 0, false}
+	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, nil, 0, false}
+	bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, nil, 0, false}
 
 	innerBids := []*pbsOrtbBid{
 		&bid1_1,
@@ -1720,9 +1731,9 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) {
 	bid2 := openrtb.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 20.0000, Cat: cats2, W: 1, H: 1}
 	bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1}
 
-	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, 0, false}
-	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
+	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, 0, false}
+	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
 
 	innerBids := []*pbsOrtbBid{
 		&bid1_1,
@@ -1802,9 +1813,9 @@ func TestCategoryMappingTranslateCategoriesFalse(t *testing.T) {
 	bid2 := openrtb.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 20.0000, Cat: cats2, W: 1, H: 1}
 	bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1}
 
-	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, 0, false}
-	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
+	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, 0, false}
+	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
 
 	innerBids := []*pbsOrtbBid{
 		&bid1_1,
@@ -1854,11 +1865,11 @@ func TestCategoryDedupe(t *testing.T) {
 	bid4 := openrtb.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 20.0000, Cat: cats4, W: 1, H: 1}
 	bid5 := openrtb.Bid{ID: "bid_id5", ImpID: "imp_id5", Price: 20.0000, Cat: cats1, W: 1, H: 1}
 
-	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, 0, false}
-	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_5 := pbsOrtbBid{&bid5, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
+	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, nil, 0, false}
+	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_5 := pbsOrtbBid{&bid5, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
 
 	selectedBids := make(map[string]int)
 	expectedCategories := map[string]string{
@@ -1933,11 +1944,11 @@ func TestNoCategoryDedupe(t *testing.T) {
 	bid4 := openrtb.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 20.0000, Cat: cats4, W: 1, H: 1}
 	bid5 := openrtb.Bid{ID: "bid_id5", ImpID: "imp_id5", Price: 10.0000, Cat: cats1, W: 1, H: 1}
 
-	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_5 := pbsOrtbBid{&bid5, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
+	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_5 := pbsOrtbBid{&bid5, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
 
 	selectedBids := make(map[string]int)
 	expectedCategories := map[string]string{
@@ -2013,8 +2024,8 @@ func TestCategoryMappingBidderName(t *testing.T) {
 	bid1 := openrtb.Bid{ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: cats1, W: 1, H: 1}
 	bid2 := openrtb.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 10.0000, Cat: cats2, W: 1, H: 1}
 
-	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
+	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
 
 	innerBids1 := []*pbsOrtbBid{
 		&bid1_1,
@@ -2067,8 +2078,8 @@ func TestCategoryMappingBidderNameNoCategories(t *testing.T) {
 	bid1 := openrtb.Bid{ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: cats1, W: 1, H: 1}
 	bid2 := openrtb.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 12.0000, Cat: cats2, W: 1, H: 1}
 
-	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
+	bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
 
 	innerBids1 := []*pbsOrtbBid{
 		&bid1_1,
@@ -2179,7 +2190,7 @@ func TestBidRejectionErrors(t *testing.T) {
 		innerBids := []*pbsOrtbBid{}
 		for _, bid := range test.bids {
 			currentBid := pbsOrtbBid{
-				bid, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: test.duration}, 0, false,
+				bid, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: test.duration}, nil, 0, false,
 			}
 			innerBids = append(innerBids, &currentBid)
 		}
@@ -2228,8 +2239,8 @@ func TestCategoryMappingTwoBiddersOneBidEachNoCategorySamePrice(t *testing.T) {
 	bidApn1 := openrtb.Bid{ID: "bid_idApn1", ImpID: "imp_idApn1", Price: 10.0000, Cat: cats1, W: 1, H: 1}
 	bidApn2 := openrtb.Bid{ID: "bid_idApn2", ImpID: "imp_idApn2", Price: 10.0000, Cat: cats2, W: 1, H: 1}
 
-	bid1_Apn1 := pbsOrtbBid{&bidApn1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
-	bid1_Apn2 := pbsOrtbBid{&bidApn2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0, false}
+	bid1_Apn1 := pbsOrtbBid{&bidApn1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
+	bid1_Apn2 := pbsOrtbBid{&bidApn2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, 0, false}
 
 	innerBidsApn1 := []*pbsOrtbBid{
 		&bid1_Apn1,
@@ -2356,7 +2367,7 @@ func TestApplyDealSupport(t *testing.T) {
 			},
 		}
 
-		bid := pbsOrtbBid{&openrtb.Bid{ID: "123456"}, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, test.dealPriority, false}
+		bid := pbsOrtbBid{&openrtb.Bid{ID: "123456"}, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, test.dealPriority, false}
 		bidCategory := map[string]string{
 			bid.bid.ID: test.targ["hb_pb_cat_dur"],
 		}
@@ -2523,7 +2534,7 @@ func TestUpdateHbPbCatDur(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		bid := pbsOrtbBid{&openrtb.Bid{ID: "123456"}, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, test.dealPriority, false}
+		bid := pbsOrtbBid{&openrtb.Bid{ID: "123456"}, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, test.dealPriority, false}
 		bidCategory := map[string]string{
 			bid.bid.ID: test.targ["hb_pb_cat_dur"],
 		}
@@ -2544,6 +2555,8 @@ type exchangeSpec struct {
 	EnforceLMT        bool                   `json:"enforceLmt"`
 	AssumeGDPRApplies bool                   `json:"assume_gdpr_applies"`
 	DebugLog          *DebugLog              `json:"debuglog,omitempty"`
+	EventsEnabled     bool                   `json:"events_enabled,omitempty"`
+	StartTime         int64                  `json:"start_time_ms,omitempty"`
 }
 
 type exchangeRequest struct {
@@ -2558,8 +2571,9 @@ type exchangeResponse struct {
 }
 
 type bidderSpec struct {
-	ExpectedRequest *bidderRequest `json:"expectRequest"`
-	MockResponse    bidderResponse `json:"mockResponse"`
+	ExpectedRequest         *bidderRequest `json:"expectRequest"`
+	MockResponse            bidderResponse `json:"mockResponse"`
+	ModifyingVastXmlAllowed bool           `json:"modifyingVastXmlAllowed,omitempty"`
 }
 
 type bidderRequest struct {
diff --git a/exchange/exchangetest/eidpermissions-allowed-alias.json b/exchange/exchangetest/eidpermissions-allowed-alias.json
new file mode 100644
index 00000000..24c9af7c
--- /dev/null
+++ b/exchange/exchangetest/eidpermissions-allowed-alias.json
@@ -0,0 +1,123 @@
+{
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "user": {
+                "ext": {
+                    "eids": [{
+                        "source": "source1",
+                        "id": "anyId"
+                    }]
+                }
+            },
+            "ext": {
+                "prebid": {
+                    "aliases": {
+                        "foo": "appnexus"
+                    },
+                    "data": {
+                        "eidpermissions": [{
+                            "source": "source1",
+                            "bidders": ["foo"]
+                        }]
+                    }
+                }
+            },
+            "imp": [{
+                "id": "my-imp-id",
+                "video": {
+                    "mimes": ["video/mp4"]
+                },
+                "ext": {
+                    "foo": {
+                        "placementId": 1
+                    }
+                }
+            }]
+        }
+    },
+    "outgoingRequests": {
+        "foo": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "user": {
+                        "ext": {
+                            "eids": [{
+                                "source": "source1",
+                                "id": "anyId"
+                            }]
+                        }
+                    },
+                    "ext": {
+                        "prebid": {
+                            "aliases": {
+                                "foo": "appnexus"
+                            },
+                            "data": {
+                                "eidpermissions": [{
+                                    "source": "source1",
+                                    "bidders": ["foo"]
+                                }]
+                            }
+                        }
+                    },
+                    "imp": [{
+                        "id": "my-imp-id",
+                        "video": {
+                            "mimes": ["video/mp4"]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": 1
+                            }
+                        }
+                    }]
+                },
+                "bidAdjustment": 1.0
+            },
+            "mockResponse": {
+                "pbsSeatBid": {
+                    "pbsBids": [{
+                        "ortbBid": {
+                            "id": "apn-bid",
+                            "impid": "my-imp-id",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 250,
+                            "crid": "creative-1"
+                        },
+                        "bidType": "video"
+                    }]
+                }
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [{
+                "seat": "foo",
+                "bid": [{
+                    "id": "apn-bid",
+                    "impid": "my-imp-id",
+                    "price": 0.3,
+                    "w": 200,
+                    "h": 250,
+                    "crid": "creative-1",
+                    "ext": {
+                        "prebid": {
+                            "type": "video"
+                        }
+                    }
+                }]
+            }]
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/eidpermissions-allowed.json b/exchange/exchangetest/eidpermissions-allowed.json
new file mode 100644
index 00000000..851c98dd
--- /dev/null
+++ b/exchange/exchangetest/eidpermissions-allowed.json
@@ -0,0 +1,117 @@
+{
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "user": {
+                "ext": {
+                    "eids": [{
+                        "source": "source1",
+                        "id": "anyId"
+                    }]
+                }
+            },
+            "ext": {
+                "prebid": {
+                    "data": {
+                        "eidpermissions": [{
+                            "source": "source1",
+                            "bidders": ["appnexus"]
+                        }]
+                    }
+                }
+            },
+            "imp": [{
+                "id": "my-imp-id",
+                "video": {
+                    "mimes": ["video/mp4"]
+                },
+                "ext": {
+                    "appnexus": {
+                        "placementId": 1
+                    }
+                }
+            }]
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "user": {
+                        "ext": {
+                            "eids": [{
+                                "source": "source1",
+                                "id": "anyId"
+                            }]
+                        }
+                    },
+                    "ext": {
+                        "prebid": {
+                            "data": {
+                                "eidpermissions": [{
+                                    "source": "source1",
+                                    "bidders": ["appnexus"]
+                                }]
+                            }
+                        }
+                    },
+                    "imp": [{
+                        "id": "my-imp-id",
+                        "video": {
+                            "mimes": ["video/mp4"]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": 1
+                            }
+                        }
+                    }]
+                },
+                "bidAdjustment": 1.0
+            },
+            "mockResponse": {
+                "pbsSeatBid": {
+                    "pbsBids": [{
+                        "ortbBid": {
+                            "id": "apn-bid",
+                            "impid": "my-imp-id",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 250,
+                            "crid": "creative-1"
+                        },
+                        "bidType": "video"
+                    }]
+                }
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [{
+                "seat": "appnexus",
+                "bid": [{
+                    "id": "apn-bid",
+                    "impid": "my-imp-id",
+                    "price": 0.3,
+                    "w": 200,
+                    "h": 250,
+                    "crid": "creative-1",
+                    "ext": {
+                        "prebid": {
+                            "type": "video"
+                        }
+                    }
+                }]
+            }]
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/eidpermissions-denied.json b/exchange/exchangetest/eidpermissions-denied.json
new file mode 100644
index 00000000..38127191
--- /dev/null
+++ b/exchange/exchangetest/eidpermissions-denied.json
@@ -0,0 +1,111 @@
+{
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "user": {
+                "ext": {
+                    "eids": [{
+                        "source": "source1",
+                        "id": "anyId"
+                    }]
+                }
+            },
+            "ext": {
+                "prebid": {
+                    "data": {
+                        "eidpermissions": [{
+                            "source": "source1",
+                            "bidders": ["otherBidder"]
+                        }]
+                    }
+                }
+            },
+            "imp": [{
+                "id": "my-imp-id",
+                "video": {
+                    "mimes": ["video/mp4"]
+                },
+                "ext": {
+                    "appnexus": {
+                        "placementId": 1
+                    }
+                }
+            }]
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "user": {
+                    },
+                    "ext": {
+                        "prebid": {
+                            "data": {
+                                "eidpermissions": [{
+                                    "source": "source1",
+                                    "bidders": ["otherBidder"]
+                                }]
+                            }
+                        }
+                    },
+                    "imp": [{
+                        "id": "my-imp-id",
+                        "video": {
+                            "mimes": ["video/mp4"]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": 1
+                            }
+                        }
+                    }]
+                },
+                "bidAdjustment": 1.0
+            },
+            "mockResponse": {
+                "pbsSeatBid": {
+                    "pbsBids": [{
+                        "ortbBid": {
+                            "id": "apn-bid",
+                            "impid": "my-imp-id",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 250,
+                            "crid": "creative-1"
+                        },
+                        "bidType": "video"
+                    }]
+                }
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [{
+                "seat": "appnexus",
+                "bid": [{
+                    "id": "apn-bid",
+                    "impid": "my-imp-id",
+                    "price": 0.3,
+                    "w": 200,
+                    "h": 250,
+                    "crid": "creative-1",
+                    "ext": {
+                        "prebid": {
+                            "type": "video"
+                        }
+                    }
+                }]
+            }]
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/events-bid-account-off-request-off.json b/exchange/exchangetest/events-bid-account-off-request-off.json
new file mode 100644
index 00000000..9ce23f5e
--- /dev/null
+++ b/exchange/exchangetest/events-bid-account-off-request-off.json
@@ -0,0 +1,97 @@
+{
+  "events_enabled": false,
+  "start_time_ms": 1234567890,
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "site": {
+        "id": "some-site"
+      },
+      "imp": [
+        {
+          "id": "my-imp-id",
+          "banner": {},
+          "ext": {
+            "appnexus": {
+              "placementId": 1
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+        }
+      }
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "mockResponse": {
+        "pbsSeatBid": {
+          "pbsBids": [
+            {
+              "ortbBid": {
+                "id": "winning-bid",
+                "impid": "my-imp-id",
+                "price": 0.71,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-1"
+              },
+              "bidType": "banner"
+            },
+            {
+              "ortbBid": {
+                "id": "losing-bid",
+                "impid": "my-imp-id",
+                "price": 0.21,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-2"
+              },
+              "bidType": "banner"
+            }
+          ]
+        }
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "winning-bid",
+              "impid": "my-imp-id",
+              "price": 0.71,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "ext": {
+                "prebid": {
+                  "type": "banner"
+                }
+              }
+            },
+            {
+              "id": "losing-bid",
+              "impid": "my-imp-id",
+              "price": 0.21,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-2",
+              "ext": {
+                "prebid": {
+                  "type": "banner"
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  }
+}
diff --git a/exchange/exchangetest/events-bid-account-off-request-on.json b/exchange/exchangetest/events-bid-account-off-request-on.json
new file mode 100644
index 00000000..b88ec67c
--- /dev/null
+++ b/exchange/exchangetest/events-bid-account-off-request-on.json
@@ -0,0 +1,106 @@
+{
+  "events_enabled": false,
+  "start_time_ms": 1234567890,
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "site": {
+        "id": "some-site"
+      },
+      "imp": [
+        {
+          "id": "my-imp-id",
+          "banner": {},
+          "ext": {
+            "appnexus": {
+              "placementId": 1
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+          "events": {}
+        }
+      }
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "mockResponse": {
+        "pbsSeatBid": {
+          "pbsBids": [
+            {
+              "ortbBid": {
+                "id": "winning-bid",
+                "impid": "my-imp-id",
+                "price": 0.71,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-1"
+              },
+              "bidType": "banner"
+            },
+            {
+              "ortbBid": {
+                "id": "losing-bid",
+                "impid": "my-imp-id",
+                "price": 0.21,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-2"
+              },
+              "bidType": "banner"
+            }
+          ]
+        }
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "winning-bid",
+              "impid": "my-imp-id",
+              "price": 0.71,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "ext": {
+                "prebid": {
+                  "type": "banner",
+                  "events": {
+                    "imp": "http://localhost/event?t=imp&b=winning-bid&a=testaccount&bidder=appnexus&ts=1234567890",
+                    "win": "http://localhost/event?t=win&b=winning-bid&a=testaccount&bidder=appnexus&ts=1234567890"
+                  }
+                }
+              }
+            },
+            {
+              "id": "losing-bid",
+              "impid": "my-imp-id",
+              "price": 0.21,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-2",
+              "ext": {
+                "prebid": {
+                  "type": "banner",
+                  "events": {
+                    "imp": "http://localhost/event?t=imp&b=losing-bid&a=testaccount&bidder=appnexus&ts=1234567890",
+                    "win": "http://localhost/event?t=win&b=losing-bid&a=testaccount&bidder=appnexus&ts=1234567890"
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  }
+}
diff --git a/exchange/exchangetest/events-bid-account-on-request-off.json b/exchange/exchangetest/events-bid-account-on-request-off.json
new file mode 100644
index 00000000..e440b030
--- /dev/null
+++ b/exchange/exchangetest/events-bid-account-on-request-off.json
@@ -0,0 +1,104 @@
+{
+  "events_enabled": true,
+  "start_time_ms": 1234567890,
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "site": {
+        "id": "some-site"
+      },
+      "imp": [
+        {
+          "id": "my-imp-id",
+          "banner": {},
+          "ext": {
+            "appnexus": {
+              "placementId": 1
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {}
+      }
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "mockResponse": {
+        "pbsSeatBid": {
+          "pbsBids": [
+            {
+              "ortbBid": {
+                "id": "winning-bid",
+                "impid": "my-imp-id",
+                "price": 0.71,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-1"
+              },
+              "bidType": "banner"
+            },
+            {
+              "ortbBid": {
+                "id": "losing-bid",
+                "impid": "my-imp-id",
+                "price": 0.21,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-2"
+              },
+              "bidType": "banner"
+            }
+          ]
+        }
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "winning-bid",
+              "impid": "my-imp-id",
+              "price": 0.71,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "ext": {
+                "prebid": {
+                  "type": "banner",
+                  "events": {
+                    "imp": "http://localhost/event?t=imp&b=winning-bid&a=testaccount&bidder=appnexus&ts=1234567890",
+                    "win": "http://localhost/event?t=win&b=winning-bid&a=testaccount&bidder=appnexus&ts=1234567890"
+                  }
+                }
+              }
+            },
+            {
+              "id": "losing-bid",
+              "impid": "my-imp-id",
+              "price": 0.21,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-2",
+              "ext": {
+                "prebid": {
+                  "type": "banner",
+                  "events": {
+                    "imp": "http://localhost/event?t=imp&b=losing-bid&a=testaccount&bidder=appnexus&ts=1234567890",
+                    "win": "http://localhost/event?t=win&b=losing-bid&a=testaccount&bidder=appnexus&ts=1234567890"
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  }
+}
diff --git a/exchange/exchangetest/events-vast-account-off-request-off.json b/exchange/exchangetest/events-vast-account-off-request-off.json
new file mode 100644
index 00000000..f322e7c5
--- /dev/null
+++ b/exchange/exchangetest/events-vast-account-off-request-off.json
@@ -0,0 +1,157 @@
+{
+  "events_enabled": false,
+  "start_time_ms": 1234567890,
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "app": {
+        "id": "some-app"
+      },
+      "imp": [
+        {
+          "id": "my-imp-id",
+          "video": {},
+          "ext": {
+            "appnexus": {
+              "placementId": 1
+            },
+            "audienceNetwork": {
+              "placementId": 1
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+          "targeting": {
+            "includewinners": true,
+            "includebidderkeys": false
+          }
+        }
+      }
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "modifyingVastXmlAllowed": true,
+      "mockResponse": {
+        "pbsSeatBid": {
+          "pbsBids": [
+            {
+              "ortbBid": {
+                "id": "winning-bid",
+                "nurl": "http://domain.com/winning-bid",
+                "impid": "my-imp-id",
+                "price": 0.71,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-1"
+              },
+              "bidType": "video"
+            },
+            {
+              "ortbBid": {
+                "id": "losing-bid",
+                "nurl": "http://domain.com/losing-bid",
+                "impid": "my-imp-id",
+                "price": 0.21,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-2"
+              },
+              "bidType": "video"
+            }
+          ]
+        }
+      }
+    },
+    "audienceNetwork": {
+      "modifyingVastXmlAllowed": false,
+      "mockResponse": {
+        "pbsSeatBid": {
+          "pbsBids": [
+            {
+              "ortbBid": {
+                "id": "contending-bid",
+                "nurl": "http://domain.com/contending-bid",
+                "impid": "my-imp-id",
+                "price": 0.51,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-4"
+              },
+              "bidType": "video"
+            }
+          ]
+        }
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "audienceNetwork",
+          "bid": [
+            {
+              "id": "contending-bid",
+              "nurl": "http://domain.com/contending-bid",
+              "impid": "my-imp-id",
+              "price": 0.51,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-4",
+              "ext": {
+                "prebid": {
+                  "type": "video"
+                }
+              }
+            }
+          ]
+        },
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "winning-bid",
+              "nurl": "http://domain.com/winning-bid",
+              "impid": "my-imp-id",
+              "price": 0.71,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "ext": {
+                "prebid": {
+                  "type": "video",
+                  "targeting": {
+                      "hb_bidder": "appnexus",
+                      "hb_cache_host": "www.pbcserver.com",
+                      "hb_cache_path": "/pbcache/endpoint",
+                      "hb_env": "mobile-app",
+                      "hb_pb": "0.70",
+                      "hb_size": "200x250"
+                  }
+                }
+              }
+            },
+            {
+              "id": "losing-bid",
+              "nurl": "http://domain.com/losing-bid",
+              "impid": "my-imp-id",
+              "price": 0.21,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-2",
+              "ext": {
+                "prebid": {
+                  "type": "video"
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/events-vast-account-off-request-on.json b/exchange/exchangetest/events-vast-account-off-request-on.json
new file mode 100644
index 00000000..6e29ccdf
--- /dev/null
+++ b/exchange/exchangetest/events-vast-account-off-request-on.json
@@ -0,0 +1,160 @@
+{
+  "events_enabled": false,
+  "start_time_ms": 1234567890,
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "app": {
+        "id": "some-app"
+      },
+      "imp": [
+        {
+          "id": "my-imp-id",
+          "video": {},
+          "ext": {
+            "appnexus": {
+              "placementId": 1
+            },
+            "audienceNetwork": {
+              "placementId": 1
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+          "targeting": {
+            "includewinners": true,
+            "includebidderkeys": false
+          },
+          "events": {}
+        }
+      }
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "modifyingVastXmlAllowed": true,
+      "mockResponse": {
+        "pbsSeatBid": {
+          "pbsBids": [
+            {
+              "ortbBid": {
+                "id": "winning-bid",
+                "nurl": "http://domain.com/winning-bid",
+                "impid": "my-imp-id",
+                "price": 0.71,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-1"
+              },
+              "bidType": "video"
+            },
+            {
+              "ortbBid": {
+                "id": "losing-bid",
+                "nurl": "http://domain.com/losing-bid",
+                "impid": "my-imp-id",
+                "price": 0.21,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-2"
+              },
+              "bidType": "video"
+            }
+          ]
+        }
+      }
+    },
+    "audienceNetwork": {
+      "modifyingVastXmlAllowed": false,
+      "mockResponse": {
+        "pbsSeatBid": {
+          "pbsBids": [
+            {
+              "ortbBid": {
+                "id": "contending-bid",
+                "nurl": "http://domain.com/contending-bid",
+                "impid": "my-imp-id",
+                "price": 0.51,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-4"
+              },
+              "bidType": "video"
+            }
+          ]
+        }
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "audienceNetwork",
+          "bid": [
+            {
+              "id": "contending-bid",
+              "nurl": "http://domain.com/contending-bid",
+              "impid": "my-imp-id",
+              "price": 0.51,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-4",
+              "ext": {
+                "prebid": {
+                  "type": "video"
+                }
+              }
+            }
+          ]
+        },
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "winning-bid",
+              "adm": "<VAST version=\"3.0\"><Ad><Wrapper><AdSystem>prebid.org wrapper</AdSystem><VASTAdTagURI><![CDATA[http://domain.com/winning-bid]]></VASTAdTagURI><Impression><![CDATA[http://localhost/event?t=imp&b=winning-bid&a=testaccount&bidder=appnexus&f=b&ts=1234567890]]></Impression><Creatives></Creatives></Wrapper></Ad></VAST>",
+              "nurl": "http://domain.com/winning-bid",
+              "impid": "my-imp-id",
+              "price": 0.71,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "ext": {
+                "prebid": {
+                  "type": "video",
+                  "targeting": {
+                      "hb_bidder": "appnexus",
+                      "hb_cache_host": "www.pbcserver.com",
+                      "hb_cache_path": "/pbcache/endpoint",
+                      "hb_env": "mobile-app",
+                      "hb_pb": "0.70",
+                      "hb_size": "200x250"
+                  }
+                }
+              }
+            },
+            {
+              "id": "losing-bid",
+              "adm": "<VAST version=\"3.0\"><Ad><Wrapper><AdSystem>prebid.org wrapper</AdSystem><VASTAdTagURI><![CDATA[http://domain.com/losing-bid]]></VASTAdTagURI><Impression><![CDATA[http://localhost/event?t=imp&b=losing-bid&a=testaccount&bidder=appnexus&f=b&ts=1234567890]]></Impression><Creatives></Creatives></Wrapper></Ad></VAST>",
+              "nurl": "http://domain.com/losing-bid",
+              "impid": "my-imp-id",
+              "price": 0.21,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-2",
+              "ext": {
+                "prebid": {
+                  "type": "video"
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/events-vast-account-on-request-off.json b/exchange/exchangetest/events-vast-account-on-request-off.json
new file mode 100644
index 00000000..21f47356
--- /dev/null
+++ b/exchange/exchangetest/events-vast-account-on-request-off.json
@@ -0,0 +1,159 @@
+{
+  "events_enabled": true,
+  "start_time_ms": 1234567890,
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "app": {
+        "id": "some-app"
+      },
+      "imp": [
+        {
+          "id": "my-imp-id",
+          "video": {},
+          "ext": {
+            "appnexus": {
+              "placementId": 1
+            },
+            "audienceNetwork": {
+              "placementId": 1
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+          "targeting": {
+            "includewinners": true,
+            "includebidderkeys": false
+          }
+        }
+      }
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "modifyingVastXmlAllowed": true,
+      "mockResponse": {
+        "pbsSeatBid": {
+          "pbsBids": [
+            {
+              "ortbBid": {
+                "id": "winning-bid",
+                "nurl": "http://domain.com/winning-bid",
+                "impid": "my-imp-id",
+                "price": 0.71,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-1"
+              },
+              "bidType": "video"
+            },
+            {
+              "ortbBid": {
+                "id": "losing-bid",
+                "nurl": "http://domain.com/losing-bid",
+                "impid": "my-imp-id",
+                "price": 0.21,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-2"
+              },
+              "bidType": "video"
+            }
+          ]
+        }
+      }
+    },
+    "audienceNetwork": {
+      "modifyingVastXmlAllowed": false,
+      "mockResponse": {
+        "pbsSeatBid": {
+          "pbsBids": [
+            {
+              "ortbBid": {
+                "id": "contending-bid",
+                "nurl": "http://domain.com/contending-bid",
+                "impid": "my-imp-id",
+                "price": 0.51,
+                "w": 200,
+                "h": 250,
+                "crid": "creative-4"
+              },
+              "bidType": "video"
+            }
+          ]
+        }
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "audienceNetwork",
+          "bid": [
+            {
+              "id": "contending-bid",
+              "nurl": "http://domain.com/contending-bid",
+              "impid": "my-imp-id",
+              "price": 0.51,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-4",
+              "ext": {
+                "prebid": {
+                  "type": "video"
+                }
+              }
+            }
+          ]
+        },
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "winning-bid",
+              "adm": "<VAST version=\"3.0\"><Ad><Wrapper><AdSystem>prebid.org wrapper</AdSystem><VASTAdTagURI><![CDATA[http://domain.com/winning-bid]]></VASTAdTagURI><Impression><![CDATA[http://localhost/event?t=imp&b=winning-bid&a=testaccount&bidder=appnexus&f=b&ts=1234567890]]></Impression><Creatives></Creatives></Wrapper></Ad></VAST>",
+              "nurl": "http://domain.com/winning-bid",
+              "impid": "my-imp-id",
+              "price": 0.71,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "ext": {
+                "prebid": {
+                  "type": "video",
+                  "targeting": {
+                      "hb_bidder": "appnexus",
+                      "hb_cache_host": "www.pbcserver.com",
+                      "hb_cache_path": "/pbcache/endpoint",
+                      "hb_env": "mobile-app",
+                      "hb_pb": "0.70",
+                      "hb_size": "200x250"
+                  }
+                }
+              }
+            },
+            {
+              "id": "losing-bid",
+              "adm": "<VAST version=\"3.0\"><Ad><Wrapper><AdSystem>prebid.org wrapper</AdSystem><VASTAdTagURI><![CDATA[http://domain.com/losing-bid]]></VASTAdTagURI><Impression><![CDATA[http://localhost/event?t=imp&b=losing-bid&a=testaccount&bidder=appnexus&f=b&ts=1234567890]]></Impression><Creatives></Creatives></Wrapper></Ad></VAST>",
+              "nurl": "http://domain.com/losing-bid",
+              "impid": "my-imp-id",
+              "price": 0.21,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-2",
+              "ext": {
+                "prebid": {
+                  "type": "video"
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/exchange/gdpr_test.go b/exchange/gdpr_test.go
index f7906104..35193909 100644
--- a/exchange/gdpr_test.go
+++ b/exchange/gdpr_test.go
@@ -14,6 +14,7 @@ func TestExtractGDPR(t *testing.T) {
 		description string
 		giveRegs    *openrtb.Regs
 		wantGDPR    gdpr.Signal
+		wantError   bool
 	}{
 		{
 			description: "Regs Ext GDPR = 0",
@@ -44,6 +45,7 @@ func TestExtractGDPR(t *testing.T) {
 			description: "JSON unmarshal error",
 			giveRegs:    &openrtb.Regs{Ext: json.RawMessage(`{"`)},
 			wantGDPR:    gdpr.SignalAmbiguous,
+			wantError:   true,
 		},
 	}
 
@@ -52,8 +54,14 @@ func TestExtractGDPR(t *testing.T) {
 			Regs: tt.giveRegs,
 		}
 
-		result := extractGDPR(&bidReq)
+		result, err := extractGDPR(&bidReq)
 		assert.Equal(t, tt.wantGDPR, result, tt.description)
+
+		if tt.wantError {
+			assert.NotNil(t, err, tt.description)
+		} else {
+			assert.Nil(t, err, tt.description)
+		}
 	}
 }
 
@@ -62,6 +70,7 @@ func TestExtractConsent(t *testing.T) {
 		description string
 		giveUser    *openrtb.User
 		wantConsent string
+		wantError   bool
 	}{
 		{
 			description: "User Ext Consent is not empty",
@@ -87,6 +96,7 @@ func TestExtractConsent(t *testing.T) {
 			description: "JSON unmarshal error",
 			giveUser:    &openrtb.User{Ext: json.RawMessage(`{`)},
 			wantConsent: "",
+			wantError:   true,
 		},
 	}
 
@@ -95,7 +105,13 @@ func TestExtractConsent(t *testing.T) {
 			User: tt.giveUser,
 		}
 
-		result := extractConsent(&bidReq)
+		result, err := extractConsent(&bidReq)
 		assert.Equal(t, tt.wantConsent, result, tt.description)
+
+		if tt.wantError {
+			assert.NotNil(t, err, tt.description)
+		} else {
+			assert.Nil(t, err, tt.description)
+		}
 	}
 }
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index e13f956b..e679397b 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -1060,6 +1060,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 		permissionsError    error
 		userSyncIfAmbiguous bool
 		expectPrivacyLabels metrics.PrivacyLabels
+		expectError         bool
 	}{
 		{
 			description:        "Enforce - TCF Invalid",
@@ -1109,6 +1110,19 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 				GDPRTCFVersion: "",
 			},
 		},
+		{
+			description:        "Enforce - TCF 1; GDPR signal extraction error",
+			gdprAccountEnabled: &trueValue,
+			gdprHostEnabled:    true,
+			gdpr:               "0{",
+			gdprConsent:        "BONV8oqONXwgmADACHENAO7pqzAAppY",
+			gdprScrub:          true,
+			expectPrivacyLabels: metrics.PrivacyLabels{
+				GDPREnforced:   true,
+				GDPRTCFVersion: metrics.TCFVersionV1,
+			},
+			expectError: true,
+		},
 		{
 			description:        "Enforce - TCF 1; account GDPR enabled, host GDPR setting disregarded",
 			gdprAccountEnabled: &trueValue,
@@ -1236,7 +1250,12 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 			privacyConfig)
 		result := results[0]
 
-		assert.Nil(t, errs)
+		if test.expectError {
+			assert.NotNil(t, errs)
+		} else {
+			assert.Nil(t, errs)
+		}
+
 		if test.gdprScrub {
 			assert.Equal(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID")
 			assert.Equal(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5")
@@ -1450,3 +1469,254 @@ func TestBidderToPrebidChainsZeroLengthSChains(t *testing.T) {
 	assert.Nil(t, err)
 	assert.Equal(t, len(output), 0)
 }
+
+func TestRemoveUnpermissionedEids(t *testing.T) {
+	bidder := "bidderA"
+
+	testCases := []struct {
+		description     string
+		userExt         json.RawMessage
+		eidPermissions  []openrtb_ext.ExtRequestPrebidDataEidPermission
+		expectedUserExt json.RawMessage
+	}{
+		{
+			description: "Extension Nil",
+			userExt:     nil,
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"bidderA"}},
+			},
+			expectedUserExt: nil,
+		},
+		{
+			description: "Extension Empty",
+			userExt:     json.RawMessage(`{}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"bidderA"}},
+			},
+			expectedUserExt: json.RawMessage(`{}`),
+		},
+		{
+			description: "Extension Empty - Keep Other Data",
+			userExt:     json.RawMessage(`{"other":42}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"bidderA"}},
+			},
+			expectedUserExt: json.RawMessage(`{"other":42}`),
+		},
+		{
+			description: "Eids Empty",
+			userExt:     json.RawMessage(`{"eids":[]}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"bidderA"}},
+			},
+			expectedUserExt: json.RawMessage(`{"eids":[]}`),
+		},
+		{
+			description: "Eids Empty - Keep Other Data",
+			userExt:     json.RawMessage(`{"eids":[],"other":42}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"bidderA"}},
+			},
+			expectedUserExt: json.RawMessage(`{"eids":[],"other":42}`),
+		},
+		{
+			description:     "Allowed By Nil Permissions",
+			userExt:         json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`),
+			eidPermissions:  nil,
+			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`),
+		},
+		{
+			description:     "Allowed By Empty Permissions",
+			userExt:         json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`),
+			eidPermissions:  []openrtb_ext.ExtRequestPrebidDataEidPermission{},
+			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`),
+		},
+		{
+			description: "Allowed By Specific Bidder",
+			userExt:     json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"bidderA"}},
+			},
+			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`),
+		},
+		{
+			description: "Allowed By All Bidders",
+			userExt:     json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"*"}},
+			},
+			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`),
+		},
+		{
+			description: "Allowed By Lack Of Matching Source",
+			userExt:     json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source2", Bidders: []string{"otherBidder"}},
+			},
+			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`),
+		},
+		{
+			description: "Allowed - Keep Other Data",
+			userExt:     json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}],"other":42}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"bidderA"}},
+			},
+			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}],"other":42}`),
+		},
+		{
+			description: "Denied",
+			userExt:     json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"otherBidder"}},
+			},
+			expectedUserExt: nil,
+		},
+		{
+			description: "Denied - Keep Other Data",
+			userExt:     json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}],"otherdata":42}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"otherBidder"}},
+			},
+			expectedUserExt: json.RawMessage(`{"otherdata":42}`),
+		},
+		{
+			description: "Mix Of Allowed By Specific Bidder, Allowed By Lack Of Matching Source, Denied, Keep Other Data",
+			userExt:     json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"},{"source":"source2","id":"anyID"},{"source":"source3","id":"anyID"}],"other":42}`),
+			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+				{Source: "source1", Bidders: []string{"bidderA"}},
+				{Source: "source3", Bidders: []string{"otherBidder"}},
+			},
+			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"},{"source":"source2","id":"anyID"}],"other":42}`),
+		},
+	}
+
+	for _, test := range testCases {
+		request := &openrtb.BidRequest{
+			User: &openrtb.User{Ext: test.userExt},
+		}
+
+		requestExt := &openrtb_ext.ExtRequest{
+			Prebid: openrtb_ext.ExtRequestPrebid{
+				Data: &openrtb_ext.ExtRequestPrebidData{
+					EidPermissions: test.eidPermissions,
+				},
+			},
+		}
+
+		expectedRequest := &openrtb.BidRequest{
+			User: &openrtb.User{Ext: test.expectedUserExt},
+		}
+
+		resultErr := removeUnpermissionedEids(request, bidder, requestExt)
+		assert.NoError(t, resultErr, test.description)
+		assert.Equal(t, expectedRequest, request, test.description)
+	}
+}
+
+func TestRemoveUnpermissionedEidsUnmarshalErrors(t *testing.T) {
+	testCases := []struct {
+		description string
+		userExt     json.RawMessage
+		expectedErr string
+	}{
+		{
+			description: "Malformed Ext",
+			userExt:     json.RawMessage(`malformed`),
+			expectedErr: "invalid character 'm' looking for beginning of value",
+		},
+		{
+			description: "Malformed Eid Array Type",
+			userExt:     json.RawMessage(`{"eids":[42]}`),
+			expectedErr: "json: cannot unmarshal number into Go value of type openrtb_ext.ExtUserEid",
+		},
+		{
+			description: "Malformed Eid Item Type",
+			userExt:     json.RawMessage(`{"eids":[{"source":42,"id":"anyID"}]}`),
+			expectedErr: "json: cannot unmarshal number into Go struct field ExtUserEid.source of type string",
+		},
+	}
+
+	for _, test := range testCases {
+		request := &openrtb.BidRequest{
+			User: &openrtb.User{Ext: test.userExt},
+		}
+
+		requestExt := &openrtb_ext.ExtRequest{
+			Prebid: openrtb_ext.ExtRequestPrebid{
+				Data: &openrtb_ext.ExtRequestPrebidData{
+					EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+						{Source: "source1", Bidders: []string{"*"}},
+					},
+				},
+			},
+		}
+
+		resultErr := removeUnpermissionedEids(request, "bidderA", requestExt)
+		assert.EqualError(t, resultErr, test.expectedErr, test.description)
+	}
+}
+
+func TestRemoveUnpermissionedEidsEmptyValidations(t *testing.T) {
+	testCases := []struct {
+		description string
+		request     *openrtb.BidRequest
+		requestExt  *openrtb_ext.ExtRequest
+	}{
+		{
+			description: "Nil User",
+			request: &openrtb.BidRequest{
+				User: nil,
+			},
+			requestExt: &openrtb_ext.ExtRequest{
+				Prebid: openrtb_ext.ExtRequestPrebid{
+					Data: &openrtb_ext.ExtRequestPrebidData{
+						EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+							{Source: "source1", Bidders: []string{"*"}},
+						},
+					},
+				},
+			},
+		},
+		{
+			description: "Empty User",
+			request: &openrtb.BidRequest{
+				User: &openrtb.User{},
+			},
+			requestExt: &openrtb_ext.ExtRequest{
+				Prebid: openrtb_ext.ExtRequestPrebid{
+					Data: &openrtb_ext.ExtRequestPrebidData{
+						EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
+							{Source: "source1", Bidders: []string{"*"}},
+						},
+					},
+				},
+			},
+		},
+		{
+			description: "Nil Ext",
+			request: &openrtb.BidRequest{
+				User: &openrtb.User{Ext: json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`)},
+			},
+			requestExt: nil,
+		},
+		{
+			description: "Nil Prebid Data",
+			request: &openrtb.BidRequest{
+				User: &openrtb.User{Ext: json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`)},
+			},
+			requestExt: &openrtb_ext.ExtRequest{
+				Prebid: openrtb_ext.ExtRequestPrebid{
+					Data: nil,
+				},
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		requestExpected := *test.request
+
+		resultErr := removeUnpermissionedEids(test.request, "bidderA", test.requestExt)
+		assert.NoError(t, resultErr, test.description+":err")
+		assert.Equal(t, &requestExpected, test.request, test.description+":request")
+	}
+}
diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go
index e5ad8793..62c6a5f9 100644
--- a/gdpr/vendorlist-fetching_test.go
+++ b/gdpr/vendorlist-fetching_test.go
@@ -596,25 +596,25 @@ func TestVendorListURLMaker(t *testing.T) {
 			description:       "TCF1 - Latest",
 			tcfSpecVersion:    1,
 			vendorListVersion: 0, // Forces latest version.
-			expectedURL:       "https://vendorlist.consensu.org/vendorlist.json",
+			expectedURL:       "https://vendor-list.consensu.org/vendorlist.json",
 		},
 		{
 			description:       "TCF1 - Specific",
 			tcfSpecVersion:    1,
 			vendorListVersion: 42,
-			expectedURL:       "https://vendorlist.consensu.org/v-42/vendorlist.json",
+			expectedURL:       "https://vendor-list.consensu.org/v-42/vendorlist.json",
 		},
 		{
 			description:       "TCF2 - Latest",
 			tcfSpecVersion:    2,
 			vendorListVersion: 0, // Forces latest version.
-			expectedURL:       "https://vendorlist.consensu.org/v2/vendor-list.json",
+			expectedURL:       "https://vendor-list.consensu.org/v2/vendor-list.json",
 		},
 		{
 			description:       "TCF2 - Specific",
 			tcfSpecVersion:    2,
 			vendorListVersion: 42,
-			expectedURL:       "https://vendorlist.consensu.org/v2/archives/vendor-list-v42.json",
+			expectedURL:       "https://vendor-list.consensu.org/v2/archives/vendor-list-v42.json",
 		},
 	}
 
@@ -710,7 +710,7 @@ type serverSettings struct {
 // If the "version" query param doesn't exist, it returns a 400.
 //
 // If the "version" query param points to a version which doesn't exist, it returns a 403.
-// Don't ask why... that's just what the official page is doing. See https://vendorlist.consensu.org/v-9999/vendorlist.json
+// Don't ask why... that's just what the official page is doing. See https://vendor-list.consensu.org/v-9999/vendorlist.json
 func mockServer(settings serverSettings) func(http.ResponseWriter, *http.Request) {
 	return func(w http.ResponseWriter, req *http.Request) {
 		vendorListVersion := req.URL.Query().Get("version")