Repository: prebid/prebid-server
Pull request #1838 (2021-05-06 06:10:55)

New Adapter : Kayzen


Patch:
diff --git a/adapters/adf/adf.go b/adapters/adf/adf.go
index 4ed0dc07..f73e23aa 100644
--- a/adapters/adf/adf.go
+++ b/adapters/adf/adf.go
@@ -92,15 +92,38 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R
 
 	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
 	bidResponse.Currency = response.Cur
-
+	var errors []error
 	for _, seatBid := range response.SeatBid {
-		for i := range seatBid.Bid {
+		for i, bid := range seatBid.Bid {
+			bidType, err := getMediaTypeForImp(bid.ImpID, request.Imp)
+			if err != nil {
+				errors = append(errors, err)
+				continue
+			}
 			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
 				Bid:     &seatBid.Bid[i],
-				BidType: openrtb_ext.BidTypeNative,
+				BidType: bidType,
 			})
 		}
 	}
 
-	return bidResponse, nil
+	return bidResponse, errors
+}
+
+func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
+	for _, imp := range imps {
+		if imp.ID == impID {
+			if imp.Banner != nil {
+				return openrtb_ext.BidTypeBanner, nil
+			} else if imp.Video != nil {
+				return openrtb_ext.BidTypeVideo, nil
+			} else if imp.Native != nil {
+				return openrtb_ext.BidTypeNative, nil
+			}
+		}
+	}
+
+	return "", &errortypes.BadInput{
+		Message: fmt.Sprintf("Failed to find supported impression \"%s\" mediatype", impID),
+	}
 }
diff --git a/adapters/conversant/conversant.go b/adapters/conversant/conversant.go
index cdcf7566..7cf4ab5d 100644
--- a/adapters/conversant/conversant.go
+++ b/adapters/conversant/conversant.go
@@ -74,8 +74,14 @@ func (c ConversantAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *a
 func parseCnvrParams(imp *openrtb2.Imp, cnvrExt openrtb_ext.ExtImpConversant) {
 	imp.DisplayManager = "prebid-s2s"
 	imp.DisplayManagerVer = "2.0.0"
-	imp.BidFloor = cnvrExt.BidFloor
-	imp.TagID = cnvrExt.TagID
+
+	if imp.BidFloor <= 0 && cnvrExt.BidFloor > 0 {
+		imp.BidFloor = cnvrExt.BidFloor
+	}
+
+	if len(cnvrExt.TagID) > 0 {
+		imp.TagID = cnvrExt.TagID
+	}
 
 	// Take care not to override the global secure flag
 	if (imp.Secure == nil || *imp.Secure == 0) && cnvrExt.Secure != nil {
diff --git a/adapters/ix/ix.go b/adapters/ix/ix.go
index f8903008..5e10138f 100644
--- a/adapters/ix/ix.go
+++ b/adapters/ix/ix.go
@@ -409,8 +409,10 @@ func (a *IxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalReque
 				unmarshalExtErr := json.Unmarshal(bid.Ext, &bidExt)
 				if unmarshalExtErr == nil && bidExt.Prebid != nil && bidExt.Prebid.Video != nil {
 					bidExtVideo = &openrtb_ext.ExtBidPrebidVideo{
-						Duration:        bidExt.Prebid.Video.Duration,
-						PrimaryCategory: bidExt.Prebid.Video.PrimaryCategory,
+						Duration: bidExt.Prebid.Video.Duration,
+					}
+					if len(bid.Cat) == 0 {
+						bid.Cat = []string{bidExt.Prebid.Video.PrimaryCategory}
 					}
 				}
 			}
diff --git a/adapters/kayzen/kayzen.go b/adapters/kayzen/kayzen.go
new file mode 100644
index 00000000..1fcb9877
--- /dev/null
+++ b/adapters/kayzen/kayzen.go
@@ -0,0 +1,154 @@
+package kayzen
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"text/template"
+
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	endpoint template.Template
+}
+
+// Builder builds a new instance of the Kayzen adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
+	}
+
+	bidder := &adapter{
+		endpoint: *template,
+	}
+	return bidder, nil
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) (requests []*adapters.RequestData, errors []error) {
+	var kayzenExt *openrtb_ext.ExtKayzen
+	var err error
+
+	if len(request.Imp) > 0 {
+		kayzenExt, err = a.getImpressionExt(&(request.Imp[0]))
+		if err != nil {
+			errors = append(errors, err)
+		}
+		request.Imp[0].Ext = nil
+	} else {
+		errors = append(errors, &errortypes.BadInput{
+			Message: "Missing Imp Object",
+		})
+	}
+
+	if len(errors) > 0 {
+		return nil, errors
+	}
+
+	url, err := a.buildEndpointURL(kayzenExt)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	requestJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+
+	return []*adapters.RequestData{{
+		Method:  http.MethodPost,
+		Body:    requestJSON,
+		Uri:     url,
+		Headers: headers,
+	}}, nil
+}
+
+func (a *adapter) getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtKayzen, error) {
+	var bidderExt adapters.ExtImpBidder
+	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "Bidder extension not provided or can't be unmarshalled",
+		}
+	}
+	var kayzenExt openrtb_ext.ExtKayzen
+	if err := json.Unmarshal(bidderExt.Bidder, &kayzenExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "Error while unmarshaling bidder extension",
+		}
+	}
+	return &kayzenExt, nil
+}
+
+func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtKayzen) (string, error) {
+	endpointParams := macros.EndpointTemplateParams{
+		ZoneID:    params.Zone,
+		AccountID: params.Exchange,
+	}
+	return macros.ResolveMacros(a.endpoint, endpointParams)
+}
+
+func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if response.StatusCode == http.StatusBadRequest {
+		err := &errortypes.BadInput{
+			Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
+		}
+		return nil, []error{err}
+	}
+
+	if response.StatusCode != http.StatusOK {
+		err := &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", response.StatusCode),
+		}
+		return nil, []error{err}
+	}
+
+	var bidResp openrtb2.BidResponse
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Bad Server Response",
+		}}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
+
+	var errs []error
+	for _, seatBid := range bidResp.SeatBid {
+		for _, bid := range seatBid.Bid {
+			b := &adapters.TypedBid{
+				Bid:     &bid,
+				BidType: getMediaTypeForImp(bid.ImpID, internalRequest.Imp),
+			}
+			bidResponse.Bids = append(bidResponse.Bids, b)
+		}
+	}
+	return bidResponse, errs
+}
+
+func getMediaTypeForImp(impID string, imps []openrtb2.Imp) openrtb_ext.BidType {
+	for _, imp := range imps {
+		if imp.ID == impID {
+			if imp.Banner != nil {
+				return openrtb_ext.BidTypeBanner
+			} else if imp.Video != nil {
+				return openrtb_ext.BidTypeVideo
+			} else if imp.Native != nil {
+				return openrtb_ext.BidTypeNative
+			}
+		}
+	}
+	return openrtb_ext.BidTypeBanner
+}
diff --git a/adapters/madvertise/madvertise.go b/adapters/madvertise/madvertise.go
new file mode 100644
index 00000000..dfc996d2
--- /dev/null
+++ b/adapters/madvertise/madvertise.go
@@ -0,0 +1,163 @@
+package madvertise
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"text/template"
+
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	endpointTemplate template.Template
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
+	}
+
+	bidder := &adapter{
+		endpointTemplate: *template,
+	}
+
+	return bidder, nil
+}
+
+func getHeaders(request *openrtb2.BidRequest) http.Header {
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	headers.Add("X-Openrtb-Version", "2.5")
+
+	if request.Device != nil {
+		if len(request.Device.UA) > 0 {
+			headers.Add("User-Agent", request.Device.UA)
+		}
+
+		if len(request.Device.IP) > 0 {
+			headers.Add("X-Forwarded-For", request.Device.IP)
+		}
+	}
+
+	return headers
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	zoneID := ""
+	for _, imp := range request.Imp {
+		madvertiseExt, err := getImpressionExt(imp)
+		if err != nil {
+			return nil, []error{err}
+		}
+		if len(madvertiseExt.ZoneID) < 7 {
+			return nil, []error{&errortypes.BadInput{
+				Message: fmt.Sprintf("The minLength of zone ID is 7; ImpID=%s", imp.ID),
+			}}
+		}
+		if zoneID == "" {
+			zoneID = madvertiseExt.ZoneID
+		} else if zoneID != madvertiseExt.ZoneID {
+			return nil, []error{&errortypes.BadInput{
+				Message: "There must be only one zone ID",
+			}}
+		}
+	}
+	url, err := a.buildEndpointURL(zoneID)
+	if err != nil {
+		return nil, []error{err}
+	}
+	requestJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	requestData := &adapters.RequestData{
+		Method:  "POST",
+		Uri:     url,
+		Body:    requestJSON,
+		Headers: getHeaders(request),
+	}
+
+	return []*adapters.RequestData{requestData}, nil
+}
+
+func getImpressionExt(imp openrtb2.Imp) (*openrtb_ext.ExtImpMadvertise, error) {
+	var bidderExt adapters.ExtImpBidder
+	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: fmt.Sprintf("%s; ImpID=%s", err.Error(), imp.ID),
+		}
+	}
+	var madvertiseExt openrtb_ext.ExtImpMadvertise
+	if err := json.Unmarshal(bidderExt.Bidder, &madvertiseExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: fmt.Sprintf("%s; ImpID=%s", err.Error(), imp.ID),
+		}
+	}
+	if madvertiseExt.ZoneID == "" {
+		return nil, &errortypes.BadInput{
+			Message: fmt.Sprintf("ext.bidder.zoneId not provided; ImpID=%s", imp.ID),
+		}
+	}
+
+	return &madvertiseExt, nil
+}
+
+func (a *adapter) buildEndpointURL(zoneID string) (string, error) {
+	endpointParams := macros.EndpointTemplateParams{ZoneID: zoneID}
+	return macros.ResolveMacros(a.endpointTemplate, endpointParams)
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if responseData.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if responseData.StatusCode != http.StatusOK {
+		err := &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", responseData.StatusCode),
+		}
+		return nil, []error{err}
+	}
+
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(responseData.Body, &response); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+	bidResponse.Currency = response.Cur
+	for _, seatBid := range response.SeatBid {
+		for i := range seatBid.Bid {
+			bid := seatBid.Bid[i]
+			bidMediaType := getMediaTypeForBid(bid.Attr)
+			b := &adapters.TypedBid{
+				Bid:     &bid,
+				BidType: bidMediaType,
+			}
+			bidResponse.Bids = append(bidResponse.Bids, b)
+		}
+	}
+	return bidResponse, nil
+}
+
+func getMediaTypeForBid(attr []openrtb2.CreativeAttribute) openrtb_ext.BidType {
+	for i := 0; i < len(attr); i++ {
+		if attr[i] == openrtb2.CreativeAttribute(16) {
+			return openrtb_ext.BidTypeVideo
+		} else if attr[i] == openrtb2.CreativeAttribute(6) {
+			return openrtb_ext.BidTypeVideo
+		} else if attr[i] == openrtb2.CreativeAttribute(7) {
+			return openrtb_ext.BidTypeVideo
+		}
+	}
+	return openrtb_ext.BidTypeBanner
+}
diff --git a/config/adapter.go b/config/adapter.go
index ff262b18..8cdad538 100644
--- a/config/adapter.go
+++ b/config/adapter.go
@@ -64,6 +64,7 @@ const (
 	dummyGDPR        string = "0"
 	dummyGDPRConsent string = "someGDPRConsentString"
 	dummyCCPA        string = "1NYN"
+	dummyZoneID      string = "zone"
 )
 
 // validateAdapterEndpoint makes sure that an adapter has a valid endpoint
@@ -84,6 +85,7 @@ func validateAdapterEndpoint(endpoint string, adapterName string, errs []error)
 		Host:        dummyHost,
 		PublisherID: dummyPublisherID,
 		AccountID:   dummyAccountID,
+		ZoneID:      dummyZoneID,
 	})
 	if err != nil {
 		return append(errs, fmt.Errorf("Unable to resolve endpoint: %s for adapter: %s. %v", endpoint, adapterName, err))
diff --git a/config/config.go b/config/config.go
index 36ec4dc0..ebd3af77 100644
--- a/config/config.go
+++ b/config/config.go
@@ -616,6 +616,7 @@ func (cfg *Configuration) setDerivedDefaults() {
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLockerDome, "https://lockerdome.com/usync/prebidserver?pid="+cfg.Adapters["lockerdome"].PlatformID+"&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlockerdome%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7Buid%7D%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLogicad, "https://cr-p31.ladsp.jp/cookiesender/31?r=true&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&ru="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlogicad%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLunaMedia, "https://api.lunamedia.io/xp/user-sync?redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlunamedia%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
+	// openrtb_ext.BidderMadvertise doesn't have a good default.
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMarsmedia, "https://dmp.rtbsrv.com/dmp/profiles/cm?p_id=179&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmarsmedia%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D")
 	// openrtb_ext.BidderMediafuse doesn't have a good default.
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMgid, "https://cm.mgid.com/m?cdsp=363893&adu="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmgid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Bmuidn%7D")
@@ -862,6 +863,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.interactiveoffers.endpoint", "https://rtb.ioadx.com/bidRequest/?partnerId=d9e56d418c4825d466ee96c7a31bf1da6b62fa04")
 	v.SetDefault("adapters.ix.disabled", true)
 	v.SetDefault("adapters.jixie.endpoint", "https://hb.jixie.io/v2/hbsvrpost")
+	v.SetDefault("adapters.kayzen.endpoint", "https://bids-{{.ZoneID}}.bidder.kayzen.io/?exchange={{.AccountID}}")
 	v.SetDefault("adapters.krushmedia.endpoint", "http://ads4.krushmedia.com/?c=rtb&m=req&key={{.AccountID}}")
 	v.SetDefault("adapters.invibes.endpoint", "https://{{.Host}}/bid/ServerBidAdContent")
 	v.SetDefault("adapters.kidoz.endpoint", "http://prebid-adapter.kidoz.net/openrtb2/auction?src=prebid-server")
@@ -870,6 +872,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.lockerdome.endpoint", "https://lockerdome.com/ladbid/prebidserver/openrtb2")
 	v.SetDefault("adapters.logicad.endpoint", "https://pbs.ladsp.com/adrequest/prebidserver")
 	v.SetDefault("adapters.lunamedia.endpoint", "http://api.lunamedia.io/xp/get?pubid={{.PublisherID}}")
+	v.SetDefault("adapters.madvertise.endpoint", "https://mobile.mng-ads.com/bidrequest{{.ZoneID}}")
 	v.SetDefault("adapters.marsmedia.endpoint", "https://bid306.rtbsrv.com/bidder/?bid=f3xtet")
 	v.SetDefault("adapters.mediafuse.endpoint", "http://ghb.hbmp.mediafuse.com/pbs/ortb")
 	v.SetDefault("adapters.mgid.endpoint", "https://prebid.mgid.com/prebid/")
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 0120005b..59789a25 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -57,12 +57,14 @@ import (
 	"github.com/prebid/prebid-server/adapters/invibes"
 	"github.com/prebid/prebid-server/adapters/ix"
 	"github.com/prebid/prebid-server/adapters/jixie"
+	"github.com/prebid/prebid-server/adapters/kayzen"
 	"github.com/prebid/prebid-server/adapters/kidoz"
 	"github.com/prebid/prebid-server/adapters/krushmedia"
 	"github.com/prebid/prebid-server/adapters/kubient"
 	"github.com/prebid/prebid-server/adapters/lockerdome"
 	"github.com/prebid/prebid-server/adapters/logicad"
 	"github.com/prebid/prebid-server/adapters/lunamedia"
+	"github.com/prebid/prebid-server/adapters/madvertise"
 	"github.com/prebid/prebid-server/adapters/marsmedia"
 	"github.com/prebid/prebid-server/adapters/mgid"
 	"github.com/prebid/prebid-server/adapters/mobfoxpb"
@@ -171,12 +173,14 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderInvibes:           invibes.Builder,
 		openrtb_ext.BidderIx:                ix.Builder,
 		openrtb_ext.BidderJixie:             jixie.Builder,
+		openrtb_ext.BidderKayzen:            kayzen.Builder,
 		openrtb_ext.BidderKidoz:             kidoz.Builder,
 		openrtb_ext.BidderKrushmedia:        krushmedia.Builder,
 		openrtb_ext.BidderKubient:           kubient.Builder,
 		openrtb_ext.BidderLockerDome:        lockerdome.Builder,
 		openrtb_ext.BidderLogicad:           logicad.Builder,
 		openrtb_ext.BidderLunaMedia:         lunamedia.Builder,
+		openrtb_ext.BidderMadvertise:        madvertise.Builder,
 		openrtb_ext.BidderMarsmedia:         marsmedia.Builder,
 		openrtb_ext.BidderMediafuse:         adtelligent.Builder,
 		openrtb_ext.BidderMgid:              mgid.Builder,
diff --git a/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json  b/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json
similarity index 100%
rename from exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json 
rename to exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json
diff --git a/exchange/utils.go b/exchange/utils.go
index 5bde1bd7..e367facb 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -133,7 +133,7 @@ func cleanOpenRTBRequests(ctx context.Context,
 				}
 			}
 			var publisherID = req.LegacyLabels.PubID
-			_, geo, id, err := gDPR.PersonalInfoAllowed(ctx, bidderRequest.BidderCoreName, publisherID, gdprSignal, consent, weakVendorEnforcement)
+			geo, id, err := gDPR.PersonalInfoAllowed(ctx, bidderRequest.BidderCoreName, publisherID, gdprSignal, consent, weakVendorEnforcement)
 			if err == nil {
 				privacyEnforcement.GDPRGeo = !geo
 				privacyEnforcement.GDPRID = !id
diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go
index 616d0f0a..2b450160 100644
--- a/gdpr/gdpr.go
+++ b/gdpr/gdpr.go
@@ -25,7 +25,7 @@ type Permissions interface {
 	// Determines whether or not to send PI information to a bidder, or mask it out.
 	//
 	// If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent.
-	PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, weakVendorEnforcement bool) (bool, bool, bool, error)
+	PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error)
 }
 
 // Versions of the GDPR TCF technical specification.
diff --git a/gdpr/impl.go b/gdpr/impl.go
index 55d1cd4a..22b4dbe3 100644
--- a/gdpr/impl.go
+++ b/gdpr/impl.go
@@ -63,19 +63,19 @@ func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context,
 	PublisherID string,
 	gdprSignal Signal,
 	consent string,
-	weakVendorEnforcement bool) (allowPI bool, allowGeo bool, allowID bool, err error) {
+	weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
 	if _, ok := p.cfg.NonStandardPublisherMap[PublisherID]; ok {
-		return true, true, true, nil
+		return true, true, nil
 	}
 
 	gdprSignal = p.normalizeGDPR(gdprSignal)
 
 	if gdprSignal == SignalNo {
-		return true, true, true, nil
+		return true, true, nil
 	}
 
 	if consent == "" && gdprSignal == SignalYes {
-		return false, false, false, nil
+		return false, false, nil
 	}
 
 	if id, ok := p.vendorIDs[bidder]; ok {
@@ -85,8 +85,8 @@ func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context,
 	return p.defaultVendorPermissions()
 }
 
-func (p *permissionsImpl) defaultVendorPermissions() (allowPI bool, allowGeo bool, allowID bool, err error) {
-	return false, false, false, nil
+func (p *permissionsImpl) defaultVendorPermissions() (allowGeo bool, allowID bool, err error) {
+	return false, false, nil
 }
 
 func (p *permissionsImpl) normalizeGDPR(gdprSignal Signal) Signal {
@@ -135,14 +135,14 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, consen
 	return false, nil
 }
 
-func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent string, weakVendorEnforcement bool) (bool, bool, bool, error) {
+func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
 	parsedConsent, vendor, err := p.parseVendor(ctx, vendorID, consent)
 	if err != nil {
-		return false, false, false, err
+		return false, false, err
 	}
 
 	if vendor == nil {
-		return false, false, false, nil
+		return false, false, nil
 	}
 
 	if parsedConsent.Version() == 2 {
@@ -150,20 +150,19 @@ func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent
 			return p.allowPITCF2(parsedConsent, vendor, vendorID, weakVendorEnforcement)
 		}
 		if (vendor.Purpose(consentconstants.InfoStorageAccess) || vendor.LegitimateInterest(consentconstants.InfoStorageAccess) || weakVendorEnforcement) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && (vendor.Purpose(consentconstants.PersonalizationProfile) || vendor.LegitimateInterest(consentconstants.PersonalizationProfile) || weakVendorEnforcement) && parsedConsent.PurposeAllowed(consentconstants.PersonalizationProfile) && (parsedConsent.VendorConsent(vendorID) || weakVendorEnforcement) {
-			return true, true, true, nil
+			return true, true, nil
 		}
 	} else {
 		if (vendor.Purpose(tcf1constants.InfoStorageAccess) || vendor.LegitimateInterest(tcf1constants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(tcf1constants.InfoStorageAccess) && (vendor.Purpose(tcf1constants.AdSelectionDeliveryReporting) || vendor.LegitimateInterest(tcf1constants.AdSelectionDeliveryReporting)) && parsedConsent.PurposeAllowed(tcf1constants.AdSelectionDeliveryReporting) && parsedConsent.VendorConsent(vendorID) {
-			return true, true, true, nil
+			return true, true, nil
 		}
 	}
-	return false, false, false, nil
+	return false, false, nil
 }
 
-func (p *permissionsImpl) allowPITCF2(parsedConsent api.VendorConsents, vendor api.Vendor, vendorID uint16, weakVendorEnforcement bool) (allowPI bool, allowGeo bool, allowID bool, err error) {
+func (p *permissionsImpl) allowPITCF2(parsedConsent api.VendorConsents, vendor api.Vendor, vendorID uint16, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
 	consent, ok := parsedConsent.(tcf2.ConsentMetadata)
 	err = nil
-	allowPI = false
 	allowGeo = false
 	allowID = false
 	if !ok {
@@ -181,17 +180,7 @@ func (p *permissionsImpl) allowPITCF2(parsedConsent api.VendorConsents, vendor a
 			break
 		}
 	}
-	// Set to true so any purpose check can flip it to false
-	allowPI = true
-	if p.cfg.TCF2.Purpose1.Enabled {
-		allowPI = allowPI && p.checkPurpose(consent, vendor, vendorID, consentconstants.InfoStorageAccess, weakVendorEnforcement)
-	}
-	if p.cfg.TCF2.Purpose2.Enabled {
-		allowPI = allowPI && p.checkPurpose(consent, vendor, vendorID, consentconstants.BasicAdserving, weakVendorEnforcement)
-	}
-	if p.cfg.TCF2.Purpose7.Enabled {
-		allowPI = allowPI && p.checkPurpose(consent, vendor, vendorID, consentconstants.AdPerformance, weakVendorEnforcement)
-	}
+
 	return
 }
 
@@ -265,8 +254,8 @@ func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.B
 	return true, nil
 }
 
-func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, weakVendorEnforcement bool) (bool, bool, bool, error) {
-	return true, true, true, nil
+func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
+	return true, true, nil
 }
 
 // Exporting to allow for easy test setups
@@ -280,6 +269,6 @@ func (a AlwaysFail) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.Bi
 	return false, nil
 }
 
-func (a AlwaysFail) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, weakVendorEnforcement bool) (bool, bool, bool, error) {
-	return false, false, false, nil
+func (a AlwaysFail) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
+	return false, false, nil
 }
diff --git a/go.mod b/go.mod
index 585798cb..b7fab89f 100644
--- a/go.mod
+++ b/go.mod
@@ -27,6 +27,7 @@ require (
 	github.com/magiconair/properties v1.8.0
 	github.com/mattn/go-colorable v0.1.2 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
+	github.com/mitchellh/copystructure v1.1.2
 	github.com/mitchellh/mapstructure v1.0.0 // indirect
 	github.com/mxmCherry/openrtb/v15 v15.0.0
 	github.com/pelletier/go-toml v1.2.0 // indirect
diff --git a/go.sum b/go.sum
index 7a38c2f1..3ddeaa2f 100644
--- a/go.sum
+++ b/go.sum
@@ -73,8 +73,12 @@ github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mitchellh/copystructure v1.1.2 h1:Th2TIvG1+6ma3e/0/bopBKohOTY7s4dA8V2q4EUcBJ0=
+github.com/mitchellh/copystructure v1.1.2/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4=
 github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I=
 github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
+github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/mxmCherry/openrtb/v15 v15.0.0 h1:inLuQ3Bsima9HLB2v6WjbtEFF69SWOT5Dux4QZtYdrw=
 github.com/mxmCherry/openrtb/v15 v15.0.0/go.mod h1:TVgncsz6MOzbL7lhun1lNuUBzVBlVDbxf9Fyy1TyhZA=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -123,7 +127,6 @@ github.com/spf13/viper v1.1.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7Sr
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -146,7 +149,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -157,10 +159,8 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -170,7 +170,6 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw=
 golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -191,11 +190,9 @@ google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyz
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 2fb50cf3..e224cae7 100755
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -128,6 +128,7 @@ const (
 	BidderInvibes           BidderName = "invibes"
 	BidderIx                BidderName = "ix"
 	BidderJixie             BidderName = "jixie"
+	BidderKayzen            BidderName = "kayzen"
 	BidderKidoz             BidderName = "kidoz"
 	BidderKrushmedia        BidderName = "krushmedia"
 	BidderKubient           BidderName = "kubient"
@@ -135,6 +136,7 @@ const (
 	BidderLockerDome        BidderName = "lockerdome"
 	BidderLogicad           BidderName = "logicad"
 	BidderLunaMedia         BidderName = "lunamedia"
+	BidderMadvertise        BidderName = "madvertise"
 	BidderMarsmedia         BidderName = "marsmedia"
 	BidderMediafuse         BidderName = "mediafuse"
 	BidderMgid              BidderName = "mgid"
@@ -242,6 +244,7 @@ func CoreBidderNames() []BidderName {
 		BidderInvibes,
 		BidderIx,
 		BidderJixie,
+		BidderKayzen,
 		BidderKidoz,
 		BidderKrushmedia,
 		BidderKubient,
@@ -249,6 +252,7 @@ func CoreBidderNames() []BidderName {
 		BidderLockerDome,
 		BidderLogicad,
 		BidderLunaMedia,
+		BidderMadvertise,
 		BidderMarsmedia,
 		BidderMediafuse,
 		BidderMgid,
diff --git a/openrtb_ext/imp_kayzen.go b/openrtb_ext/imp_kayzen.go
new file mode 100644
index 00000000..35dece64
--- /dev/null
+++ b/openrtb_ext/imp_kayzen.go
@@ -0,0 +1,7 @@
+package openrtb_ext
+
+// ExtKayzen defines the contract for bidrequest.imp[i].ext.kayzen
+type ExtKayzen struct {
+	Zone     string `json:"zone"`
+	Exchange string `json:"exchange"`
+}
diff --git a/openrtb_ext/imp_madvertise.go b/openrtb_ext/imp_madvertise.go
new file mode 100644
index 00000000..30bd88db
--- /dev/null
+++ b/openrtb_ext/imp_madvertise.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+// ExtImpMadvertise defines the contract for bidrequest.imp[i].ext.madvertise
+type ExtImpMadvertise struct {
+	ZoneID string `json:"zoneId"`
+}
diff --git a/static/bidder-info/adf.yaml b/static/bidder-info/adf.yaml
index 8cc96350..776e208f 100644
--- a/static/bidder-info/adf.yaml
+++ b/static/bidder-info/adf.yaml
@@ -4,7 +4,11 @@ gvlVendorID: 50
 capabilities:
   app:
     mediaTypes:
+      - banner
       - native
+      - video
   site:
     mediaTypes:
+      - banner
       - native
+      - video
diff --git a/static/bidder-info/kayzen.yaml b/static/bidder-info/kayzen.yaml
new file mode 100644
index 00000000..45fd43c5
--- /dev/null
+++ b/static/bidder-info/kayzen.yaml
@@ -0,0 +1,15 @@
+maintainer:
+  email: "platform-dev@kayzen.io"
+gvlVendorID: 528
+modifyingVastXmlAllowed: true
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
diff --git a/static/bidder-info/madvertise.yaml b/static/bidder-info/madvertise.yaml
new file mode 100644
index 00000000..6156940e
--- /dev/null
+++ b/static/bidder-info/madvertise.yaml
@@ -0,0 +1,11 @@
+maintainer:
+  email: "support@madvertise.com"
+gvlVendorID: 153
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+  site:
+    mediaTypes:
+      - banner
diff --git a/static/bidder-info/revcontent.yaml b/static/bidder-info/revcontent.yaml
index c0216dfc..57e88756 100644
--- a/static/bidder-info/revcontent.yaml
+++ b/static/bidder-info/revcontent.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "developers@revcontent.com"
+gvlVendorID: 203
 capabilities:
   app:
     mediaTypes:
@@ -8,4 +9,4 @@ capabilities:
   site:
     mediaTypes:
       - banner
-      - native
\ No newline at end of file
+      - native
diff --git a/static/bidder-params/kayzen.json b/static/bidder-params/kayzen.json
new file mode 100644
index 00000000..f2256c6b
--- /dev/null
+++ b/static/bidder-params/kayzen.json
@@ -0,0 +1,21 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Kayzen Adapter Params",
+  "description": "A schema which validates params accepted by the Kayzen adapter",
+  "type": "object",
+
+  "properties": {
+    "zone": {
+      "type": "string",
+      "minLength": 1,
+      "description": "Zone ID"
+    },
+    "exchange": {
+      "type": "string",
+      "minLength": 1,
+      "description": "Exchange/Publisher Name"
+    }
+  },
+  "required": ["zone", "exchange"]
+}
+
diff --git a/static/bidder-params/madvertise.json b/static/bidder-params/madvertise.json
new file mode 100644
index 00000000..c2fbd941
--- /dev/null
+++ b/static/bidder-params/madvertise.json
@@ -0,0 +1,16 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "Madvertise Adapter Params",
+    "description": "A schema which validates params accepted by the Madvertise adapter",
+    "type": "object",
+    "properties": {
+        "zoneId": {
+            "type": "string",
+            "minLength": 7,
+            "description": "The zone ID provided by Madvertise"
+        }
+    },
+    "required": [
+        "zoneId"
+    ]
+}
\ No newline at end of file


Test patch:
diff --git a/adapters/adapterstest/test_json.go b/adapters/adapterstest/test_json.go
index 95319f1e..35a5a570 100644
--- a/adapters/adapterstest/test_json.go
+++ b/adapters/adapterstest/test_json.go
@@ -7,8 +7,10 @@ import (
 	"regexp"
 	"testing"
 
+	"github.com/mitchellh/copystructure"
 	"github.com/mxmCherry/openrtb/v15/openrtb2"
 	"github.com/prebid/prebid-server/adapters"
+	"github.com/stretchr/testify/assert"
 	"github.com/yudai/gojsondiff"
 	"github.com/yudai/gojsondiff/formatter"
 
@@ -107,24 +109,10 @@ func runSpec(t *testing.T, filename string, spec *testSpec, bidder adapters.Bidd
 	} else if isVideoTest {
 		reqInfo.PbsEntryPoint = "video"
 	}
-	actualReqs, errs := bidder.MakeRequests(&spec.BidRequest, &reqInfo)
-	diffErrorLists(t, fmt.Sprintf("%s: MakeRequests", filename), errs, spec.MakeRequestErrors)
-	diffHttpRequestLists(t, filename, actualReqs, spec.HttpCalls)
 
-	bidResponses := make([]*adapters.BidderResponse, 0)
+	requests := testMakeRequestsImpl(t, filename, spec, bidder, &reqInfo)
 
-	var bidsErrs = make([]error, 0, len(spec.MakeBidsErrors))
-	for i := 0; i < len(actualReqs); i++ {
-		thisBidResponse, theseErrs := bidder.MakeBids(&spec.BidRequest, spec.HttpCalls[i].Request.ToRequestData(t), spec.HttpCalls[i].Response.ToResponseData(t))
-		bidsErrs = append(bidsErrs, theseErrs...)
-		bidResponses = append(bidResponses, thisBidResponse)
-	}
-
-	diffErrorLists(t, fmt.Sprintf("%s: MakeBids", filename), bidsErrs, spec.MakeBidsErrors)
-
-	for i := 0; i < len(spec.BidResponses); i++ {
-		diffBidLists(t, filename, bidResponses[i], spec.BidResponses[i].Bids)
-	}
+	testMakeBidsImpl(t, filename, spec, bidder, requests)
 }
 
 type testSpec struct {
@@ -194,8 +182,8 @@ type expectedBid struct {
 //
 // Marshalling the structs and then using a JSON-diff library isn't great either, since
 
-// diffHttpRequests compares the actual http requests to the expected ones.
-func diffHttpRequestLists(t *testing.T, filename string, actual []*adapters.RequestData, expected []httpCall) {
+// assertMakeRequestsOutput compares the actual http requests to the expected ones.
+func assertMakeRequestsOutput(t *testing.T, filename string, actual []*adapters.RequestData, expected []httpCall) {
 	t.Helper()
 
 	if len(expected) != len(actual) {
@@ -206,7 +194,7 @@ func diffHttpRequestLists(t *testing.T, filename string, actual []*adapters.Requ
 	}
 }
 
-func diffErrorLists(t *testing.T, description string, actual []error, expected []testSpecExpectedError) {
+func assertErrorList(t *testing.T, description string, actual []error, expected []testSpecExpectedError) {
 	t.Helper()
 
 	if len(expected) != len(actual) {
@@ -227,10 +215,10 @@ func diffErrorLists(t *testing.T, description string, actual []error, expected [
 	}
 }
 
-func diffBidLists(t *testing.T, filename string, response *adapters.BidderResponse, expected []expectedBid) {
+func assertMakeBidsOutput(t *testing.T, filename string, bidderResponse *adapters.BidderResponse, expected []expectedBid) {
 	t.Helper()
 
-	if (response == nil || len(response.Bids) == 0) != (len(expected) == 0) {
+	if (bidderResponse == nil || len(bidderResponse.Bids) == 0) != (len(expected) == 0) {
 		if len(expected) == 0 {
 			t.Fatalf("%s: expectedBidResponses indicated a nil response, but mockResponses supplied a non-nil response", filename)
 		}
@@ -239,17 +227,15 @@ func diffBidLists(t *testing.T, filename string, response *adapters.BidderRespon
 	}
 
 	// Expected nil response - give diffBids something to work with.
-	if response == nil {
-		response = new(adapters.BidderResponse)
+	if bidderResponse == nil {
+		bidderResponse = new(adapters.BidderResponse)
 	}
 
-	actual := response.Bids
-
-	if len(actual) != len(expected) {
-		t.Fatalf("%s: MakeBids returned wrong bid count. Expected %d, got %d", filename, len(expected), len(actual))
+	if len(bidderResponse.Bids) != len(expected) {
+		t.Fatalf("%s: MakeBids returned wrong bid count. Expected %d, got %d", filename, len(expected), len(bidderResponse.Bids))
 	}
-	for i := 0; i < len(actual); i++ {
-		diffBids(t, fmt.Sprintf("%s:  typedBid[%d]", filename, i), actual[i], &(expected[i]))
+	for i := 0; i < len(bidderResponse.Bids); i++ {
+		diffBids(t, fmt.Sprintf("%s:  typedBid[%d]", filename, i), bidderResponse.Bids[i], &(expected[i]))
 	}
 }
 
@@ -331,3 +317,78 @@ func diffJson(t *testing.T, description string, actual []byte, expected []byte)
 		}
 	}
 }
+
+// testMakeRequestsImpl asserts the resulting values of the bidder's `MakeRequests()` implementation
+// against the expected JSON-defined results and ensures we do not encounter data races in the process.
+// To assert no data races happen we make use of:
+//  1) A shallow copy of the unmarshalled openrtb2.BidRequest that will provide reference values to
+//     shared memory that we don't want the adapters' implementation of `MakeRequests()` to modify.
+//  2) A deep copy that will preserve the original values of all the fields. This copy remains untouched
+//     by the adapters' processes and serves as reference of what the shared memory values should still
+//     be after the `MakeRequests()` call.
+func testMakeRequestsImpl(t *testing.T, filename string, spec *testSpec, bidder adapters.Bidder, reqInfo *adapters.ExtraRequestInfo) []*adapters.RequestData {
+	t.Helper()
+
+	deepBidReqCopy, shallowBidReqCopy, err := getDataRaceTestCopies(&spec.BidRequest)
+	assert.NoError(t, err, "Could not create request copies. %s", filename)
+
+	// Run MakeRequests
+	requests, errs := bidder.MakeRequests(&spec.BidRequest, reqInfo)
+
+	// Compare MakeRequests actual output versus expected values found in JSON file
+	assertErrorList(t, fmt.Sprintf("%s: MakeRequests", filename), errs, spec.MakeRequestErrors)
+	assertMakeRequestsOutput(t, filename, requests, spec.HttpCalls)
+
+	// Assert no data races occur using original bidRequest copies of references and values
+	assert.Equal(t, deepBidReqCopy, shallowBidReqCopy, "Data race found. Test: %s", filename)
+
+	return requests
+}
+
+// getDataRaceTestCopies returns a deep copy and a shallow copy of the original bidRequest that will get
+// compared to verify no data races occur.
+func getDataRaceTestCopies(original *openrtb2.BidRequest) (*openrtb2.BidRequest, *openrtb2.BidRequest, error) {
+	cpy, err := copystructure.Copy(original)
+	if err != nil {
+		return nil, nil, err
+	}
+	deepReqCopy := cpy.(*openrtb2.BidRequest)
+
+	shallowReqCopy := *original
+
+	// Prebid Server core makes shallow copies of imp elements and adapters are allowed to make changes
+	// to them. Therefore, we need shallow copies of Imp elements here so our test replicates that
+	// functionality and only fail when actual shared momory gets modified.
+	if original.Imp != nil {
+		shallowReqCopy.Imp = make([]openrtb2.Imp, len(original.Imp))
+		copy(shallowReqCopy.Imp, original.Imp)
+	}
+
+	return deepReqCopy, &shallowReqCopy, nil
+}
+
+// testMakeBidsImpl asserts the results of the bidder MakeBids implementation against the expected JSON-defined results
+func testMakeBidsImpl(t *testing.T, filename string, spec *testSpec, bidder adapters.Bidder, makeRequestsOut []*adapters.RequestData) {
+	t.Helper()
+
+	bidResponses := make([]*adapters.BidderResponse, 0)
+	var bidsErrs = make([]error, 0, len(spec.MakeBidsErrors))
+
+	// We should have as many bids as number of adapters.RequestData found in MakeRequests output
+	for i := 0; i < len(makeRequestsOut); i++ {
+		// Run MakeBids with JSON refined spec.HttpCalls info that was asserted to match MakeRequests
+		// output inside testMakeRequestsImpl
+		thisBidResponse, theseErrs := bidder.MakeBids(&spec.BidRequest, spec.HttpCalls[i].Request.ToRequestData(t), spec.HttpCalls[i].Response.ToResponseData(t))
+
+		bidsErrs = append(bidsErrs, theseErrs...)
+		bidResponses = append(bidResponses, thisBidResponse)
+	}
+
+	// Assert actual errors thrown by MakeBids implementation versus expected JSON-defined spec.MakeBidsErrors
+	assertErrorList(t, fmt.Sprintf("%s: MakeBids", filename), bidsErrs, spec.MakeBidsErrors)
+
+	// Assert MakeBids implementation BidResponses with expected JSON-defined spec.BidResponses[i].Bids
+	for i := 0; i < len(spec.BidResponses); i++ {
+		assertMakeBidsOutput(t, filename, bidResponses[i], spec.BidResponses[i].Bids)
+	}
+}
diff --git a/adapters/adf/adftest/exemplary/multi-format.json b/adapters/adf/adftest/exemplary/multi-format.json
new file mode 100644
index 00000000..6b917658
--- /dev/null
+++ b/adapters/adf/adftest/exemplary/multi-format.json
@@ -0,0 +1,118 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+      "id": "test-imp-id-1",
+      "ext": {
+        "bidder": {
+          "mid": "828782"
+        }
+      },
+      "video": {
+        "mimes": [
+          "video/mp4"
+        ],
+        "placement": 1
+      }
+    }, {
+      "id": "test-imp-id-2",
+      "ext": {
+        "bidder": {
+          "mid": "828783"
+        }
+      },
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      }
+    }]
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://adx.adform.net/adx/openrtb",
+      "body": {
+        "id": "test-request-id",
+        "imp": [{
+          "id": "test-imp-id-1",
+          "ext": {
+            "bidder": {
+              "mid": "828782"
+            }
+          },
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ],
+            "placement": 1
+          },
+          "tagid": "828782"
+        }, {
+          "id": "test-imp-id-2",
+          "ext": {
+            "bidder": {
+              "mid": "828783"
+            }
+          },
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "tagid": "828783"
+        }]
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "test-request-id",
+        "seatbid": [{
+          "bid": [{
+            "id": "test-bid-id-1",
+            "impid": "test-imp-id-1",
+            "price": 10,
+            "adm": "{video xml}",
+            "adomain": [],
+            "crid": "test-creative-id-1"
+          }]
+        }, {
+          "bid": [{
+            "id": "test-bid-id-2",
+            "impid": "test-imp-id-2",
+            "price": 2,
+            "adm": "{banner html}",
+            "adomain": [ "ad-domain" ],
+            "crid": "test-creative-id-2"
+          }]
+        }],
+        "cur": "EUR"
+      }
+    }
+  }],
+  "expectedBidResponses": [{
+    "currency": "EUR",
+    "bids": [{
+      "bid": {
+        "id": "test-bid-id-1",
+        "impid": "test-imp-id-1",
+        "price": 10,
+        "adm": "{video xml}",
+        "crid": "test-creative-id-1"
+      },
+      "type": "video"
+    }, {
+      "bid": {
+        "id": "test-bid-id-2",
+        "impid": "test-imp-id-2",
+        "price": 2,
+        "adm": "{banner html}",
+        "adomain": [ "ad-domain" ],
+        "crid": "test-creative-id-2"
+      },
+      "type": "banner"
+    }]
+  }]
+}
diff --git a/adapters/adf/adftest/exemplary/single-banner.json b/adapters/adf/adftest/exemplary/single-banner.json
new file mode 100644
index 00000000..5fdfe8af
--- /dev/null
+++ b/adapters/adf/adftest/exemplary/single-banner.json
@@ -0,0 +1,95 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+      "id": "test-imp-id",
+      "ext": {
+        "bidder": {
+          "mid": "828782"
+        }
+      },
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      }
+    }],
+    "site": {
+      "publisher": {
+        "id": "1"
+      },
+      "page": "some-page-url"
+    },
+    "device": {
+      "w": 1920,
+      "h": 800
+    }
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://adx.adform.net/adx/openrtb",
+      "body": {
+        "id": "test-request-id",
+        "imp": [{
+          "id": "test-imp-id",
+          "ext": {
+            "bidder": {
+              "mid": "828782"
+            }
+          },
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "tagid": "828782"
+        }],
+        "site": {
+          "publisher": {
+            "id": "1"
+          },
+          "page": "some-page-url"
+        },
+        "device": {
+          "w": 1920,
+          "h": 800
+        }
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "test-request-id",
+        "seatbid": [{
+          "bid": [{
+            "id": "test-bid-id",
+            "impid": "test-imp-id",
+            "price": 10,
+            "adm": "{banner html}",
+            "adomain": [ "test.com" ],
+            "crid": "test-creative-id"
+          }]
+        }],
+        "cur": "USD"
+      }
+    }
+  }],
+  "expectedBidResponses": [{
+    "currency": "USD",
+    "bids": [
+      {
+        "bid": {
+          "id": "test-bid-id",
+          "impid": "test-imp-id",
+          "price": 10,
+          "adm": "{banner html}",
+          "crid": "test-creative-id",
+          "adomain": [ "test.com" ]
+        },
+        "type": "banner"
+      }
+    ]
+  }]
+}
diff --git a/adapters/adf/adftest/exemplary/single-video.json b/adapters/adf/adftest/exemplary/single-video.json
new file mode 100644
index 00000000..ebe4c6f1
--- /dev/null
+++ b/adapters/adf/adftest/exemplary/single-video.json
@@ -0,0 +1,93 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+      "id": "test-imp-id",
+      "ext": {
+        "bidder": {
+          "mid": "828782"
+        }
+      },
+      "video": {
+        "mimes": [
+          "video/mp4"
+        ],
+        "placement": 1
+      }
+    }],
+    "site": {
+      "publisher": {
+        "id": "1"
+      },
+      "page": "some-page-url"
+    },
+    "device": {
+      "w": 1920,
+      "h": 800
+    }
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://adx.adform.net/adx/openrtb",
+      "body": {
+        "id": "test-request-id",
+        "imp": [{
+          "id": "test-imp-id",
+          "ext": {
+            "bidder": {
+              "mid": "828782"
+            }
+          },
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ],
+            "placement": 1
+          },
+          "tagid": "828782"
+        }],
+        "site": {
+          "publisher": {
+            "id": "1"
+          },
+          "page": "some-page-url"
+        },
+        "device": {
+          "w": 1920,
+          "h": 800
+        }
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "test-request-id",
+        "seatbid": [{
+          "bid": [{
+            "id": "test-bid-id",
+            "impid": "test-imp-id",
+            "price": 10,
+            "adm": "{vast xml}",
+            "crid": "test-creative-id"
+          }]
+        }],
+        "cur": "USD"
+      }
+    }
+  }],
+  "expectedBidResponses": [{
+    "currency": "USD",
+    "bids": [
+      {
+        "bid": {
+          "id": "test-bid-id",
+          "impid": "test-imp-id",
+          "price": 10,
+          "adm": "{vast xml}",
+          "crid": "test-creative-id"
+        },
+        "type": "video"
+      }
+    ]
+  }]
+}
diff --git a/adapters/adf/adftest/supplemental/invalid-imp-mediatype.json b/adapters/adf/adftest/supplemental/invalid-imp-mediatype.json
new file mode 100644
index 00000000..2f01e7ea
--- /dev/null
+++ b/adapters/adf/adftest/supplemental/invalid-imp-mediatype.json
@@ -0,0 +1,59 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [{
+        "id": "test-imp-id",
+        "audio": {},
+        "ext": {
+          "bidder": {
+            "mid": 12345
+          }
+        }
+      }]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://adx.adform.net/adx/openrtb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [{
+            "ext": {
+              "bidder": {
+                "mid": 12345
+              }
+            },
+            "id": "test-imp-id",
+            "audio": {
+              "mimes": null
+            },
+            "tagid": "12345"
+          }]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [{
+            "bid": [{
+              "id": "test-bid-id",
+              "impid": "test-imp-id",
+              "price": 10,
+              "adm": "{vast xml}",
+              "crid": "test-creative-id"
+            }]
+          }],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [ 
+    {
+      "value": "Failed to find supported impression \"test-imp-id\" mediatype",
+      "comparison": "literal" 
+    }
+  ]
+}
diff --git a/adapters/conversant/conversanttest/supplemental/test_params.json b/adapters/conversant/conversanttest/supplemental/test_params.json
new file mode 100644
index 00000000..403bcc42
--- /dev/null
+++ b/adapters/conversant/conversanttest/supplemental/test_params.json
@@ -0,0 +1,306 @@
+{
+  "mockBidRequest": {
+    "id": "testauction",
+    "imp": [
+      {
+        "id": "1",
+        "bidfloor": 7,
+        "tagid": "mytag",
+        "secure": 1,
+        "video": {
+          "format": [{"w": 300, "h": 250}],
+          "pos": 1,
+          "mimes": ["video/mp4"],
+          "api": [1,2],
+          "protocols": [2],
+          "maxduration": 60
+        },
+        "ext": {
+          "bidder": {
+            "site_id": "108060"
+          }
+        }
+      },
+      {
+        "id": "2",
+        "bidfloor": 1,
+        "secure": 0,
+        "tagid": "sometag",
+        "video": {
+          "format": [{"w": 300, "h": 250}],
+          "pos": 0,
+          "mimes": ["video/wmv"],
+          "api": [0],
+          "protocols": [0],
+          "maxduration": 0
+        },
+        "ext": {
+          "bidder": {
+            "site_id": "108060",
+            "bidfloor": 7,
+            "tag_id": "mytag",
+            "secure": 1,
+            "position": 1,
+            "mimes": ["video/mp4"],
+            "api": [1,2],
+            "protocols": [2],
+            "maxduration": 60
+          }
+        }
+      },
+      {
+        "id": "3",
+        "video": {
+          "format": [{"w": 300, "h": 250}]
+        },
+        "ext": {
+          "bidder": {
+            "site_id": "108060",
+            "bidfloor": 7,
+            "tag_id": "mytag",
+            "secure": 1,
+            "position": 1,
+            "mimes": ["video/mp4"],
+            "api": [1,2],
+            "protocols": [2],
+            "maxduration": 60
+          }
+        }
+      },
+      {
+        "id": "4",
+	"bidfloor": -3,
+        "video": {
+          "format": [{"w": 300, "h": 250}]
+        },
+        "ext": {
+          "bidder": {
+            "site_id": "108060",
+            "bidfloor": 0,
+            "tag_id": "mytag",
+            "secure": 1,
+            "position": 1,
+            "mimes": ["video/mp4"],
+            "api": [1,2],
+            "protocols": [2],
+            "maxduration": 60
+          }
+        }
+      }
+    ],
+    "site": {
+      "domain": "www.mypage.com"
+    }
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "",
+      "body": {
+        "id": "testauction",
+        "site": {
+          "id": "108060",
+          "domain": "www.mypage.com"
+        },
+        "imp": [
+          {
+            "id": "1",
+            "bidfloor": 7,
+            "secure": 1,
+            "tagid": "mytag",
+            "displaymanager": "prebid-s2s",
+            "displaymanagerver": "2.0.0",
+            "video": {
+              "api": [1,2],
+              "maxduration": 60,
+              "mimes": ["video/mp4"],
+              "protocols": [2]
+            },
+            "ext": {
+              "bidder": {
+                "site_id": "108060"
+              }
+            }
+          },
+          {
+            "id": "2",
+            "bidfloor": 1,
+            "secure": 1,
+            "tagid": "mytag",
+            "displaymanager": "prebid-s2s",
+            "displaymanagerver": "2.0.0",
+            "video": {
+              "api": [1,2],
+              "maxduration": 60,
+              "mimes": ["video/mp4"],
+              "protocols": [2],
+              "pos": 1
+            },
+            "ext": {
+              "bidder": {
+                "site_id": "108060",
+                "bidfloor": 7,
+                "tag_id": "mytag",
+                "secure": 1,
+                "position": 1,
+                "mimes": ["video/mp4"],
+                "api": [1,2],
+                "protocols": [2],
+                "maxduration": 60
+              }
+            }
+          },
+          {
+            "id": "3",
+            "bidfloor": 7,
+            "secure": 1,
+            "tagid": "mytag",
+            "displaymanager": "prebid-s2s",
+            "displaymanagerver": "2.0.0",
+            "video": {
+              "api": [1,2],
+              "maxduration": 60,
+              "mimes": ["video/mp4"],
+              "protocols": [2],
+              "pos": 1
+            },
+            "ext": {
+              "bidder": {
+                "site_id": "108060",
+                "bidfloor": 7,
+                "tag_id": "mytag",
+                "secure": 1,
+                "position": 1,
+                "mimes": ["video/mp4"],
+                "api": [1,2],
+                "protocols": [2],
+                "maxduration": 60
+              }
+            }
+          },
+          {
+            "id": "4",
+            "bidfloor": -3,
+            "secure": 1,
+            "tagid": "mytag",
+            "displaymanager": "prebid-s2s",
+            "displaymanagerver": "2.0.0",
+            "video": {
+              "api": [1,2],
+              "maxduration": 60,
+              "mimes": ["video/mp4"],
+              "protocols": [2],
+              "pos": 1
+            },
+            "ext": {
+              "bidder": {
+                "site_id": "108060",
+                "bidfloor": 0,
+                "tag_id": "mytag",
+                "secure": 1,
+                "position": 1,
+                "mimes": ["video/mp4"],
+                "api": [1,2],
+                "protocols": [2],
+                "maxduration": 60
+              }
+            }
+          }
+        ]
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "testauction",
+        "bidid": "c8d95f4b-bcbb-4a6c-adbb-4c7f33af3c24",
+        "cur": "USD",
+        "seatbid": [
+          {
+            "bid": [
+              {
+                "id": "1",
+                "impid": "1",
+                "price": 0.0340,
+                "nurl": "https:\/\/event.ad.cpe.dotomi.com\/cvx\/event\/imp?enc=eyJ1c2VyaWQiOiI3MTI3MDUzNzM3NTM3MTAzMjIiLCJwYXJ0bmVyVHhpZCI6ImUyZWUzNjZlLWEyMjgtNDI0Mi1hNjJlLTk4ODk3ODhiYzgxNCIsInR4aWQiOiI3MTE1NzQwNDg3NTczODUwMDIiLCJuZXR3b3JrUmVxdWVzdElkIjoiNzExNTc0MDQ4NzU3Mzg1ODc0Iiwic2lkIjoxMTgwOTgsImRpdmlzaW9uSWQiOjgsInRpZCI6OCwibW9iaWxlRGF0YSI6IjU5IiwiYmlkUHJpY2UiOjAuMDY4MCwicHViQ29zdCI6MC4wMzQwLCJwYXJ0bmVyRmVlIjowLjAxMzYsImlwU3RyaW5nIjoiNzMuMTE4LjEzMC4xODYiLCJzdXBwbHlUeXBlIjoxLCJpbnRlZ3JhdGlvblR5cGUiOjQsIm1lZGlhdGlvblR5cGUiOjEyNiwicGxhY2VtZW50SWQiOiIxMTY5ODcwIiwiaGVhZGVyQmlkIjoxLCJpc0RpcmVjdFB1Ymxpc2hlciI6MCwiaGFzQ29uc2VudCI6MSwib3BlcmF0aW9uIjoiQ0xJRU5UX0hFQURFUl8yNSIsImlzQ29yZVNoaWVsZCI6MCwicGFydG5lckNyZWF0aXZlSWQiOiIyNDk2NDRfMzAweDI1MCIsInBhcnRuZXJEb21haW5zIjpbIndhbG1hcnQuY29tIl0sInNlbGxlclJlcXVlc3RJZCI6ImE3ODcyMWQ3LWE2ZmUtNGJiNS1hNjFkLTFhMDg1MzkxZTVlZCIsInNlbGxlckltcElkIjoiMzAwNDIxZDY0NWY2ZjRjOWMifQ&",
+                "adm": "<div id='beacon_97896' style='position:absolute;left:0px;top:0px;visibility:hidden;'><img src='https:\/\/rtb-us-east1.openx.net\/win\/conversant?p=0.068000&t=2DAABBgABAAECAAIBAAsAAgAAAyIcGAozeG96d1E1ekpoHBbYlLq-s8_-0YUBFu-eiJTkjsmfjAEAHBb79r3dq-TkkToW19-h99Dds9GzAQAW0Nm29AsVBjgkNTIzZDJkNzgtYjc1Yi0wM2VmLTI1Y2MtMmM2MjJmYTlhN2ZhACwcFQIAHBUCABwVAgAAHCbEvIODBBUEFQQmvLyDgwQWruuBgwTWiAEWiAEWiAEWZBZkFmQWZBbUAQAcHCwWgLGKpLvsg60qFuuytdnn4oDnpQEAABam-p2ABBaopLKABBaIrIKBBBaC1NKABBUYHBT0AxTYBAAVBCbUARbUARbUARE1DibUAQAsLBaXzu2B6O7j1_ABFrHv8IeZkI7RzAEAFtDZtvQLBiim-p2ABBaopLKABBaC1NKABBaIrIKBBBgOMjQ5NjQ0XzMwMHgyNTAWjAwW1AElBBZMJQKWwMqMAhEoAk9YDHoU-AMU2gQAFgIYA3J0YoEA3BsEiApiaWRfc291cmNlBVNTUlRCCGJpZF91dWlkJDgzZTVlOGRlLThjNzktNGE4ZC05OTQyLThiOGFkYTc1ZDI2MhhkZWxpdmVyeV9tZWRpdW1fY2F0ZWdvcnkDV0VCIXJldHJpZXZlZF9mcm9tX3NzcnRiX2Vwb2NoX21pbGxpcw0xNTk4NDc3OTI5MDgxAKwYmwJodHRwczovL21wZC5teHB0aW50Lm5ldC8xL1MxL0cxL1Q0ODA2L2RpP3Npej0zMDB4MjUwJmNwZD0yNDk2NDQmY3BzPTI3NSZzc2w9MSZtaWQ9QjMzNkI0X0NGQTRBNTczXzUyRDA1REEmbXBpZD1SMUIzNDJfQzZFRjI3MUVfMTMxRjQwOTZEJmJwPTAuMTA2MDYzNCZkbT0xNDkzREIyMDZGN0M0MUQ2NTRERDI1OUZDRjk2RTVDNSZyaT0wJnY9cTcxbzlGS0d4VVpGNUlONUEycGU5QUpRSWVEYlVSSjFIQ2p2cmRBbmx3M1F2UTFuVkQtSWhnJmFkaWQ9MSZ1dWlkPUE4emxpcHVrLUJJTEZIcl90SDVoT1p3KAt3YWxtYXJ0LmNvbQAAAA&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8'\/><\/div><script type=\"text\/javascript\" src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/js?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\"><\/script><noscript><iframe src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/if?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\" width=\"300px\" height=\"250px\" marginwidth=\"0\" marginheight=\"0\" frameborder=\"0\" scrolling=\"no\"><img src=\"https:\/\/mpc.mxptint.net\/11S2SEECE726S6C47S3CF2CS12CSFAS12C6SB336B4_CFA4A573_52D05DAS%3fhttp:\/\/noredir.mxptint.net\"\/><\/iframe><\/noscript><iframe src='https:\/\/us-u.openx.net\/w\/1.0\/pd?plm=6&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8' width='0' height='0' style='display:none;'><\/iframe>",
+                "adomain": ["walmart.com"],
+                "crid": "249644_300x250",
+                "w": 300,
+                "h": 250
+              },
+              {
+                "id": "2",
+                "impid": "2",
+                "price": 0.0340,
+                "nurl": "https:\/\/event.ad.cpe.dotomi.com\/cvx\/event\/imp?enc=eyJ1c2VyaWQiOiI3MTI3MDUzNzM3NTM3MTAzMjIiLCJwYXJ0bmVyVHhpZCI6ImUyZWUzNjZlLWEyMjgtNDI0Mi1hNjJlLTk4ODk3ODhiYzgxNCIsInR4aWQiOiI3MTE1NzQwNDg3NTczODUwMDIiLCJuZXR3b3JrUmVxdWVzdElkIjoiNzExNTc0MDQ4NzU3Mzg1ODc0Iiwic2lkIjoxMTgwOTgsImRpdmlzaW9uSWQiOjgsInRpZCI6OCwibW9iaWxlRGF0YSI6IjU5IiwiYmlkUHJpY2UiOjAuMDY4MCwicHViQ29zdCI6MC4wMzQwLCJwYXJ0bmVyRmVlIjowLjAxMzYsImlwU3RyaW5nIjoiNzMuMTE4LjEzMC4xODYiLCJzdXBwbHlUeXBlIjoxLCJpbnRlZ3JhdGlvblR5cGUiOjQsIm1lZGlhdGlvblR5cGUiOjEyNiwicGxhY2VtZW50SWQiOiIxMTY5ODcwIiwiaGVhZGVyQmlkIjoxLCJpc0RpcmVjdFB1Ymxpc2hlciI6MCwiaGFzQ29uc2VudCI6MSwib3BlcmF0aW9uIjoiQ0xJRU5UX0hFQURFUl8yNSIsImlzQ29yZVNoaWVsZCI6MCwicGFydG5lckNyZWF0aXZlSWQiOiIyNDk2NDRfMzAweDI1MCIsInBhcnRuZXJEb21haW5zIjpbIndhbG1hcnQuY29tIl0sInNlbGxlclJlcXVlc3RJZCI6ImE3ODcyMWQ3LWE2ZmUtNGJiNS1hNjFkLTFhMDg1MzkxZTVlZCIsInNlbGxlckltcElkIjoiMzAwNDIxZDY0NWY2ZjRjOWMifQ&",
+                "adm": "<div id='beacon_97896' style='position:absolute;left:0px;top:0px;visibility:hidden;'><img src='https:\/\/rtb-us-east1.openx.net\/win\/conversant?p=0.068000&t=2DAABBgABAAECAAIBAAsAAgAAAyIcGAozeG96d1E1ekpoHBbYlLq-s8_-0YUBFu-eiJTkjsmfjAEAHBb79r3dq-TkkToW19-h99Dds9GzAQAW0Nm29AsVBjgkNTIzZDJkNzgtYjc1Yi0wM2VmLTI1Y2MtMmM2MjJmYTlhN2ZhACwcFQIAHBUCABwVAgAAHCbEvIODBBUEFQQmvLyDgwQWruuBgwTWiAEWiAEWiAEWZBZkFmQWZBbUAQAcHCwWgLGKpLvsg60qFuuytdnn4oDnpQEAABam-p2ABBaopLKABBaIrIKBBBaC1NKABBUYHBT0AxTYBAAVBCbUARbUARbUARE1DibUAQAsLBaXzu2B6O7j1_ABFrHv8IeZkI7RzAEAFtDZtvQLBiim-p2ABBaopLKABBaC1NKABBaIrIKBBBgOMjQ5NjQ0XzMwMHgyNTAWjAwW1AElBBZMJQKWwMqMAhEoAk9YDHoU-AMU2gQAFgIYA3J0YoEA3BsEiApiaWRfc291cmNlBVNTUlRCCGJpZF91dWlkJDgzZTVlOGRlLThjNzktNGE4ZC05OTQyLThiOGFkYTc1ZDI2MhhkZWxpdmVyeV9tZWRpdW1fY2F0ZWdvcnkDV0VCIXJldHJpZXZlZF9mcm9tX3NzcnRiX2Vwb2NoX21pbGxpcw0xNTk4NDc3OTI5MDgxAKwYmwJodHRwczovL21wZC5teHB0aW50Lm5ldC8xL1MxL0cxL1Q0ODA2L2RpP3Npej0zMDB4MjUwJmNwZD0yNDk2NDQmY3BzPTI3NSZzc2w9MSZtaWQ9QjMzNkI0X0NGQTRBNTczXzUyRDA1REEmbXBpZD1SMUIzNDJfQzZFRjI3MUVfMTMxRjQwOTZEJmJwPTAuMTA2MDYzNCZkbT0xNDkzREIyMDZGN0M0MUQ2NTRERDI1OUZDRjk2RTVDNSZyaT0wJnY9cTcxbzlGS0d4VVpGNUlONUEycGU5QUpRSWVEYlVSSjFIQ2p2cmRBbmx3M1F2UTFuVkQtSWhnJmFkaWQ9MSZ1dWlkPUE4emxpcHVrLUJJTEZIcl90SDVoT1p3KAt3YWxtYXJ0LmNvbQAAAA&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8'\/><\/div><script type=\"text\/javascript\" src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/js?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\"><\/script><noscript><iframe src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/if?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\" width=\"300px\" height=\"250px\" marginwidth=\"0\" marginheight=\"0\" frameborder=\"0\" scrolling=\"no\"><img src=\"https:\/\/mpc.mxptint.net\/11S2SEECE726S6C47S3CF2CS12CSFAS12C6SB336B4_CFA4A573_52D05DAS%3fhttp:\/\/noredir.mxptint.net\"\/><\/iframe><\/noscript><iframe src='https:\/\/us-u.openx.net\/w\/1.0\/pd?plm=6&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8' width='0' height='0' style='display:none;'><\/iframe>",
+                "adomain": ["walmart.com"],
+                "crid": "249644_300x250",
+                "w": 300,
+                "h": 250
+              },
+              {
+                "id": "3",
+                "impid": "3",
+                "price": 0.0340,
+                "nurl": "https:\/\/event.ad.cpe.dotomi.com\/cvx\/event\/imp?enc=eyJ1c2VyaWQiOiI3MTI3MDUzNzM3NTM3MTAzMjIiLCJwYXJ0bmVyVHhpZCI6ImUyZWUzNjZlLWEyMjgtNDI0Mi1hNjJlLTk4ODk3ODhiYzgxNCIsInR4aWQiOiI3MTE1NzQwNDg3NTczODUwMDIiLCJuZXR3b3JrUmVxdWVzdElkIjoiNzExNTc0MDQ4NzU3Mzg1ODc0Iiwic2lkIjoxMTgwOTgsImRpdmlzaW9uSWQiOjgsInRpZCI6OCwibW9iaWxlRGF0YSI6IjU5IiwiYmlkUHJpY2UiOjAuMDY4MCwicHViQ29zdCI6MC4wMzQwLCJwYXJ0bmVyRmVlIjowLjAxMzYsImlwU3RyaW5nIjoiNzMuMTE4LjEzMC4xODYiLCJzdXBwbHlUeXBlIjoxLCJpbnRlZ3JhdGlvblR5cGUiOjQsIm1lZGlhdGlvblR5cGUiOjEyNiwicGxhY2VtZW50SWQiOiIxMTY5ODcwIiwiaGVhZGVyQmlkIjoxLCJpc0RpcmVjdFB1Ymxpc2hlciI6MCwiaGFzQ29uc2VudCI6MSwib3BlcmF0aW9uIjoiQ0xJRU5UX0hFQURFUl8yNSIsImlzQ29yZVNoaWVsZCI6MCwicGFydG5lckNyZWF0aXZlSWQiOiIyNDk2NDRfMzAweDI1MCIsInBhcnRuZXJEb21haW5zIjpbIndhbG1hcnQuY29tIl0sInNlbGxlclJlcXVlc3RJZCI6ImE3ODcyMWQ3LWE2ZmUtNGJiNS1hNjFkLTFhMDg1MzkxZTVlZCIsInNlbGxlckltcElkIjoiMzAwNDIxZDY0NWY2ZjRjOWMifQ&",
+                "adm": "<div id='beacon_97896' style='position:absolute;left:0px;top:0px;visibility:hidden;'><img src='https:\/\/rtb-us-east1.openx.net\/win\/conversant?p=0.068000&t=2DAABBgABAAECAAIBAAsAAgAAAyIcGAozeG96d1E1ekpoHBbYlLq-s8_-0YUBFu-eiJTkjsmfjAEAHBb79r3dq-TkkToW19-h99Dds9GzAQAW0Nm29AsVBjgkNTIzZDJkNzgtYjc1Yi0wM2VmLTI1Y2MtMmM2MjJmYTlhN2ZhACwcFQIAHBUCABwVAgAAHCbEvIODBBUEFQQmvLyDgwQWruuBgwTWiAEWiAEWiAEWZBZkFmQWZBbUAQAcHCwWgLGKpLvsg60qFuuytdnn4oDnpQEAABam-p2ABBaopLKABBaIrIKBBBaC1NKABBUYHBT0AxTYBAAVBCbUARbUARbUARE1DibUAQAsLBaXzu2B6O7j1_ABFrHv8IeZkI7RzAEAFtDZtvQLBiim-p2ABBaopLKABBaC1NKABBaIrIKBBBgOMjQ5NjQ0XzMwMHgyNTAWjAwW1AElBBZMJQKWwMqMAhEoAk9YDHoU-AMU2gQAFgIYA3J0YoEA3BsEiApiaWRfc291cmNlBVNTUlRCCGJpZF91dWlkJDgzZTVlOGRlLThjNzktNGE4ZC05OTQyLThiOGFkYTc1ZDI2MhhkZWxpdmVyeV9tZWRpdW1fY2F0ZWdvcnkDV0VCIXJldHJpZXZlZF9mcm9tX3NzcnRiX2Vwb2NoX21pbGxpcw0xNTk4NDc3OTI5MDgxAKwYmwJodHRwczovL21wZC5teHB0aW50Lm5ldC8xL1MxL0cxL1Q0ODA2L2RpP3Npej0zMDB4MjUwJmNwZD0yNDk2NDQmY3BzPTI3NSZzc2w9MSZtaWQ9QjMzNkI0X0NGQTRBNTczXzUyRDA1REEmbXBpZD1SMUIzNDJfQzZFRjI3MUVfMTMxRjQwOTZEJmJwPTAuMTA2MDYzNCZkbT0xNDkzREIyMDZGN0M0MUQ2NTRERDI1OUZDRjk2RTVDNSZyaT0wJnY9cTcxbzlGS0d4VVpGNUlONUEycGU5QUpRSWVEYlVSSjFIQ2p2cmRBbmx3M1F2UTFuVkQtSWhnJmFkaWQ9MSZ1dWlkPUE4emxpcHVrLUJJTEZIcl90SDVoT1p3KAt3YWxtYXJ0LmNvbQAAAA&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8'\/><\/div><script type=\"text\/javascript\" src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/js?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\"><\/script><noscript><iframe src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/if?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\" width=\"300px\" height=\"250px\" marginwidth=\"0\" marginheight=\"0\" frameborder=\"0\" scrolling=\"no\"><img src=\"https:\/\/mpc.mxptint.net\/11S2SEECE726S6C47S3CF2CS12CSFAS12C6SB336B4_CFA4A573_52D05DAS%3fhttp:\/\/noredir.mxptint.net\"\/><\/iframe><\/noscript><iframe src='https:\/\/us-u.openx.net\/w\/1.0\/pd?plm=6&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8' width='0' height='0' style='display:none;'><\/iframe>",
+                "adomain": ["walmart.com"],
+                "crid": "249644_300x250",
+                "w": 300,
+                "h": 250
+              }
+            ]
+          }
+        ]
+      }
+    }
+  }],
+  "expectedBidResponses": [
+    {
+      "bids": [{
+          "bid": {
+            "id": "1",
+            "impid": "1",
+            "price": 0.0340,
+            "nurl": "https:\/\/event.ad.cpe.dotomi.com\/cvx\/event\/imp?enc=eyJ1c2VyaWQiOiI3MTI3MDUzNzM3NTM3MTAzMjIiLCJwYXJ0bmVyVHhpZCI6ImUyZWUzNjZlLWEyMjgtNDI0Mi1hNjJlLTk4ODk3ODhiYzgxNCIsInR4aWQiOiI3MTE1NzQwNDg3NTczODUwMDIiLCJuZXR3b3JrUmVxdWVzdElkIjoiNzExNTc0MDQ4NzU3Mzg1ODc0Iiwic2lkIjoxMTgwOTgsImRpdmlzaW9uSWQiOjgsInRpZCI6OCwibW9iaWxlRGF0YSI6IjU5IiwiYmlkUHJpY2UiOjAuMDY4MCwicHViQ29zdCI6MC4wMzQwLCJwYXJ0bmVyRmVlIjowLjAxMzYsImlwU3RyaW5nIjoiNzMuMTE4LjEzMC4xODYiLCJzdXBwbHlUeXBlIjoxLCJpbnRlZ3JhdGlvblR5cGUiOjQsIm1lZGlhdGlvblR5cGUiOjEyNiwicGxhY2VtZW50SWQiOiIxMTY5ODcwIiwiaGVhZGVyQmlkIjoxLCJpc0RpcmVjdFB1Ymxpc2hlciI6MCwiaGFzQ29uc2VudCI6MSwib3BlcmF0aW9uIjoiQ0xJRU5UX0hFQURFUl8yNSIsImlzQ29yZVNoaWVsZCI6MCwicGFydG5lckNyZWF0aXZlSWQiOiIyNDk2NDRfMzAweDI1MCIsInBhcnRuZXJEb21haW5zIjpbIndhbG1hcnQuY29tIl0sInNlbGxlclJlcXVlc3RJZCI6ImE3ODcyMWQ3LWE2ZmUtNGJiNS1hNjFkLTFhMDg1MzkxZTVlZCIsInNlbGxlckltcElkIjoiMzAwNDIxZDY0NWY2ZjRjOWMifQ&",
+            "adm": "<div id='beacon_97896' style='position:absolute;left:0px;top:0px;visibility:hidden;'><img src='https:\/\/rtb-us-east1.openx.net\/win\/conversant?p=0.068000&t=2DAABBgABAAECAAIBAAsAAgAAAyIcGAozeG96d1E1ekpoHBbYlLq-s8_-0YUBFu-eiJTkjsmfjAEAHBb79r3dq-TkkToW19-h99Dds9GzAQAW0Nm29AsVBjgkNTIzZDJkNzgtYjc1Yi0wM2VmLTI1Y2MtMmM2MjJmYTlhN2ZhACwcFQIAHBUCABwVAgAAHCbEvIODBBUEFQQmvLyDgwQWruuBgwTWiAEWiAEWiAEWZBZkFmQWZBbUAQAcHCwWgLGKpLvsg60qFuuytdnn4oDnpQEAABam-p2ABBaopLKABBaIrIKBBBaC1NKABBUYHBT0AxTYBAAVBCbUARbUARbUARE1DibUAQAsLBaXzu2B6O7j1_ABFrHv8IeZkI7RzAEAFtDZtvQLBiim-p2ABBaopLKABBaC1NKABBaIrIKBBBgOMjQ5NjQ0XzMwMHgyNTAWjAwW1AElBBZMJQKWwMqMAhEoAk9YDHoU-AMU2gQAFgIYA3J0YoEA3BsEiApiaWRfc291cmNlBVNTUlRCCGJpZF91dWlkJDgzZTVlOGRlLThjNzktNGE4ZC05OTQyLThiOGFkYTc1ZDI2MhhkZWxpdmVyeV9tZWRpdW1fY2F0ZWdvcnkDV0VCIXJldHJpZXZlZF9mcm9tX3NzcnRiX2Vwb2NoX21pbGxpcw0xNTk4NDc3OTI5MDgxAKwYmwJodHRwczovL21wZC5teHB0aW50Lm5ldC8xL1MxL0cxL1Q0ODA2L2RpP3Npej0zMDB4MjUwJmNwZD0yNDk2NDQmY3BzPTI3NSZzc2w9MSZtaWQ9QjMzNkI0X0NGQTRBNTczXzUyRDA1REEmbXBpZD1SMUIzNDJfQzZFRjI3MUVfMTMxRjQwOTZEJmJwPTAuMTA2MDYzNCZkbT0xNDkzREIyMDZGN0M0MUQ2NTRERDI1OUZDRjk2RTVDNSZyaT0wJnY9cTcxbzlGS0d4VVpGNUlONUEycGU5QUpRSWVEYlVSSjFIQ2p2cmRBbmx3M1F2UTFuVkQtSWhnJmFkaWQ9MSZ1dWlkPUE4emxpcHVrLUJJTEZIcl90SDVoT1p3KAt3YWxtYXJ0LmNvbQAAAA&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8'\/><\/div><script type=\"text\/javascript\" src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/js?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\"><\/script><noscript><iframe src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/if?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\" width=\"300px\" height=\"250px\" marginwidth=\"0\" marginheight=\"0\" frameborder=\"0\" scrolling=\"no\"><img src=\"https:\/\/mpc.mxptint.net\/11S2SEECE726S6C47S3CF2CS12CSFAS12C6SB336B4_CFA4A573_52D05DAS%3fhttp:\/\/noredir.mxptint.net\"\/><\/iframe><\/noscript><iframe src='https:\/\/us-u.openx.net\/w\/1.0\/pd?plm=6&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8' width='0' height='0' style='display:none;'><\/iframe>",
+            "adomain": ["walmart.com"],
+            "crid": "249644_300x250",
+            "w": 300,
+            "h": 250
+          },
+          "type": "video"
+        },
+        {
+          "bid": {
+            "id": "2",
+            "impid": "2",
+            "price": 0.0340,
+            "nurl": "https:\/\/event.ad.cpe.dotomi.com\/cvx\/event\/imp?enc=eyJ1c2VyaWQiOiI3MTI3MDUzNzM3NTM3MTAzMjIiLCJwYXJ0bmVyVHhpZCI6ImUyZWUzNjZlLWEyMjgtNDI0Mi1hNjJlLTk4ODk3ODhiYzgxNCIsInR4aWQiOiI3MTE1NzQwNDg3NTczODUwMDIiLCJuZXR3b3JrUmVxdWVzdElkIjoiNzExNTc0MDQ4NzU3Mzg1ODc0Iiwic2lkIjoxMTgwOTgsImRpdmlzaW9uSWQiOjgsInRpZCI6OCwibW9iaWxlRGF0YSI6IjU5IiwiYmlkUHJpY2UiOjAuMDY4MCwicHViQ29zdCI6MC4wMzQwLCJwYXJ0bmVyRmVlIjowLjAxMzYsImlwU3RyaW5nIjoiNzMuMTE4LjEzMC4xODYiLCJzdXBwbHlUeXBlIjoxLCJpbnRlZ3JhdGlvblR5cGUiOjQsIm1lZGlhdGlvblR5cGUiOjEyNiwicGxhY2VtZW50SWQiOiIxMTY5ODcwIiwiaGVhZGVyQmlkIjoxLCJpc0RpcmVjdFB1Ymxpc2hlciI6MCwiaGFzQ29uc2VudCI6MSwib3BlcmF0aW9uIjoiQ0xJRU5UX0hFQURFUl8yNSIsImlzQ29yZVNoaWVsZCI6MCwicGFydG5lckNyZWF0aXZlSWQiOiIyNDk2NDRfMzAweDI1MCIsInBhcnRuZXJEb21haW5zIjpbIndhbG1hcnQuY29tIl0sInNlbGxlclJlcXVlc3RJZCI6ImE3ODcyMWQ3LWE2ZmUtNGJiNS1hNjFkLTFhMDg1MzkxZTVlZCIsInNlbGxlckltcElkIjoiMzAwNDIxZDY0NWY2ZjRjOWMifQ&",
+            "adm": "<div id='beacon_97896' style='position:absolute;left:0px;top:0px;visibility:hidden;'><img src='https:\/\/rtb-us-east1.openx.net\/win\/conversant?p=0.068000&t=2DAABBgABAAECAAIBAAsAAgAAAyIcGAozeG96d1E1ekpoHBbYlLq-s8_-0YUBFu-eiJTkjsmfjAEAHBb79r3dq-TkkToW19-h99Dds9GzAQAW0Nm29AsVBjgkNTIzZDJkNzgtYjc1Yi0wM2VmLTI1Y2MtMmM2MjJmYTlhN2ZhACwcFQIAHBUCABwVAgAAHCbEvIODBBUEFQQmvLyDgwQWruuBgwTWiAEWiAEWiAEWZBZkFmQWZBbUAQAcHCwWgLGKpLvsg60qFuuytdnn4oDnpQEAABam-p2ABBaopLKABBaIrIKBBBaC1NKABBUYHBT0AxTYBAAVBCbUARbUARbUARE1DibUAQAsLBaXzu2B6O7j1_ABFrHv8IeZkI7RzAEAFtDZtvQLBiim-p2ABBaopLKABBaC1NKABBaIrIKBBBgOMjQ5NjQ0XzMwMHgyNTAWjAwW1AElBBZMJQKWwMqMAhEoAk9YDHoU-AMU2gQAFgIYA3J0YoEA3BsEiApiaWRfc291cmNlBVNTUlRCCGJpZF91dWlkJDgzZTVlOGRlLThjNzktNGE4ZC05OTQyLThiOGFkYTc1ZDI2MhhkZWxpdmVyeV9tZWRpdW1fY2F0ZWdvcnkDV0VCIXJldHJpZXZlZF9mcm9tX3NzcnRiX2Vwb2NoX21pbGxpcw0xNTk4NDc3OTI5MDgxAKwYmwJodHRwczovL21wZC5teHB0aW50Lm5ldC8xL1MxL0cxL1Q0ODA2L2RpP3Npej0zMDB4MjUwJmNwZD0yNDk2NDQmY3BzPTI3NSZzc2w9MSZtaWQ9QjMzNkI0X0NGQTRBNTczXzUyRDA1REEmbXBpZD1SMUIzNDJfQzZFRjI3MUVfMTMxRjQwOTZEJmJwPTAuMTA2MDYzNCZkbT0xNDkzREIyMDZGN0M0MUQ2NTRERDI1OUZDRjk2RTVDNSZyaT0wJnY9cTcxbzlGS0d4VVpGNUlONUEycGU5QUpRSWVEYlVSSjFIQ2p2cmRBbmx3M1F2UTFuVkQtSWhnJmFkaWQ9MSZ1dWlkPUE4emxpcHVrLUJJTEZIcl90SDVoT1p3KAt3YWxtYXJ0LmNvbQAAAA&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8'\/><\/div><script type=\"text\/javascript\" src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/js?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\"><\/script><noscript><iframe src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/if?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\" width=\"300px\" height=\"250px\" marginwidth=\"0\" marginheight=\"0\" frameborder=\"0\" scrolling=\"no\"><img src=\"https:\/\/mpc.mxptint.net\/11S2SEECE726S6C47S3CF2CS12CSFAS12C6SB336B4_CFA4A573_52D05DAS%3fhttp:\/\/noredir.mxptint.net\"\/><\/iframe><\/noscript><iframe src='https:\/\/us-u.openx.net\/w\/1.0\/pd?plm=6&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8' width='0' height='0' style='display:none;'><\/iframe>",
+            "adomain": ["walmart.com"],
+            "crid": "249644_300x250",
+            "w": 300,
+            "h": 250
+          },
+          "type": "video"
+        },
+        {
+        "bid": {
+          "id": "3",
+          "impid": "3",
+          "price": 0.0340,
+          "nurl": "https:\/\/event.ad.cpe.dotomi.com\/cvx\/event\/imp?enc=eyJ1c2VyaWQiOiI3MTI3MDUzNzM3NTM3MTAzMjIiLCJwYXJ0bmVyVHhpZCI6ImUyZWUzNjZlLWEyMjgtNDI0Mi1hNjJlLTk4ODk3ODhiYzgxNCIsInR4aWQiOiI3MTE1NzQwNDg3NTczODUwMDIiLCJuZXR3b3JrUmVxdWVzdElkIjoiNzExNTc0MDQ4NzU3Mzg1ODc0Iiwic2lkIjoxMTgwOTgsImRpdmlzaW9uSWQiOjgsInRpZCI6OCwibW9iaWxlRGF0YSI6IjU5IiwiYmlkUHJpY2UiOjAuMDY4MCwicHViQ29zdCI6MC4wMzQwLCJwYXJ0bmVyRmVlIjowLjAxMzYsImlwU3RyaW5nIjoiNzMuMTE4LjEzMC4xODYiLCJzdXBwbHlUeXBlIjoxLCJpbnRlZ3JhdGlvblR5cGUiOjQsIm1lZGlhdGlvblR5cGUiOjEyNiwicGxhY2VtZW50SWQiOiIxMTY5ODcwIiwiaGVhZGVyQmlkIjoxLCJpc0RpcmVjdFB1Ymxpc2hlciI6MCwiaGFzQ29uc2VudCI6MSwib3BlcmF0aW9uIjoiQ0xJRU5UX0hFQURFUl8yNSIsImlzQ29yZVNoaWVsZCI6MCwicGFydG5lckNyZWF0aXZlSWQiOiIyNDk2NDRfMzAweDI1MCIsInBhcnRuZXJEb21haW5zIjpbIndhbG1hcnQuY29tIl0sInNlbGxlclJlcXVlc3RJZCI6ImE3ODcyMWQ3LWE2ZmUtNGJiNS1hNjFkLTFhMDg1MzkxZTVlZCIsInNlbGxlckltcElkIjoiMzAwNDIxZDY0NWY2ZjRjOWMifQ&",
+          "adm": "<div id='beacon_97896' style='position:absolute;left:0px;top:0px;visibility:hidden;'><img src='https:\/\/rtb-us-east1.openx.net\/win\/conversant?p=0.068000&t=2DAABBgABAAECAAIBAAsAAgAAAyIcGAozeG96d1E1ekpoHBbYlLq-s8_-0YUBFu-eiJTkjsmfjAEAHBb79r3dq-TkkToW19-h99Dds9GzAQAW0Nm29AsVBjgkNTIzZDJkNzgtYjc1Yi0wM2VmLTI1Y2MtMmM2MjJmYTlhN2ZhACwcFQIAHBUCABwVAgAAHCbEvIODBBUEFQQmvLyDgwQWruuBgwTWiAEWiAEWiAEWZBZkFmQWZBbUAQAcHCwWgLGKpLvsg60qFuuytdnn4oDnpQEAABam-p2ABBaopLKABBaIrIKBBBaC1NKABBUYHBT0AxTYBAAVBCbUARbUARbUARE1DibUAQAsLBaXzu2B6O7j1_ABFrHv8IeZkI7RzAEAFtDZtvQLBiim-p2ABBaopLKABBaC1NKABBaIrIKBBBgOMjQ5NjQ0XzMwMHgyNTAWjAwW1AElBBZMJQKWwMqMAhEoAk9YDHoU-AMU2gQAFgIYA3J0YoEA3BsEiApiaWRfc291cmNlBVNTUlRCCGJpZF91dWlkJDgzZTVlOGRlLThjNzktNGE4ZC05OTQyLThiOGFkYTc1ZDI2MhhkZWxpdmVyeV9tZWRpdW1fY2F0ZWdvcnkDV0VCIXJldHJpZXZlZF9mcm9tX3NzcnRiX2Vwb2NoX21pbGxpcw0xNTk4NDc3OTI5MDgxAKwYmwJodHRwczovL21wZC5teHB0aW50Lm5ldC8xL1MxL0cxL1Q0ODA2L2RpP3Npej0zMDB4MjUwJmNwZD0yNDk2NDQmY3BzPTI3NSZzc2w9MSZtaWQ9QjMzNkI0X0NGQTRBNTczXzUyRDA1REEmbXBpZD1SMUIzNDJfQzZFRjI3MUVfMTMxRjQwOTZEJmJwPTAuMTA2MDYzNCZkbT0xNDkzREIyMDZGN0M0MUQ2NTRERDI1OUZDRjk2RTVDNSZyaT0wJnY9cTcxbzlGS0d4VVpGNUlONUEycGU5QUpRSWVEYlVSSjFIQ2p2cmRBbmx3M1F2UTFuVkQtSWhnJmFkaWQ9MSZ1dWlkPUE4emxpcHVrLUJJTEZIcl90SDVoT1p3KAt3YWxtYXJ0LmNvbQAAAA&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8'\/><\/div><script type=\"text\/javascript\" src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/js?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\"><\/script><noscript><iframe src=\"https:\/\/mpd.mxptint.net\/1\/S1\/G1\/T4806\/if?siz=300x250&cpd=249644&cps=275&ssl=1&mid=B336B4_CFA4A573_52D05DA&mpid=R1B342_C6EF271E_131F4096D&bp=0.1060634&dm=1493DB206F7C41D654DD259FCF96E5C5&ri=0&v=q71o9FKGxUZF5IN5A2pe9AJQIeDbURJ1HCjvrdAnlw3QvQ1nVD-Ihg&adid=1&uuid=A8zlipuk-BILFHr_tH5hOZw&spox=AAABdCy1ioekWBgw3-MoY-mUAAZXsF6Wi8M83w\" width=\"300px\" height=\"250px\" marginwidth=\"0\" marginheight=\"0\" frameborder=\"0\" scrolling=\"no\"><img src=\"https:\/\/mpc.mxptint.net\/11S2SEECE726S6C47S3CF2CS12CSFAS12C6SB336B4_CFA4A573_52D05DAS%3fhttp:\/\/noredir.mxptint.net\"\/><\/iframe><\/noscript><iframe src='https:\/\/us-u.openx.net\/w\/1.0\/pd?plm=6&ph=bec2690e-a73d-4d95-9901-75ad2a8d91b8' width='0' height='0' style='display:none;'><\/iframe>",
+          "adomain": ["walmart.com"],
+          "crid": "249644_300x250",
+          "w": 300,
+          "h": 250
+        },
+        "type": "video"
+      }
+      ]
+    }
+  ]
+}
diff --git a/adapters/ix/ix_test.go b/adapters/ix/ix_test.go
index bc70f399..d292273a 100644
--- a/adapters/ix/ix_test.go
+++ b/adapters/ix/ix_test.go
@@ -792,8 +792,8 @@ func TestIxMakeBidsWithCategoryDuration(t *testing.T) {
 	if bidResponse.Bids[0].BidVideo.Duration != expectedBidDuration {
 		t.Errorf("video duration should be set")
 	}
-	if bidResponse.Bids[0].BidVideo.PrimaryCategory != expectedBidCategory {
-		t.Errorf("video category should be set")
+	if bidResponse.Bids[0].Bid.Cat[0] != expectedBidCategory {
+		t.Errorf("bid category should be set")
 	}
 	if len(errors) != expectedErrorCount {
 		t.Errorf("should not have any errors, errors=%v", errors)
diff --git a/adapters/kayzen/kayzen_test.go b/adapters/kayzen/kayzen_test.go
new file mode 100644
index 00000000..4cd07470
--- /dev/null
+++ b/adapters/kayzen/kayzen_test.go
@@ -0,0 +1,29 @@
+package kayzen
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderKayzen, config.Adapter{
+		Endpoint: "https://example-{{.ZoneID}}.com/?exchange={{.AccountID}}",
+	})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "kayzentest", bidder)
+}
+
+func TestEndpointTemplateMalformed(t *testing.T) {
+	_, buildErr := Builder(openrtb_ext.BidderKayzen, config.Adapter{
+		Endpoint: "{{Malformed}}"})
+
+	assert.Error(t, buildErr)
+}
diff --git a/adapters/kayzen/kayzentest/exemplary/banner-app.json b/adapters/kayzen/kayzentest/exemplary/banner-app.json
new file mode 100644
index 00000000..157b7916
--- /dev/null
+++ b/adapters/kayzen/kayzentest/exemplary/banner-app.json
@@ -0,0 +1,142 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "banner": {
+          "w": 320,
+          "h": 50
+        },
+        "ext": {
+          "bidder": {
+            "zone": "dc",
+            "exchange": "ex"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "uri": "https://example-dc.com/?exchange=ex",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "banner": {
+                "w": 320,
+                "h": 50
+              }
+            }
+          ],
+          "app": {
+            "id": "123456789",
+            "name": "Awesome App",
+            "bundle": "com.app.awesome",
+            "domain": "awesomeapp.com",
+            "cat": [
+              "IAB22-1"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "0000-000-000-0000"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "1",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 320,
+                  "h": 50
+                }
+              ],
+              "type": "banner",
+              "seat": "kayzen-prebid"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "1",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "adomain": [
+              "awesome.com"
+            ],
+            "crid": "20",
+            "w": 320,
+            "h": 50
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/adapters/kayzen/kayzentest/exemplary/banner-web.json b/adapters/kayzen/kayzentest/exemplary/banner-web.json
new file mode 100644
index 00000000..0d25d4fd
--- /dev/null
+++ b/adapters/kayzen/kayzentest/exemplary/banner-web.json
@@ -0,0 +1,129 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "banner": {
+          "w":320,
+          "h":50
+        },
+        "ext": {
+          "bidder": {
+            "zone": "dc",
+            "exchange": "ex"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "uri": "https://example-dc.com/?exchange=ex",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "banner": {
+                "w":320,
+                "h":50
+              }
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "0000-000-000-0000"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "1",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 320,
+                  "h": 50
+                }
+              ],
+              "type": "banner",
+              "seat": "kayzen-prebid"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "1",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "crid": "20",
+            "adomain": [
+              "awesome.com"
+            ],
+            "w": 320,
+            "h": 50
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/kayzen/kayzentest/exemplary/native-app.json b/adapters/kayzen/kayzentest/exemplary/native-app.json
new file mode 100644
index 00000000..ef43dab7
--- /dev/null
+++ b/adapters/kayzen/kayzentest/exemplary/native-app.json
@@ -0,0 +1,137 @@
+{
+    "mockBidRequest": {
+      "id": "some-request-id",
+      "device": {
+        "ua": "test-user-agent",
+        "ip": "123.123.123.123",
+        "language": "en",
+        "dnt": 0
+      },
+      "tmax": 1000,
+      "user": {
+        "buyeruid": "0000-000-000-0000"
+      },
+      "app": {
+        "publisher": {
+          "id": "123456789"
+        },
+        "cat": [
+          "IAB22-1"
+        ],
+        "bundle": "com.app.awesome",
+        "name": "Awesome App",
+        "domain": "awesomeapp.com",
+        "id": "123456789"
+      },
+      "imp": [
+        {
+          "id": "some-impression-id",
+          "native": {
+            "ver":"1.1",
+            "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+          },
+          "ext": {
+            "bidder": {
+              "zone": "dc",
+              "exchange": "ex"
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "headers": {
+            "Content-Type": [
+              "application/json;charset=utf-8"
+            ],
+            "Accept": [
+              "application/json"
+            ]
+          },
+          "uri": "https://example-dc.com/?exchange=ex",
+          "body": {
+            "id": "some-request-id",
+            "device": {
+              "ua": "test-user-agent",
+              "ip": "123.123.123.123",
+              "language": "en",
+              "dnt": 0
+            },
+            "imp": [
+              {
+                "id": "some-impression-id",
+                "native": {
+                  "ver":"1.1",
+                  "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+                }
+              }
+            ],
+            "app": {
+              "id": "123456789",
+              "name": "Awesome App",
+              "bundle": "com.app.awesome",
+              "domain": "awesomeapp.com",
+              "cat": [
+                "IAB22-1"
+              ],
+              "publisher": {
+                "id": "123456789"
+              }
+            },
+            "user": {
+              "buyeruid": "0000-000-000-0000"
+            },
+            "tmax": 1000
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "id": "awesome-resp-id",
+            "seatbid": [
+              {
+                "bid": [
+                  {
+                    "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                    "impid": "some-impression-id",
+                    "price": 3.5,
+                    "adm": "awesome-markup",
+                    "adomain": [
+                      "awesome.com"
+                    ],
+                    "crid": "20"
+                  }
+                ],
+                "type": "native",
+                "seat": "kayzen-prebid"
+              }
+            ],
+            "cur": "USD",
+            "ext": {
+            }
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "bids":[
+          {
+            "bid":  {
+              "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+              "impid": "some-impression-id",
+              "price": 3.5,
+              "adm": "awesome-markup",
+              "crid": "20",
+              "adomain": [
+                "awesome.com"
+              ]
+            },
+            "type": "native"
+          }
+        ]
+      }
+    ]
+  }
diff --git a/adapters/kayzen/kayzentest/exemplary/native-web.json b/adapters/kayzen/kayzentest/exemplary/native-web.json
new file mode 100644
index 00000000..34050f98
--- /dev/null
+++ b/adapters/kayzen/kayzentest/exemplary/native-web.json
@@ -0,0 +1,124 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "native": {
+          "ver":"1.1",
+          "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+        },
+        "ext": {
+          "bidder": {
+            "zone": "dc",
+            "exchange": "ex"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "uri": "https://example-dc.com/?exchange=ex",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "native": {
+                "ver":"1.1",
+                "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+              }
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "0000-000-000-0000"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20"
+                }
+              ],
+              "seat": "kayzen-prebid"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "crid": "20",
+            "adomain": [
+              "awesome.com"
+            ]
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/kayzen/kayzentest/exemplary/video-app.json b/adapters/kayzen/kayzentest/exemplary/video-app.json
new file mode 100644
index 00000000..f8744b63
--- /dev/null
+++ b/adapters/kayzen/kayzentest/exemplary/video-app.json
@@ -0,0 +1,150 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "zone": "dc",
+            "exchange": "ex"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "uri": "https://example-dc.com/?exchange=ex",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              }
+            }
+          ],
+          "app": {
+            "id": "123456789",
+            "name": "Awesome App",
+            "bundle": "com.app.awesome",
+            "domain": "awesomeapp.com",
+            "cat": [
+              "IAB22-1"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "0000-000-000-0000"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 1280,
+                  "h": 720
+                }
+              ],
+              "seat": "kayzen-prebid"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "crid": "20",
+            "adomain": [
+              "awesome.com"
+            ],
+            "w": 1280,
+            "h": 720
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/kayzen/kayzentest/exemplary/video-web.json b/adapters/kayzen/kayzentest/exemplary/video-web.json
new file mode 100644
index 00000000..7a507ca3
--- /dev/null
+++ b/adapters/kayzen/kayzentest/exemplary/video-web.json
@@ -0,0 +1,149 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "zone": "dc",
+            "exchange": "ex"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "uri": "https://example-dc.com/?exchange=ex",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              }
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "0000-000-000-0000"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 1280,
+                  "h": 720,
+                  "ext": {
+                    "prebid": {
+                      "type": "video"
+                    }
+                  }
+                }
+              ],
+              "seat": "kayzen-prebid"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "adomain": [
+              "awesome.com"
+            ],
+            "crid": "20",
+            "w": 1280,
+            "h": 720,
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/adapters/kayzen/kayzentest/params/race/banner.json b/adapters/kayzen/kayzentest/params/race/banner.json
new file mode 100644
index 00000000..bdb2c9de
--- /dev/null
+++ b/adapters/kayzen/kayzentest/params/race/banner.json
@@ -0,0 +1,5 @@
+{
+  "zone": "dc",
+  "exchange": "ex"
+}
+
diff --git a/adapters/kayzen/kayzentest/params/race/native.json b/adapters/kayzen/kayzentest/params/race/native.json
new file mode 100644
index 00000000..bdb2c9de
--- /dev/null
+++ b/adapters/kayzen/kayzentest/params/race/native.json
@@ -0,0 +1,5 @@
+{
+  "zone": "dc",
+  "exchange": "ex"
+}
+
diff --git a/adapters/kayzen/kayzentest/params/race/video.json b/adapters/kayzen/kayzentest/params/race/video.json
new file mode 100644
index 00000000..bdb2c9de
--- /dev/null
+++ b/adapters/kayzen/kayzentest/params/race/video.json
@@ -0,0 +1,5 @@
+{
+  "zone": "dc",
+  "exchange": "ex"
+}
+
diff --git a/adapters/kayzen/kayzentest/supplemental/invalid-ext-object.json b/adapters/kayzen/kayzentest/supplemental/invalid-ext-object.json
new file mode 100644
index 00000000..2dda068b
--- /dev/null
+++ b/adapters/kayzen/kayzentest/supplemental/invalid-ext-object.json
@@ -0,0 +1,29 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Bidder extension not provided or can't be unmarshalled",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "my-adcode",
+        "video": {
+          "mimes": ["video/mp4"],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": ""
+      }
+    ],
+    "site": {
+      "page": "test.com"
+    }
+  },
+  "httpCalls": []
+}
diff --git a/adapters/kayzen/kayzentest/supplemental/invalid-response.json b/adapters/kayzen/kayzentest/supplemental/invalid-response.json
new file mode 100644
index 00000000..4963ed67
--- /dev/null
+++ b/adapters/kayzen/kayzentest/supplemental/invalid-response.json
@@ -0,0 +1,101 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "banner": {
+          "w":320,
+          "h":50
+        },
+        "ext": {
+          "bidder": {
+            "zone": "dc",
+            "exchange": "ex"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "headers": {
+        "Content-Type": [
+          "application/json;charset=utf-8"
+        ],
+        "Accept": [
+          "application/json"
+        ]
+      },
+      "uri": "https://example-dc.com/?exchange=ex",
+      "body": {
+        "id": "some-request-id",
+        "device": {
+          "ua": "test-user-agent",
+          "ip": "123.123.123.123",
+          "language": "en",
+          "dnt": 0
+        },
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "banner": {
+                "w":320,
+                "h":50
+            }
+          }
+        ],
+        "app": {
+          "id": "123456789",
+          "name": "Awesome App",
+          "bundle": "com.app.awesome",
+          "domain": "awesomeapp.com",
+          "cat": [
+            "IAB22-1"
+          ],
+          "publisher": {
+            "id": "123456789"
+          }
+        },
+        "user": {
+          "buyeruid": "0000-000-000-0000"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": "invalid response"
+    }
+  }],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bad Server Response",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/kayzen/kayzentest/supplemental/requires-imp-object.json b/adapters/kayzen/kayzentest/supplemental/requires-imp-object.json
new file mode 100644
index 00000000..1fcf1b48
--- /dev/null
+++ b/adapters/kayzen/kayzentest/supplemental/requires-imp-object.json
@@ -0,0 +1,16 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Missing Imp Object",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [],
+    "site": {
+      "page": "test.com"
+    }
+  },
+  "httpCalls": []
+}
diff --git a/adapters/kayzen/kayzentest/supplemental/status-code-bad-request.json b/adapters/kayzen/kayzentest/supplemental/status-code-bad-request.json
new file mode 100644
index 00000000..8b9e4d06
--- /dev/null
+++ b/adapters/kayzen/kayzentest/supplemental/status-code-bad-request.json
@@ -0,0 +1,91 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "zone": "dc",
+            "exchange": "ex"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://example-dc.com/?exchange=ex",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 120,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            }
+          }
+        ],
+        "app":  {
+          "publisher": {
+            "id": "123456789"
+          },
+          "cat": [
+            "IAB22-1"
+          ],
+          "bundle": "com.app.awesome",
+          "name": "Awesome App",
+          "domain": "awesomeapp.com",
+          "id": "123456789"
+        },
+        "user": {
+          "buyeruid": "0000-000-000-0000"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 400
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/kayzen/kayzentest/supplemental/status-code-no-content.json b/adapters/kayzen/kayzentest/supplemental/status-code-no-content.json
new file mode 100644
index 00000000..69e64c37
--- /dev/null
+++ b/adapters/kayzen/kayzentest/supplemental/status-code-no-content.json
@@ -0,0 +1,72 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "video": {
+          "mimes": ["video/mp4"],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "zone": "dc",
+            "exchange": "ex"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://example-dc.com/?exchange=ex",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 120,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            }
+          }
+        ],
+        "site": {
+          "page": "test.com",
+          "publisher": {
+            "id": "123456789"
+          }
+        },
+        "user": {
+          "buyeruid": "0000-000-000-0000"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 204
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": []
+}
diff --git a/adapters/kayzen/kayzentest/supplemental/status-code-other-error.json b/adapters/kayzen/kayzentest/supplemental/status-code-other-error.json
new file mode 100644
index 00000000..b487b91d
--- /dev/null
+++ b/adapters/kayzen/kayzentest/supplemental/status-code-other-error.json
@@ -0,0 +1,77 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "video": {
+          "mimes": ["video/mp4"],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "zone": "dc",
+            "exchange": "ex"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://example-dc.com/?exchange=ex",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 120,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            }
+          }
+        ],
+        "site": {
+          "page": "test.com",
+          "publisher": {
+            "id": "123456789"
+          }
+        },
+        "user": {
+          "buyeruid": "0000-000-000-0000"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 306
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 306. Run with request.debug = 1 for more info.",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/kayzen/params_test.go b/adapters/kayzen/params_test.go
new file mode 100644
index 00000000..07bd0851
--- /dev/null
+++ b/adapters/kayzen/params_test.go
@@ -0,0 +1,55 @@
+package kayzen
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+var validParams = []string{
+	`{ "zone": "dc", "exchange": "ex" }`,
+}
+
+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.BidderKayzen, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected Kayzen params: %s", validParam)
+		}
+	}
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{ "key": 2 }`,
+	`{ "anyparam": "anyvalue" }`,
+	`{ "zone": "dc" }`,
+	`{ "exchange": "ex" }`,
+	`{ "exchange": "", "zone" : "" }`,
+	`{ "exchange": "ex", "zone" : "" }`,
+	`{ "exchange": "", "zone" : "dc" }`,
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderKayzen, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
diff --git a/adapters/madvertise/madvertise_test.go b/adapters/madvertise/madvertise_test.go
new file mode 100644
index 00000000..924f25c1
--- /dev/null
+++ b/adapters/madvertise/madvertise_test.go
@@ -0,0 +1,26 @@
+package madvertise
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestEndpointTemplateMalformed(t *testing.T) {
+	_, buildErr := Builder(openrtb_ext.BidderMadvertise, config.Adapter{
+		Endpoint: "{{Malformed}}"})
+
+	assert.Error(t, buildErr)
+}
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderMadvertise, config.Adapter{
+		Endpoint: "https://mobile.mng-ads.com/bidrequest{{.ZoneID}}"})
+
+	assert.NoError(t, buildErr, "Builder returned unexpected error %v", buildErr)
+
+	adapterstest.RunJSONBidderTest(t, "madvertisetest", bidder)
+}
diff --git a/adapters/madvertise/madvertisetest/exemplary/simple-banner.json b/adapters/madvertise/madvertisetest/exemplary/simple-banner.json
new file mode 100644
index 00000000..0c277a06
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/exemplary/simple-banner.json
@@ -0,0 +1,203 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "tmax": 500,
+        "at": 1,
+        "cur": [
+            "EUR"
+        ],
+        "regs": {
+            "ext": {
+                "gdpr": 1
+            }
+        },
+        "user": {
+            "ext": {
+                "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+            }
+        },
+        "app": {
+            "id": "9561622",
+            "bundle": "com.madvertise.bluestack",
+            "name": "FR_BlueStack_App_Android_MNG",
+            "storeurl": "https://play.google.com/store/apps/details?id=com.madvertise.bluestack"
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "test-user-agent",
+            "dnt": 0,
+            "lmt": 0,
+            "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+            "make": "Sony",
+            "model": "G8441",
+            "os": "Android",
+            "osv": "9.0",
+            "devicetype": 4,
+            "connectiontype": 6,
+            "js": 1,
+            "carrier": "Free",
+            "geo": {
+                "type": 2,
+                "country": "FRA"
+            }
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "secure": 1,
+                "bidfloor": 3,
+                "bidfloorcur": "EUR",
+                "instl": 0,
+                "banner": {
+                    "h": 50,
+                    "w": 320,
+                    "pos": 1
+                },
+                "ext": {
+                    "bidder": {
+                        "zoneId": "/1111111/banner"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://mobile.mng-ads.com/bidrequest/1111111/banner",
+                "headers": {
+                    "Content-Type": [
+                        "application/json;charset=utf-8"
+                    ],
+                    "Accept": [
+                        "application/json"
+                    ],
+                    "X-Openrtb-Version": [
+                        "2.5"
+                    ],
+                    "User-Agent": [
+                        "test-user-agent"
+                    ],
+                    "X-Forwarded-For": [
+                        "123.123.123.123"
+                    ]
+                },
+                "body": {
+                    "id": "test-request-id",
+                    "tmax": 500,
+                    "at": 1,
+                    "cur": [
+                        "EUR"
+                    ],
+                    "regs": {
+                        "ext": {
+                            "gdpr": 1
+                        }
+                    },
+                    "user": {
+                        "ext": {
+                            "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+                        }
+                    },
+                    "app": {
+                        "id": "9561622",
+                        "bundle": "com.madvertise.bluestack",
+                        "name": "FR_BlueStack_App_Android_MNG",
+                        "storeurl": "https://play.google.com/store/apps/details?id=com.madvertise.bluestack"
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "test-user-agent",
+                        "dnt": 0,
+                        "lmt": 0,
+                        "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+                        "make": "Sony",
+                        "model": "G8441",
+                        "os": "Android",
+                        "osv": "9.0",
+                        "devicetype": 4,
+                        "connectiontype": 6,
+                        "js": 1,
+                        "carrier": "Free",
+                        "geo": {
+                            "type": 2,
+                            "country": "FRA"
+                        }
+                    },
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "secure": 1,
+                            "bidfloor": 3,
+                            "bidfloorcur": "EUR",
+                            "banner": {
+                                "h": 50,
+                                "w": 320,
+                                "pos": 1
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "zoneId": "/1111111/banner"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "some-test-ad",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "burl": "https://burl.com",
+                                    "adomain": [
+                                        "madvertise.com"
+                                    ],
+                                    "w": 320,
+                                    "h": 50
+                                }
+                            ],
+                            "seat": "adman"
+                        }
+                    ],
+                    "cur": "EUR"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "EUR",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "some-test-ad",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "burl": "https://burl.com",
+                        "adomain": [
+                            "madvertise.com"
+                        ],
+                        "w": 320,
+                        "h": 50
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/madvertise/madvertisetest/exemplary/simple-video.json b/adapters/madvertise/madvertisetest/exemplary/simple-video.json
new file mode 100644
index 00000000..a387c06e
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/exemplary/simple-video.json
@@ -0,0 +1,256 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "tmax": 500,
+        "at": 2,
+        "cur": [
+            "EUR"
+        ],
+        "regs": {
+            "ext": {
+                "gdpr": 1
+            }
+        },
+        "user": {
+            "ext": {
+                "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+            }
+        },
+        "app": {
+            "id": "9561622",
+            "bundle": "com.madvertise.bluestack",
+            "name": "FR_BlueStack_App_Android_MNG",
+            "storeurl": "https://play.google.com/store/apps/details?id=com.madvertise.bluestack",
+            "cat": [
+                "IAB6",
+                "IAB9",
+                "IAB10",
+                "IAB12",
+                "IAB14",
+                "IAB20"
+            ]
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "test-user-agent",
+            "dnt": 0,
+            "lmt": 0,
+            "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+            "make": "Sony",
+            "model": "G8441",
+            "os": "Android",
+            "osv": "9.0",
+            "devicetype": 4,
+            "connectiontype": 6,
+            "js": 1,
+            "carrier": "Free",
+            "geo": {
+                "type": 2,
+                "country": "FRA"
+            }
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "secure": 1,
+                "bidfloor": 3,
+                "bidfloorcur": "EUR",
+                "instl": 0,
+                "video": {
+                    "mimes": [
+                        "video/mp4"
+                    ],
+                    "w": 320,
+                    "h": 480,
+                    "minduration": 2,
+                    "maxduration": 30,
+                    "playbackmethod": [
+                        1,
+                        3
+                    ],
+                    "boxingallowed": 0,
+                    "protocols": [
+                        1,
+                        2,
+                        3,
+                        4,
+                        5,
+                        6
+                    ],
+                    "placement": 5
+                },
+                "ext": {
+                    "bidder": {
+                        "zoneId": "/1111111/video"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://mobile.mng-ads.com/bidrequest/1111111/video",
+                "headers": {
+                    "Content-Type": [
+                        "application/json;charset=utf-8"
+                    ],
+                    "Accept": [
+                        "application/json"
+                    ],
+                    "X-Openrtb-Version": [
+                        "2.5"
+                    ],
+                    "User-Agent": [
+                        "test-user-agent"
+                    ],
+                    "X-Forwarded-For": [
+                        "123.123.123.123"
+                    ]
+                },
+                "body": {
+                    "id": "test-request-id",
+                    "tmax": 500,
+                    "at": 2,
+                    "cur": [
+                        "EUR"
+                    ],
+                    "regs": {
+                        "ext": {
+                            "gdpr": 1
+                        }
+                    },
+                    "user": {
+                        "ext": {
+                            "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+                        }
+                    },
+                    "app": {
+                        "id": "9561622",
+                        "bundle": "com.madvertise.bluestack",
+                        "name": "FR_BlueStack_App_Android_MNG",
+                        "storeurl": "https://play.google.com/store/apps/details?id=com.madvertise.bluestack",
+                        "cat": [
+                            "IAB6",
+                            "IAB9",
+                            "IAB10",
+                            "IAB12",
+                            "IAB14",
+                            "IAB20"
+                        ]
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "test-user-agent",
+                        "dnt": 0,
+                        "lmt": 0,
+                        "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+                        "make": "Sony",
+                        "model": "G8441",
+                        "os": "Android",
+                        "osv": "9.0",
+                        "devicetype": 4,
+                        "connectiontype": 6,
+                        "js": 1,
+                        "carrier": "Free",
+                        "geo": {
+                            "type": 2,
+                            "country": "FRA"
+                        }
+                    },
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "secure": 1,
+                            "bidfloor": 3,
+                            "bidfloorcur": "EUR",
+                            "video": {
+                                "mimes": [
+                                    "video/mp4"
+                                ],
+                                "w": 320,
+                                "h": 480,
+                                "minduration": 2,
+                                "maxduration": 30,
+                                "playbackmethod": [
+                                    1,
+                                    3
+                                ],
+                                "protocols": [
+                                    1,
+                                    2,
+                                    3,
+                                    4,
+                                    5,
+                                    6
+                                ],
+                                "placement": 5
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "zoneId": "/1111111/video"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "some-test-ad",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "burl": "https://burl.com",
+                                    "attr": [6],
+                                    "adomain": [
+                                        "madvertise.com"
+                                    ],
+                                    "w": 320,
+                                    "h": 480
+                                }
+                            ],
+                            "seat": "adman"
+                        }
+                    ],
+                    "cur": "EUR"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "EUR",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "some-test-ad",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "burl": "https://burl.com",
+                        "attr": [6],
+                        "adomain": [
+                            "madvertise.com"
+                        ],
+                        "w": 320,
+                        "h": 480
+                    },
+                    "type": "video"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/madvertise/madvertisetest/params/race/banner.json b/adapters/madvertise/madvertisetest/params/race/banner.json
new file mode 100644
index 00000000..45243896
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/params/race/banner.json
@@ -0,0 +1,4 @@
+{
+  "zoneId": "/1111111/banner"
+}
+  
\ No newline at end of file
diff --git a/adapters/madvertise/madvertisetest/params/race/video.json b/adapters/madvertise/madvertisetest/params/race/video.json
new file mode 100644
index 00000000..8d462a8e
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/params/race/video.json
@@ -0,0 +1,3 @@
+{
+  "zoneId": "/1111111/video"
+}
\ No newline at end of file
diff --git a/adapters/madvertise/madvertisetest/supplemental/display-site-test.json b/adapters/madvertise/madvertisetest/supplemental/display-site-test.json
new file mode 100644
index 00000000..e111a5d9
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/supplemental/display-site-test.json
@@ -0,0 +1,203 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "tmax": 500,
+        "at": 1,
+        "cur": [
+            "EUR"
+        ],
+        "regs": {
+            "ext": {
+                "gdpr": 1
+            }
+        },
+        "user": {
+            "ext": {
+                "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+            }
+        },
+        "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "test-user-agent",
+            "dnt": 0,
+            "lmt": 0,
+            "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+            "make": "Sony",
+            "model": "G8441",
+            "os": "Android",
+            "osv": "9.0",
+            "devicetype": 4,
+            "connectiontype": 6,
+            "js": 1,
+            "carrier": "Free",
+            "geo": {
+                "type": 2,
+                "country": "FRA"
+            }
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "secure": 1,
+                "bidfloor": 3,
+                "bidfloorcur": "EUR",
+                "instl": 0,
+                "banner": {
+                    "h": 50,
+                    "w": 320,
+                    "pos": 1
+                },
+                "ext": {
+                    "bidder": {
+                        "zoneId": "/1111111/banner"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://mobile.mng-ads.com/bidrequest/1111111/banner",
+                "headers": {
+                    "Content-Type": [
+                        "application/json;charset=utf-8"
+                    ],
+                    "Accept": [
+                        "application/json"
+                    ],
+                    "X-Openrtb-Version": [
+                        "2.5"
+                    ],
+                    "User-Agent": [
+                        "test-user-agent"
+                    ],
+                    "X-Forwarded-For": [
+                        "123.123.123.123"
+                    ]
+                },
+                "body": {
+                    "id": "test-request-id",
+                    "tmax": 500,
+                    "at": 1,
+                    "cur": [
+                        "EUR"
+                    ],
+                    "regs": {
+                        "ext": {
+                            "gdpr": 1
+                        }
+                    },
+                    "user": {
+                        "ext": {
+                            "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+                        }
+                    },
+                    "site": {
+                        "page": "test.com",
+                        "publisher": {
+                          "id": "123456789"
+                        }
+                      },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "test-user-agent",
+                        "dnt": 0,
+                        "lmt": 0,
+                        "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+                        "make": "Sony",
+                        "model": "G8441",
+                        "os": "Android",
+                        "osv": "9.0",
+                        "devicetype": 4,
+                        "connectiontype": 6,
+                        "js": 1,
+                        "carrier": "Free",
+                        "geo": {
+                            "type": 2,
+                            "country": "FRA"
+                        }
+                    },
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "secure": 1,
+                            "bidfloor": 3,
+                            "bidfloorcur": "EUR",
+                            "banner": {
+                                "h": 50,
+                                "w": 320,
+                                "pos": 1
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "zoneId": "/1111111/banner"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "some-test-ad",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "burl": "https://burl.com",
+                                    "adomain": [
+                                        "madvertise.com"
+                                    ],
+                                    "w": 320,
+                                    "h": 50
+                                }
+                            ],
+                            "seat": "adman"
+                        }
+                    ],
+                    "cur": "EUR"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "EUR",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "some-test-ad",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "burl": "https://burl.com",
+                        "adomain": [
+                            "madvertise.com"
+                        ],
+                        "w": 320,
+                        "h": 50
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/madvertise/madvertisetest/supplemental/length-zoneid.json b/adapters/madvertise/madvertisetest/supplemental/length-zoneid.json
new file mode 100644
index 00000000..9d78bc85
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/supplemental/length-zoneid.json
@@ -0,0 +1,29 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id-1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "zoneId": "/22222"
+                    }
+                }
+            }
+        ]
+    },
+    "expectedMakeRequestsErrors": [
+        {
+            "value": "The minLength of zone ID is 7; ImpID=test-imp-id-1",
+            "comparison": "literal"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/madvertise/madvertisetest/supplemental/required-ext.json b/adapters/madvertise/madvertisetest/supplemental/required-ext.json
new file mode 100644
index 00000000..8989a8dc
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/supplemental/required-ext.json
@@ -0,0 +1,19 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id-1",
+        "banner": {
+          "format": [{"w": 300, "h": 250}]
+        }
+      }
+    ]
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "unexpected end of JSON input; ImpID=test-imp-id-1",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/madvertise/madvertisetest/supplemental/required-zoneid-.json b/adapters/madvertise/madvertisetest/supplemental/required-zoneid-.json
new file mode 100644
index 00000000..bfff193d
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/supplemental/required-zoneid-.json
@@ -0,0 +1,27 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id-1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {}
+                }
+            }
+        ]
+    },
+    "expectedMakeRequestsErrors": [
+        {
+            "value": "ext.bidder.zoneId not provided; ImpID=test-imp-id-1",
+            "comparison": "literal"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/madvertise/madvertisetest/supplemental/response-204.json b/adapters/madvertise/madvertisetest/supplemental/response-204.json
new file mode 100644
index 00000000..f0162291
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/supplemental/response-204.json
@@ -0,0 +1,189 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "tmax": 500,
+        "at": 2,
+        "cur": [
+            "EUR"
+        ],
+        "regs": {
+            "ext": {
+                "gdpr": 1
+            }
+        },
+        "user": {
+            "ext": {
+                "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+            }
+        },
+        "app": {
+            "id": "9561622",
+            "bundle": "com.madvertise.bluestack",
+            "name": "FR_BlueStack_App_Android_MNG",
+            "storeurl": "https://play.google.com/store/apps/details?id=com.madvertise.bluestack",
+            "cat": [
+                "IAB6",
+                "IAB9",
+                "IAB10",
+                "IAB12",
+                "IAB14",
+                "IAB20"
+            ]
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "test-user-agent",
+            "dnt": 0,
+            "lmt": 0,
+            "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+            "make": "Sony",
+            "model": "G8441",
+            "os": "Android",
+            "osv": "9.0",
+            "devicetype": 4,
+            "connectiontype": 6,
+            "js": 1,
+            "carrier": "Free",
+            "geo": {
+                "type": 2,
+                "country": "FRA"
+            }
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "secure": 1,
+                "bidfloor": 3,
+                "bidfloorcur": "EUR",
+                "instl": 0,
+                "banner": {
+                    "format": [
+                        {
+                            "w": 320,
+                            "h": 50
+                        }
+                    ],
+                    "w": 320,
+                    "h": 50,
+                    "api": [
+                        3,
+                        5,
+                        6
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "zoneId": "/1111111/banner"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://mobile.mng-ads.com/bidrequest/1111111/banner",
+                "headers": {
+                    "Content-Type": [
+                        "application/json;charset=utf-8"
+                    ],
+                    "Accept": [
+                        "application/json"
+                    ],
+                    "X-Openrtb-Version": [
+                        "2.5"
+                    ],
+                    "User-Agent": [
+                        "test-user-agent"
+                    ],
+                    "X-Forwarded-For": [
+                        "123.123.123.123"
+                    ]
+                },
+                "body": {
+                    "id": "test-request-id",
+                    "tmax": 500,
+                    "at": 2,
+                    "cur": [
+                        "EUR"
+                    ],
+                    "regs": {
+                        "ext": {
+                            "gdpr": 1
+                        }
+                    },
+                    "user": {
+                        "ext": {
+                            "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+                        }
+                    },
+                    "app": {
+                        "id": "9561622",
+                        "bundle": "com.madvertise.bluestack",
+                        "name": "FR_BlueStack_App_Android_MNG",
+                        "storeurl": "https://play.google.com/store/apps/details?id=com.madvertise.bluestack",
+                        "cat": [
+                            "IAB6",
+                            "IAB9",
+                            "IAB10",
+                            "IAB12",
+                            "IAB14",
+                            "IAB20"
+                        ]
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "test-user-agent",
+                        "dnt": 0,
+                        "lmt": 0,
+                        "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+                        "make": "Sony",
+                        "model": "G8441",
+                        "os": "Android",
+                        "osv": "9.0",
+                        "devicetype": 4,
+                        "connectiontype": 6,
+                        "js": 1,
+                        "carrier": "Free",
+                        "geo": {
+                            "type": 2,
+                            "country": "FRA"
+                        }
+                    },
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "secure": 1,
+                            "bidfloor": 3,
+                            "bidfloorcur": "EUR",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 320,
+                                        "h": 50
+                                    }
+                                ],
+                                "w": 320,
+                                "h": 50,
+                                "api": [
+                                    3,
+                                    5,
+                                    6
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "zoneId": "/1111111/banner"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 204
+            }
+        }
+    ],
+    "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/madvertise/madvertisetest/supplemental/response-400.json b/adapters/madvertise/madvertisetest/supplemental/response-400.json
new file mode 100644
index 00000000..a5af414d
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/supplemental/response-400.json
@@ -0,0 +1,194 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "tmax": 500,
+        "at": 2,
+        "cur": [
+            "EUR"
+        ],
+        "regs": {
+            "ext": {
+                "gdpr": 1
+            }
+        },
+        "user": {
+            "ext": {
+                "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+            }
+        },
+        "app": {
+            "id": "9561622",
+            "bundle": "com.madvertise.bluestack",
+            "name": "FR_BlueStack_App_Android_MNG",
+            "storeurl": "https://play.google.com/store/apps/details?id=com.madvertise.bluestack",
+            "cat": [
+                "IAB6",
+                "IAB9",
+                "IAB10",
+                "IAB12",
+                "IAB14",
+                "IAB20"
+            ]
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "test-user-agent",
+            "dnt": 0,
+            "lmt": 0,
+            "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+            "make": "Sony",
+            "model": "G8441",
+            "os": "Android",
+            "osv": "9.0",
+            "devicetype": 4,
+            "connectiontype": 6,
+            "js": 1,
+            "carrier": "Free",
+            "geo": {
+                "type": 2,
+                "country": "FRA"
+            }
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "secure": 1,
+                "bidfloor": 3,
+                "bidfloorcur": "EUR",
+                "instl": 0,
+                "banner": {
+                    "format": [
+                        {
+                            "w": 320,
+                            "h": 50
+                        }
+                    ],
+                    "w": 320,
+                    "h": 50,
+                    "api": [
+                        3,
+                        5,
+                        6
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "zoneId": "/1111111/banner"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://mobile.mng-ads.com/bidrequest/1111111/banner",
+                "headers": {
+                    "Content-Type": [
+                        "application/json;charset=utf-8"
+                    ],
+                    "Accept": [
+                        "application/json"
+                    ],
+                    "X-Openrtb-Version": [
+                        "2.5"
+                    ],
+                    "User-Agent": [
+                        "test-user-agent"
+                    ],
+                    "X-Forwarded-For": [
+                        "123.123.123.123"
+                    ]
+                },
+                "body": {
+                    "id": "test-request-id",
+                    "tmax": 500,
+                    "at": 2,
+                    "cur": [
+                        "EUR"
+                    ],
+                    "regs": {
+                        "ext": {
+                            "gdpr": 1
+                        }
+                    },
+                    "user": {
+                        "ext": {
+                            "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+                        }
+                    },
+                    "app": {
+                        "id": "9561622",
+                        "bundle": "com.madvertise.bluestack",
+                        "name": "FR_BlueStack_App_Android_MNG",
+                        "storeurl": "https://play.google.com/store/apps/details?id=com.madvertise.bluestack",
+                        "cat": [
+                            "IAB6",
+                            "IAB9",
+                            "IAB10",
+                            "IAB12",
+                            "IAB14",
+                            "IAB20"
+                        ]
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "test-user-agent",
+                        "dnt": 0,
+                        "lmt": 0,
+                        "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+                        "make": "Sony",
+                        "model": "G8441",
+                        "os": "Android",
+                        "osv": "9.0",
+                        "devicetype": 4,
+                        "connectiontype": 6,
+                        "js": 1,
+                        "carrier": "Free",
+                        "geo": {
+                            "type": 2,
+                            "country": "FRA"
+                        }
+                    },
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "secure": 1,
+                            "bidfloor": 3,
+                            "bidfloorcur": "EUR",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 320,
+                                        "h": 50
+                                    }
+                                ],
+                                "w": 320,
+                                "h": 50,
+                                "api": [
+                                    3,
+                                    5,
+                                    6
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "zoneId": "/1111111/banner"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 400
+            }
+        }
+    ],
+    "expectedMakeBidsErrors": [
+        {
+            "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+            "comparison": "literal"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/madvertise/madvertisetest/supplemental/response-500.json b/adapters/madvertise/madvertisetest/supplemental/response-500.json
new file mode 100644
index 00000000..8a4b0da4
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/supplemental/response-500.json
@@ -0,0 +1,194 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "tmax": 500,
+        "at": 2,
+        "cur": [
+            "EUR"
+        ],
+        "regs": {
+            "ext": {
+                "gdpr": 1
+            }
+        },
+        "user": {
+            "ext": {
+                "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+            }
+        },
+        "app": {
+            "id": "9561622",
+            "bundle": "com.madvertise.bluestack",
+            "name": "FR_BlueStack_App_Android_MNG",
+            "storeurl": "https://play.google.com/store/apps/details?id=com.madvertise.bluestack",
+            "cat": [
+                "IAB6",
+                "IAB9",
+                "IAB10",
+                "IAB12",
+                "IAB14",
+                "IAB20"
+            ]
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "test-user-agent",
+            "dnt": 0,
+            "lmt": 0,
+            "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+            "make": "Sony",
+            "model": "G8441",
+            "os": "Android",
+            "osv": "9.0",
+            "devicetype": 4,
+            "connectiontype": 6,
+            "js": 1,
+            "carrier": "Free",
+            "geo": {
+                "type": 2,
+                "country": "FRA"
+            }
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "secure": 1,
+                "bidfloor": 3,
+                "bidfloorcur": "EUR",
+                "instl": 0,
+                "banner": {
+                    "format": [
+                        {
+                            "w": 320,
+                            "h": 50
+                        }
+                    ],
+                    "w": 320,
+                    "h": 50,
+                    "api": [
+                        3,
+                        5,
+                        6
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "zoneId": "/1111111/banner"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://mobile.mng-ads.com/bidrequest/1111111/banner",
+                "headers": {
+                    "Content-Type": [
+                        "application/json;charset=utf-8"
+                    ],
+                    "Accept": [
+                        "application/json"
+                    ],
+                    "X-Openrtb-Version": [
+                        "2.5"
+                    ],
+                    "User-Agent": [
+                        "test-user-agent"
+                    ],
+                    "X-Forwarded-For": [
+                        "123.123.123.123"
+                    ]
+                },
+                "body": {
+                    "id": "test-request-id",
+                    "tmax": 500,
+                    "at": 2,
+                    "cur": [
+                        "EUR"
+                    ],
+                    "regs": {
+                        "ext": {
+                            "gdpr": 1
+                        }
+                    },
+                    "user": {
+                        "ext": {
+                            "consent": "CO-X2XiO_eyUoAsAxBFRBECsAP_AAH_AAAqIGMgB7CpERSNAYWApAOMAKYhfQAACAGAABAYIASgBQQBAMJQEkGAIMAjAAAAKAAAEACQAAAAgCAGAAAAAAAAAAQCMAAAAABAAACAAAAASAAAACAAACACSCAAQjAAAAAEAgAAAAAIF5wJwAFgAPAAqABcADIAHAAQAAqABiADQAHkARABFACeAFKALgAugBiADaAG8AOYAfwBCAClAGqANcAdQA_QCBgEIAIsARwAq4BgADZAHyAP-Aj0BUwC6gF5gCAoAYBEAC4AKpCQAwCIAFwAVSIgAgABFQAQAAg"
+                        }
+                    },
+                    "app": {
+                        "id": "9561622",
+                        "bundle": "com.madvertise.bluestack",
+                        "name": "FR_BlueStack_App_Android_MNG",
+                        "storeurl": "https://play.google.com/store/apps/details?id=com.madvertise.bluestack",
+                        "cat": [
+                            "IAB6",
+                            "IAB9",
+                            "IAB10",
+                            "IAB12",
+                            "IAB14",
+                            "IAB20"
+                        ]
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "test-user-agent",
+                        "dnt": 0,
+                        "lmt": 0,
+                        "ifa": "b879a967-cccc-bbbb-aaaa-1aa111111111",
+                        "make": "Sony",
+                        "model": "G8441",
+                        "os": "Android",
+                        "osv": "9.0",
+                        "devicetype": 4,
+                        "connectiontype": 6,
+                        "js": 1,
+                        "carrier": "Free",
+                        "geo": {
+                            "type": 2,
+                            "country": "FRA"
+                        }
+                    },
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "secure": 1,
+                            "bidfloor": 3,
+                            "bidfloorcur": "EUR",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 320,
+                                        "h": 50
+                                    }
+                                ],
+                                "w": 320,
+                                "h": 50,
+                                "api": [
+                                    3,
+                                    5,
+                                    6
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "zoneId": "/1111111/banner"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 500
+            }
+        }
+    ],
+    "expectedMakeBidsErrors": [
+        {
+            "value": "Unexpected status code: 500. Run with request.debug = 1 for more info",
+            "comparison": "literal"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/madvertise/madvertisetest/supplemental/unique-zone-id.json b/adapters/madvertise/madvertisetest/supplemental/unique-zone-id.json
new file mode 100644
index 00000000..68a56652
--- /dev/null
+++ b/adapters/madvertise/madvertisetest/supplemental/unique-zone-id.json
@@ -0,0 +1,45 @@
+{
+    "mockBidRequest": {
+        "id": "test-no-placement-id",
+        "imp": [
+            {
+                "id": "test-imp-id-unique-id-1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 728,
+                            "h": 90
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "zoneId": "/1111111/banner"
+                    }
+                }
+            },
+            {
+                "id": "test-imp-id-unique-id-2",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 728,
+                            "h": 90
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "zoneId": "/2222222/banner"
+                    }
+                }
+            }
+        ]
+    },
+    "expectedMakeRequestsErrors": [
+        {
+            "value": "There must be only one zone ID",
+            "comparison": "literal"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/madvertise/params_test.go b/adapters/madvertise/params_test.go
new file mode 100644
index 00000000..7c138bf4
--- /dev/null
+++ b/adapters/madvertise/params_test.go
@@ -0,0 +1,55 @@
+package madvertise
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/Madvertise.json
+//
+// These also validate the format of the external API: request.imp[i].ext.Madvertise
+
+// TestValidParams makes sure that the Madvertise schema accepts all imp.ext fields which we intend to support.
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderMadvertise, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected Madvertise params: %s \n Error: %s", validParam, err)
+		}
+	}
+}
+
+// TestInvalidParams makes sure that the Madvertise schema rejects all the imp.ext fields we don't support.
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderMadvertise, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"zoneId":"/1111111/banner"}`,
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`[]`,
+	`{}`,
+	`{"zoneId":""}`,
+	`{"zoneId":/1111111}`,
+	`{"zoneId":/1111"}`,
+}
diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go
index 17ed7f74..f8b4f9d9 100644
--- a/endpoints/auction_test.go
+++ b/endpoints/auction_test.go
@@ -441,7 +441,6 @@ func TestShouldUsersync(t *testing.T) {
 type auctionMockPermissions struct {
 	allowBidderSync  bool
 	allowHostCookies bool
-	allowPI          bool
 	allowGeo         bool
 	allowID          bool
 }
@@ -454,8 +453,8 @@ func (m *auctionMockPermissions) BidderSyncAllowed(ctx context.Context, bidder o
 	return m.allowBidderSync, nil
 }
 
-func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (bool, bool, bool, error) {
-	return m.allowPI, m.allowGeo, m.allowID, nil
+func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
+	return m.allowGeo, m.allowID, nil
 }
 
 func TestBidSizeValidate(t *testing.T) {
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index d4b89a15..bfd6c507 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -254,6 +254,6 @@ func (g *gdprPerms) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.Bi
 	return ok, nil
 }
 
-func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (bool, bool, bool, error) {
-	return true, true, true, nil
+func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
+	return true, true, nil
 }
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 0d68c15b..45ea2490 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -389,9 +389,9 @@ func makeRequest(uri string, existingSyncs map[string]string) *http.Request {
 func doRequest(req *http.Request, metrics metrics.MetricsEngine, validFamilyNames []string, gdprAllowsHostCookies bool, gdprReturnsError bool) *httptest.ResponseRecorder {
 	cfg := config.Configuration{}
 	perms := &mockPermsSetUID{
-		allowHost: gdprAllowsHostCookies,
-		errorHost: gdprReturnsError,
-		allowPI:   true,
+		allowHost:           gdprAllowsHostCookies,
+		errorHost:           gdprReturnsError,
+		personalInfoAllowed: true,
 	}
 	analytics := analyticsConf.NewPBSAnalytics(&cfg.Analytics)
 	syncers := make(map[openrtb_ext.BidderName]usersync.Usersyncer)
@@ -422,9 +422,9 @@ func parseCookieString(t *testing.T, response *httptest.ResponseRecorder) *users
 }
 
 type mockPermsSetUID struct {
-	allowHost bool
-	errorHost bool
-	allowPI   bool
+	allowHost           bool
+	errorHost           bool
+	personalInfoAllowed bool
 }
 
 func (g *mockPermsSetUID) HostCookiesAllowed(ctx context.Context, gdprSignal gdpr.Signal, consent string) (bool, error) {
@@ -439,8 +439,8 @@ func (g *mockPermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	return false, nil
 }
 
-func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (bool, bool, bool, error) {
-	return g.allowPI, g.allowPI, g.allowPI, nil
+func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
+	return g.personalInfoAllowed, g.personalInfoAllowed, nil
 }
 
 func newFakeSyncer(familyName string) usersync.Usersyncer {
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 55a0950a..9f6041e9 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -32,8 +32,8 @@ func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	return true, nil
 }
 
-func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdpr gdpr.Signal, consent string, weakVendorEnforcement bool) (bool, bool, bool, error) {
-	return p.personalInfoAllowed, p.personalInfoAllowed, p.personalInfoAllowed, p.personalInfoAllowedError
+func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdpr gdpr.Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
+	return p.personalInfoAllowed, p.personalInfoAllowed, p.personalInfoAllowedError
 }
 
 func assertReq(t *testing.T, bidderRequests []BidderRequest,
diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go
index b13d469a..cc08aedd 100644
--- a/gdpr/impl_test.go
+++ b/gdpr/impl_test.go
@@ -181,7 +181,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 		userSyncIfAmbiguous   bool
 		gdpr                  Signal
 		consent               string
-		allowPI               bool
+		allowID               bool
 		weakVendorEnforcement bool
 	}{
 		{
@@ -191,7 +191,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalYes,
 			consent:             consent,
-			allowPI:             true,
+			allowID:             true,
 		},
 		{
 			description:         "Allow PI - known vendor with No GDPR",
@@ -199,7 +199,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalNo,
 			consent:             consent,
-			allowPI:             true,
+			allowID:             true,
 		},
 		{
 			description:         "Allow PI - known vendor with Yes GDPR",
@@ -207,7 +207,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalYes,
 			consent:             consent,
-			allowPI:             true,
+			allowID:             true,
 		},
 		{
 			description:         "PI allowed according to host setting UserSyncIfAmbiguous true - known vendor with ambiguous GDPR and empty consent",
@@ -215,7 +215,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: true,
 			gdpr:                SignalAmbiguous,
 			consent:             "",
-			allowPI:             true,
+			allowID:             true,
 		},
 		{
 			description:         "PI allowed according to host setting UserSyncIfAmbiguous true - known vendor with ambiguous GDPR and non-empty consent",
@@ -223,7 +223,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: true,
 			gdpr:                SignalAmbiguous,
 			consent:             consent,
-			allowPI:             true,
+			allowID:             true,
 		},
 		{
 			description:         "PI allowed according to host setting UserSyncIfAmbiguous false - known vendor with ambiguous GDPR and empty consent",
@@ -231,7 +231,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalAmbiguous,
 			consent:             "",
-			allowPI:             false,
+			allowID:             false,
 		},
 		{
 			description:         "PI allowed according to host setting UserSyncIfAmbiguous false - known vendor with ambiguous GDPR and non-empty consent",
@@ -239,7 +239,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalAmbiguous,
 			consent:             consent,
-			allowPI:             true,
+			allowID:             true,
 		},
 		{
 			description:         "Don't allow PI - known vendor with Yes GDPR and empty consent",
@@ -247,7 +247,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalYes,
 			consent:             "",
-			allowPI:             false,
+			allowID:             false,
 		},
 		{
 			description:         "Don't allow PI - default vendor with Yes GDPR and non-empty consent",
@@ -255,7 +255,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalYes,
 			consent:             consent,
-			allowPI:             false,
+			allowID:             false,
 		},
 	}
 
@@ -286,10 +286,10 @@ func TestAllowPersonalInfo(t *testing.T) {
 	for _, tt := range tests {
 		perms.cfg.UsersyncIfAmbiguous = tt.userSyncIfAmbiguous
 
-		allowPI, _, _, err := perms.PersonalInfoAllowed(context.Background(), tt.bidderName, tt.publisherID, tt.gdpr, tt.consent, tt.weakVendorEnforcement)
+		_, allowID, err := perms.PersonalInfoAllowed(context.Background(), tt.bidderName, tt.publisherID, tt.gdpr, tt.consent, tt.weakVendorEnforcement)
 
 		assert.Nil(t, err, tt.description)
-		assert.Equal(t, tt.allowPI, allowPI, tt.description)
+		assert.Equal(t, tt.allowID, allowID, tt.description)
 	}
 }
 
@@ -347,7 +347,6 @@ type tcf2TestDef struct {
 	description           string
 	bidder                openrtb_ext.BidderName
 	consent               string
-	allowPI               bool
 	allowGeo              bool
 	allowID               bool
 	weakVendorEnforcement bool
@@ -379,7 +378,6 @@ func TestAllowPersonalInfoTCF2(t *testing.T) {
 			description: "Appnexus vendor test, insufficient purposes claimed",
 			bidder:      openrtb_ext.BidderAppnexus,
 			consent:     "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA",
-			allowPI:     false,
 			allowGeo:    false,
 			allowID:     false,
 		},
@@ -387,7 +385,6 @@ func TestAllowPersonalInfoTCF2(t *testing.T) {
 			description:           "Appnexus vendor test, insufficient purposes claimed, basic enforcement",
 			bidder:                openrtb_ext.BidderAppnexus,
 			consent:               "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA",
-			allowPI:               true,
 			allowGeo:              true,
 			allowID:               true,
 			weakVendorEnforcement: true,
@@ -396,7 +393,6 @@ func TestAllowPersonalInfoTCF2(t *testing.T) {
 			description: "Pubmatic vendor test, flex purposes claimed",
 			bidder:      openrtb_ext.BidderPubmatic,
 			consent:     "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA",
-			allowPI:     true,
 			allowGeo:    true,
 			allowID:     true,
 		},
@@ -404,7 +400,6 @@ func TestAllowPersonalInfoTCF2(t *testing.T) {
 			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
 			bidder:      openrtb_ext.BidderRubicon,
 			consent:     "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA",
-			allowPI:     true,
 			allowGeo:    false,
 			allowID:     true,
 		},
@@ -415,16 +410,14 @@ func TestAllowPersonalInfoTCF2(t *testing.T) {
 			description: "OpenX vendor test, Specific purposes/LIs claimed, no geo claimed, Publisher restrictions apply",
 			bidder:      openrtb_ext.BidderOpenx,
 			consent:     "CPAavcCPAavcCAGABCFRBKCsAP_AAH_AAAqIHFNf_X_fb3_j-_59_9t0eY1f9_7_v-0zjgeds-8Nyd_X_L8X5mM7vB36pq4KuR4Eu3LBAQdlHOHcTUmw6IkVqTPsbk2Mr7NKJ7PEinMbe2dYGH9_n9XT_ZKY79_____7__-_____7_f__-__3_vp9V---wOJAIMBAUAgAEMAAQIFCIQAAQhiQAAAABBCIBQJIAEqgAWVwEdoIEACAxAQgQAgBBQgwCAAQAAJKAgBACwQCAAiAQAAgAEAIAAEIAILACQEAAAEAJCAAiACECAgiAAg5DAgIgCCAFABAAAuJDACAMooASBAPGQGAAKAAqACGAEwALgAjgBlgDUAHZAPsA_ACMAFLAK2AbwBMQCbAFogLYAYEAw8BkQDOQGeAM-EQHwAVABWAC4AIYAZAAywBqADZAHYAPwAgABGAClgFPANYAdUA-QCGwEOgIvASIAmwBOwCkQFyAMCAYSAw8Bk4DOQGfCQAYADgBzgN_CQTgAEAALgAoACoAGQAOAAeABAACIAFQAMIAaABqADyAIYAigBMgCqAKwAWAAuABvADmAHoAQ0AiACJgEsAS4AmgBSgC3AGGAMgAZcA1ADVAGyAO8AewA-IB9gH6AQAAjABQQClgFPAL8AYoA1gBtADcAG8AOIAegA-QCGwEOgIqAReAkQBMQCZQE2AJ2AUOApEBYoC2AFyALvAYEAwYBhIDDQGHgMiAZIAycBlwDOQGfANIAadA1gDWQoAEAYQaBIACoAKwAXABDADIAGWANQAbIA7AB-AEAAIKARgApYBT4C0ALSAawA3gB1QD5AIbAQ6Ai8BIgCbAE7AKRAXIAwIBhIDDwGMAMnAZyAzwBnwcAEAA4Bv4qA2ABQAFQAQwAmABcAEcAMsAagA7AB-AEYAKXAWgBaQDeAJBATEAmwBTYC2AFyAMCAYeAyIBnIDPAGfANyHQWQAFwAUABUADIAHAAQAAiABdADAAMYAaABqADwAH0AQwBFACZAFUAVgAsABcADEAGYAN4AcwA9ACGAERAJYAmABNACjAFKALEAW4AwwBkADKAGiANQAbIA3wB3gD2gH2AfoBGACVAFBAKeAWKAtAC0gFzALyAX4AxQBuADiQHTAdQA9ACGwEOgIiAReAkEBIgCbAE7AKHAU0AqwBYsC2ALZAXAAuQBdoC7wGEgMNAYeAxIBjADHgGSAMnAZUAywBlwDOQGfANEgaQBpIDSwGnANYAbGPABAIqAb-QgZgALAAoABkAEQALgAYgBDACYAFUALgAYgAzABvAD0AI4AWIAygBqADfAHfAPsA_ACMAFBAKGAU-AtAC0gF-AMUAdQA9ACQQEiAJsAU0AsUBaMC2ALaAXAAuQBdoDDwGJAMiAZOAzkBngDPgGiANJAaWA4AlAyAAQAAsACgAGQAOAAigBgAGIAPAAiABMACqAFwAMQAZgA2gCGgEQARIAowBSgC3AGEAMoAaoA2QB3gD8AIwAU-AtAC0gGKANwAcQA6gCHQEXgJEATYAsUBbAC7QGHgMiAZOAywBnIDPAGfANIAawA4AmACARUA38pBBAAXABQAFQAMgAcABAACKAGAAYwA0ADUAHkAQwBFACYAFIAKoAWAAuABiADMAHMAQwAiABRgClAFiALcAZQA0QBqgDZAHfAPsA_ACMAFBAKGAVsAuYBeQDaAG4APQAh0BF4CRAE2AJ2AUOApoBWwCxQFsALgAXIAu0BhoDDwGMAMiAZIAycBlwDOQGeAM-gaQBpMDWANZAbGVABAA-Ab-A.YAAAAAAAAAAA",
-			allowPI:     true,
 			allowGeo:    false,
 			allowID:     true,
 		},
 	}
 
 	for _, td := range testDefs {
-		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
+		allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
 		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
-		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
 		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
 		assert.EqualValuesf(t, td.allowID, allowID, "AllowGeo failure on %s", td.description)
 	}
@@ -448,9 +441,8 @@ func TestAllowPersonalInfoWhitelistTCF2(t *testing.T) {
 	}
 	// Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array
 	perms.cfg.NonStandardPublisherMap = map[string]struct{}{"appNexusAppID": {}}
-	allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", false)
+	allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", false)
 	assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed")
-	assert.EqualValuesf(t, true, allowPI, "AllowPI failure")
 	assert.EqualValuesf(t, true, allowGeo, "AllowGeo failure")
 	assert.EqualValuesf(t, true, allowID, "AllowID failure")
 }
@@ -479,7 +471,6 @@ func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) {
 			description: "Appnexus vendor test, insufficient purposes claimed",
 			bidder:      openrtb_ext.BidderAppnexus,
 			consent:     "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA",
-			allowPI:     false,
 			allowGeo:    false,
 			allowID:     false,
 		},
@@ -487,7 +478,6 @@ func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) {
 			description: "Pubmatic vendor test, flex purposes claimed",
 			bidder:      openrtb_ext.BidderPubmatic,
 			consent:     "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA",
-			allowPI:     false,
 			allowGeo:    false,
 			allowID:     false,
 		},
@@ -495,18 +485,16 @@ func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) {
 			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
 			bidder:      openrtb_ext.BidderRubicon,
 			consent:     "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA",
-			allowPI:     false,
 			allowGeo:    false,
 			allowID:     true,
 		},
 	}
 
 	for _, td := range testDefs {
-		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
+		allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
 		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
-		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
 		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
-		assert.EqualValuesf(t, td.allowID, allowID, "AllowPI failure on %s", td.description)
+		assert.EqualValuesf(t, td.allowID, allowID, "AllowID failure on %s", td.description)
 	}
 }
 
@@ -535,7 +523,6 @@ func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) {
 			description: "Appnexus vendor test, insufficient purposes claimed",
 			bidder:      openrtb_ext.BidderAppnexus,
 			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowPI:     false,
 			allowGeo:    false,
 			allowID:     false,
 		},
@@ -543,7 +530,6 @@ func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) {
 			description: "Pubmatic vendor test, flex purposes claimed",
 			bidder:      openrtb_ext.BidderPubmatic,
 			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowPI:     true,
 			allowGeo:    true,
 			allowID:     true,
 		},
@@ -551,16 +537,14 @@ func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) {
 			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
 			bidder:      openrtb_ext.BidderRubicon,
 			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowPI:     true,
 			allowGeo:    false,
 			allowID:     true,
 		},
 	}
 
 	for _, td := range testDefs {
-		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
+		allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
 		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
-		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
 		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
 		assert.EqualValuesf(t, td.allowID, allowID, "AllowID failure on %s", td.description)
 	}
@@ -592,7 +576,6 @@ func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) {
 			description: "Appnexus vendor test, insufficient purposes claimed",
 			bidder:      openrtb_ext.BidderAppnexus,
 			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowPI:     false,
 			allowGeo:    false,
 			allowID:     false,
 		},
@@ -600,7 +583,6 @@ func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) {
 			description: "Pubmatic vendor test, flex purposes claimed",
 			bidder:      openrtb_ext.BidderPubmatic,
 			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowPI:     false,
 			allowGeo:    true,
 			allowID:     true,
 		},
@@ -608,16 +590,14 @@ func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) {
 			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
 			bidder:      openrtb_ext.BidderRubicon,
 			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowPI:     false,
 			allowGeo:    false,
 			allowID:     true,
 		},
 	}
 
 	for _, td := range testDefs {
-		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
+		allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
 		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
-		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
 		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
 		assert.EqualValuesf(t, td.allowID, allowID, "AllowID failure on %s", td.description)
 	}
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 79bf84e3..36b94000 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -115,8 +115,10 @@ func TestNewSyncerMap(t *testing.T) {
 		openrtb_ext.BidderDecenterAds:       true,
 		openrtb_ext.BidderInMobi:            true,
 		openrtb_ext.BidderInteractiveoffers: true,
+		openrtb_ext.BidderKayzen:            true,
 		openrtb_ext.BidderKidoz:             true,
 		openrtb_ext.BidderKubient:           true,
+		openrtb_ext.BidderMadvertise:        true,
 		openrtb_ext.BidderMobfoxpb:          true,
 		openrtb_ext.BidderMobileFuse:        true,
 		openrtb_ext.BidderOrbidder:          true,