Repository: prebid/prebid-server
Pull request #1842 (2021-05-08 07:02:59)

Pangle: add appid & placementid to bidder param
doc PR: https://github.com/prebid/prebid.github.io/pull/2942

changes:
- add the optional appid and placementid (which when used, must be used in conjunction) to bidder param, then use them to form networkids in imp.ext
- add a true is_prebid flag for the auto recognition of prebid requests on our side


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/beachfront/beachfront.go b/adapters/beachfront/beachfront.go
index 6bcabf4a..c5f6c4d5 100644
--- a/adapters/beachfront/beachfront.go
+++ b/adapters/beachfront/beachfront.go
@@ -60,23 +60,24 @@ type beachfrontVideoRequest struct {
 //              Banner
 // ---------------------------------------------------
 type beachfrontBannerRequest struct {
-	Slots          []beachfrontSlot `json:"slots"`
-	Domain         string           `json:"domain"`
-	Page           string           `json:"page"`
-	Referrer       string           `json:"referrer"`
-	Search         string           `json:"search"`
-	Secure         int8             `json:"secure"`
-	DeviceOs       string           `json:"deviceOs"`
-	DeviceModel    string           `json:"deviceModel"`
-	IsMobile       int8             `json:"isMobile"`
-	UA             string           `json:"ua"`
-	Dnt            int8             `json:"dnt"`
-	User           openrtb2.User    `json:"user"`
-	AdapterName    string           `json:"adapterName"`
-	AdapterVersion string           `json:"adapterVersion"`
-	IP             string           `json:"ip"`
-	RequestID      string           `json:"requestId"`
-	Real204        bool             `json:"real204"`
+	Slots          []beachfrontSlot                         `json:"slots"`
+	Domain         string                                   `json:"domain"`
+	Page           string                                   `json:"page"`
+	Referrer       string                                   `json:"referrer"`
+	Search         string                                   `json:"search"`
+	Secure         int8                                     `json:"secure"`
+	DeviceOs       string                                   `json:"deviceOs"`
+	DeviceModel    string                                   `json:"deviceModel"`
+	IsMobile       int8                                     `json:"isMobile"`
+	UA             string                                   `json:"ua"`
+	Dnt            int8                                     `json:"dnt"`
+	User           openrtb2.User                            `json:"user"`
+	AdapterName    string                                   `json:"adapterName"`
+	AdapterVersion string                                   `json:"adapterVersion"`
+	IP             string                                   `json:"ip"`
+	RequestID      string                                   `json:"requestId"`
+	Real204        bool                                     `json:"real204"`
+	SChain         openrtb_ext.ExtRequestPrebidSChainSChain `json:"schain,omitempty"`
 }
 
 type beachfrontSlot struct {
@@ -263,6 +264,11 @@ func getAppId(ext openrtb_ext.ExtImpBeachfront, media openrtb_ext.BidType) (stri
 	return appid, error
 }
 
+func getSchain(request *openrtb2.BidRequest) (openrtb_ext.ExtRequestPrebidSChain, error) {
+	var schain openrtb_ext.ExtRequestPrebidSChain
+	return schain, json.Unmarshal(request.Source.Ext, &schain)
+}
+
 /*
 getBannerRequest, singular. A "Slot" is an "imp," and each Slot can have an AppId, so just one
 request to the beachfront banner endpoint gets all banner Imps.
@@ -370,6 +376,13 @@ func getBannerRequest(request *openrtb2.BidRequest) (beachfrontBannerRequest, []
 	}
 	bfr.Real204 = true
 
+	if request.Source != nil && request.Source.Ext != nil {
+		schain, err := getSchain(request)
+		if err == nil {
+			bfr.SChain = schain.SChain
+		}
+	}
+
 	return bfr, errs
 }
 
@@ -517,10 +530,10 @@ func (a *BeachfrontAdapter) MakeBids(internalRequest *openrtb2.BidRequest, exter
 	// only get the User struct and everything else will be nil
 	if err := json.Unmarshal(externalRequest.Body, &xtrnal); err != nil {
 		errs = append(errs, err)
+	} else {
+		bids, errs = postprocess(response, xtrnal, externalRequest.Uri, internalRequest.ID)
 	}
 
-	bids, errs = postprocess(response, xtrnal, externalRequest.Uri, internalRequest.ID)
-
 	if len(errs) != 0 {
 		return nil, errs
 	}
@@ -569,7 +582,7 @@ func postprocess(response *adapters.ResponseData, xtrnal openrtb2.BidRequest, ur
 
 		if err := json.Unmarshal(response.Body, &beachfrontResp); err != nil {
 			return nil, []error{&errortypes.BadServerResponse{
-				Message: fmt.Sprint("server response failed to unmarshal as valid rtb. Run with request.debug = 1 for more info"),
+				Message: "server response failed to unmarshal as valid rtb. Run with request.debug = 1 for more info",
 			}}
 		} else {
 			return postprocessBanner(beachfrontResp, id)
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/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/pangle/pangle.go b/adapters/pangle/pangle.go
index a4694c71..da86a904 100644
--- a/adapters/pangle/pangle.go
+++ b/adapters/pangle/pangle.go
@@ -16,9 +16,16 @@ type adapter struct {
 	Endpoint string
 }
 
+type NetworkIDs struct {
+	AppID       string `json:"appid,omitempty"`
+	PlacementID string `json:"placementid,omitempty"`
+}
+
 type wrappedExtImpBidder struct {
 	*adapters.ExtImpBidder
-	AdType int `json:"adtype,omitempty"`
+	AdType     int         `json:"adtype,omitempty"`
+	IsPrebid   bool        `json:"is_prebid,omitempty"`
+	NetworkIDs *NetworkIDs `json:"networkids,omitempty"`
 }
 
 type pangleBidExt struct {
@@ -78,23 +85,34 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapte
 			errs = append(errs, fmt.Errorf("failed unmarshalling imp ext (err)%s", err.Error()))
 			continue
 		}
+		// get token & networkIDs
+		var bidderImpExt openrtb_ext.ImpExtPangle
+		if err := json.Unmarshal(impExt.Bidder, &bidderImpExt); err != nil {
+			errs = append(errs, fmt.Errorf("failed unmarshalling bidder imp ext (err)%s", err.Error()))
+			continue
+		}
 		// detect and fill adtype
-		if adType := getAdType(imp, &impExt); adType == -1 {
+		adType := getAdType(imp, &impExt)
+		if adType == -1 {
 			errs = append(errs, &errortypes.BadInput{Message: "not a supported adtype"})
 			continue
-		} else {
-			impExt.AdType = adType
-			if newImpExt, err := json.Marshal(impExt); err == nil {
-				imp.Ext = newImpExt
-			} else {
-				errs = append(errs, fmt.Errorf("failed re-marshalling imp ext with adtype"))
-				continue
+		}
+		// remarshal imp.ext
+		impExt.AdType = adType
+		impExt.IsPrebid = true
+		if len(bidderImpExt.AppID) > 0 && len(bidderImpExt.PlacementID) > 0 {
+			impExt.NetworkIDs = &NetworkIDs{
+				AppID:       bidderImpExt.AppID,
+				PlacementID: bidderImpExt.PlacementID,
 			}
+		} else if len(bidderImpExt.AppID) > 0 || len(bidderImpExt.PlacementID) > 0 {
+			errs = append(errs, &errortypes.BadInput{Message: "only one of appid or placementid is provided"})
+			continue
 		}
-		// for setting token
-		var bidderImpExt openrtb_ext.ImpExtPangle
-		if err := json.Unmarshal(impExt.Bidder, &bidderImpExt); err != nil {
-			errs = append(errs, fmt.Errorf("failed unmarshalling bidder imp ext (err)%s", err.Error()))
+		if newImpExt, err := json.Marshal(impExt); err == nil {
+			imp.Ext = newImpExt
+		} else {
+			errs = append(errs, fmt.Errorf("failed re-marshalling imp ext"))
 			continue
 		}
 
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 61135e89..ebd3af77 100644
--- a/config/config.go
+++ b/config/config.go
@@ -863,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")
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index f28d98f3..59789a25 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -57,6 +57,7 @@ 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"
@@ -172,6 +173,7 @@ 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,
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 08194955..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"
@@ -243,6 +244,7 @@ func CoreBidderNames() []BidderName {
 		BidderInvibes,
 		BidderIx,
 		BidderJixie,
+		BidderKayzen,
 		BidderKidoz,
 		BidderKrushmedia,
 		BidderKubient,
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_pangle.go b/openrtb_ext/imp_pangle.go
index cc32dcf1..cf3b7e74 100644
--- a/openrtb_ext/imp_pangle.go
+++ b/openrtb_ext/imp_pangle.go
@@ -1,5 +1,7 @@
 package openrtb_ext
 
 type ImpExtPangle struct {
-	Token string `json:"token"`
+	Token       string `json:"token"`
+	AppID       string `json:"appid,omitempty"`
+	PlacementID string `json:"placementid,omitempty"`
 }
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/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/pangle.json b/static/bidder-params/pangle.json
index 74085cb5..b36922c3 100644
--- a/static/bidder-params/pangle.json
+++ b/static/bidder-params/pangle.json
@@ -8,9 +8,27 @@
       "type": "string",
       "description": "Access Token",
       "pattern": ".+"
+    },
+    "appid": {
+      "type": "string",
+      "description": "App ID",
+      "pattern": "[0-9]+"
+    },
+    "placementid": {
+      "type": "string",
+      "description": "Placement ID",
+      "pattern": "[0-9]+"
     }
   },
   "required": [
     "token"
-  ]
-}
+  ],
+  "dependencies": {
+    "placementid": [
+      "appid"
+    ],
+    "appid": [
+      "placementid"
+    ]
+  }
+}
\ 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/beachfront/beachfronttest/exemplary/banner.json b/adapters/beachfront/beachfronttest/exemplary/banner.json
index 1a8e8305..11db9728 100644
--- a/adapters/beachfront/beachfronttest/exemplary/banner.json
+++ b/adapters/beachfront/beachfronttest/exemplary/banner.json
@@ -58,7 +58,12 @@
           "ua": "",
           "adapterName": "BF_PREBID_S2S",
           "adapterVersion": "0.9.2",
-          "user": {}
+          "user": {},
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
+          }
         }
       },
       "mockResponse": {
diff --git a/adapters/beachfront/beachfronttest/supplemental/adm-video-schain.json b/adapters/beachfront/beachfronttest/supplemental/adm-video-schain.json
new file mode 100644
index 00000000..32929817
--- /dev/null
+++ b/adapters/beachfront/beachfronttest/supplemental/adm-video-schain.json
@@ -0,0 +1,158 @@
+{
+  "mockBidRequest": {
+    "id": "adm-video",
+    "imp": [
+      {
+        "id": "video1",
+        "ext": {
+          "bidder": {
+            "bidfloor": 3.01,
+            "appId": "videoAppId1"
+          }
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "context": "instream",
+          "w": 300,
+          "h": 250
+        }
+      }
+    ],
+    "site": {
+      "page": "https://some.domain.us/some/page.html"
+    },
+    "device":{
+      "ip":"192.168.168.168"
+    },
+    "source": {
+      "fd": 1,
+      "ext": {
+        "schain": {
+          "complete": 1,
+          "ver": "1.0",
+          "nodes": [
+            {
+              "asi": "somenode.com",
+              "sid": "some-sid",
+              "rid": "some-rid",
+              "hp": 1
+            }
+          ]
+        }
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1",
+        "body": {
+          "id": "adm-video",
+          "imp": [
+            {
+              "video": {
+                "w": 300,
+                "h": 250,
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "bidfloor": 3.01,
+              "id": "video1",
+              "secure": 1
+            }
+          ],
+          "site": {
+            "page": "https://some.domain.us/some/page.html",
+            "domain": "some.domain.us"
+          },
+          "cur": [
+            "USD"
+          ],
+          "device":{
+            "devicetype": 2,
+            "ip":"192.168.168.168"
+          },
+          "source": {
+            "fd": 1,
+            "ext": {
+              "schain": {
+                "complete": 1,
+                "ver": "1.0",
+                "nodes": [
+                  {
+                    "asi": "somenode.com",
+                    "sid": "some-sid",
+                    "rid": "some-rid",
+                    "hp": 1
+                  }
+                ]
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "adm-video",
+          "seatBid": [
+            {
+              "bid": [
+                {
+                  "id": "5fd7c8a6ff2f1f0d42ee6427",
+                  "impid": "video1",
+                  "price": 20,
+                  "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+                  "adid": "1088",
+                  "adomain": [
+                    "beachfront.io"
+                  ],
+                  "cid": "277",
+                  "crid": "532",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "duration": 30
+                  }
+                }
+              ],
+              "seat": "bfio-s-1"
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "video1AdmVideo",
+            "impid": "video1",
+            "price": 20,
+            "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+            "adid": "1088",
+            "adomain": [
+              "beachfront.io"
+            ],
+            "cid": "277",
+            "crid": "532",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "duration": 30
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-204-with-body.json b/adapters/beachfront/beachfronttest/supplemental/banner-204-with-body.json
index edba490e..af165c26 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-204-with-body.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-204-with-body.json
@@ -60,6 +60,11 @@
           "adapterName": "BF_PREBID_S2S",
           "adapterVersion": "0.9.2",
           "user": {
+          },
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
           }
         }
       },
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-204.json b/adapters/beachfront/beachfronttest/supplemental/banner-204.json
index 69aa0b88..1c00f52c 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-204.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-204.json
@@ -60,6 +60,11 @@
           "adapterName": "BF_PREBID_S2S",
           "adapterVersion": "0.9.2",
           "user": {
+          },
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
           }
         }
       },
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-by-explicit.json b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-by-explicit.json
index b060efe6..283ba761 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-by-explicit.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-by-explicit.json
@@ -73,7 +73,12 @@
           "user": {},
           "adapterName": "BF_PREBID_S2S",
           "adapterVersion": "0.9.2",
-          "requestId": "banner-and-video"
+          "requestId": "banner-and-video",
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
+          }
         }
       },
       "mockResponse": {
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-expected-204-response-on-banner.json b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-expected-204-response-on-banner.json
index 240994bb..3026c133 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-expected-204-response-on-banner.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-expected-204-response-on-banner.json
@@ -72,7 +72,12 @@
           "user": {},
           "adapterName": "BF_PREBID_S2S",
           "adapterVersion": "0.9.2",
-          "requestId": "banner-and-video"
+          "requestId": "banner-and-video",
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
+          }
         }
       },
       "mockResponse": {
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video.json b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video.json
index 18e8ca7a..73aa7df0 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video.json
@@ -72,7 +72,12 @@
           "user": {},
           "adapterName": "BF_PREBID_S2S",
           "adapterVersion": "0.9.2",
-          "requestId": "banner-and-video"
+          "requestId": "banner-and-video",
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
+          }
         }
       },
       "mockResponse": {
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-and-nurl-video.json b/adapters/beachfront/beachfronttest/supplemental/banner-and-nurl-video.json
index 2ef8ca58..f1e06544 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-and-nurl-video.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-and-nurl-video.json
@@ -70,7 +70,12 @@
           "user": {},
           "adapterName": "BF_PREBID_S2S",
           "adapterVersion": "0.9.2",
-          "requestId": "banner-and-video"
+          "requestId": "banner-and-video",
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
+          }
         }
       },
       "mockResponse": {
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-bad-request-400.json b/adapters/beachfront/beachfronttest/supplemental/banner-bad-request-400.json
index 35926b3c..42649958 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-bad-request-400.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-bad-request-400.json
@@ -58,7 +58,12 @@
           "ua": "",
           "adapterName": "BF_PREBID_S2S",
           "adapterVersion": "0.9.2",
-          "user": {}
+          "user": {},
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
+          }
         }
       },
       "mockResponse": {
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-schain.json b/adapters/beachfront/beachfronttest/supplemental/banner-schain.json
new file mode 100644
index 00000000..4856e3e9
--- /dev/null
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-schain.json
@@ -0,0 +1,126 @@
+{
+  "mockBidRequest": {
+    "id": "some_test_ad",
+    "site": {
+      "page": "https://some.domain.us/some/page.html"
+    },
+    "imp": [
+      {
+        "bidfloor": 0.02,
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "bidfloor": 0.02,
+            "appId": "bannerAppId1"
+          }
+        }
+      }
+    ],
+    "source": {
+      "fd": 1,
+      "ext": {
+        "schain": {
+          "complete": 1,
+          "ver": "1.0",
+          "nodes": [
+            {
+              "asi": "somenode.com",
+              "sid": "some-sid",
+              "rid": "some-rid",
+              "hp": 1
+            }
+          ]
+        }
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/prebid_display",
+        "body": {
+          "slots": [
+            {
+              "slot": "",
+              "id": "bannerAppId1",
+              "bidfloor": 0.02,
+              "sizes": [
+                {
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "domain": "some.domain.us",
+          "page": "https://some.domain.us/some/page.html",
+          "real204": true,
+          "referrer": "",
+          "search": "",
+          "secure": 1,
+          "requestId": "some_test_ad",
+          "isMobile": 0,
+          "ip": "",
+          "deviceModel": "",
+          "deviceOs": "",
+          "dnt": 0,
+          "ua": "",
+          "adapterName": "BF_PREBID_S2S",
+          "adapterVersion": "0.9.2",
+          "user": {},
+          "schain": {
+            "complete": 1,
+            "ver": "1.0",
+            "nodes": [
+              {
+                  "asi": "somenode.com",
+                  "sid": "some-sid",
+                  "rid": "some-rid",
+                  "hp": 1
+              }
+            ]
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "crid": "crid_1",
+            "price": 2.942808,
+            "w": 300,
+            "h": 250,
+            "slot": "div-gpt-ad-1460505748561-0",
+            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>"
+          }
+        ]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "div-gpt-ad-1460505748561-0Banner",
+            "impid": "div-gpt-ad-1460505748561-0",
+            "price": 2.942808,
+            "adm": "<div id=\"44861168\"><script>!function(){console.log\"Hello, ad.\";}();<\/script><\/div>",
+            "crid": "crid_1",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/beachfront/beachfronttest/supplemental/bidder_response_unmarshal_error_banner.json b/adapters/beachfront/beachfronttest/supplemental/bidder_response_unmarshal_error_banner.json
index e7869d36..dc709071 100644
--- a/adapters/beachfront/beachfronttest/supplemental/bidder_response_unmarshal_error_banner.json
+++ b/adapters/beachfront/beachfronttest/supplemental/bidder_response_unmarshal_error_banner.json
@@ -57,7 +57,12 @@
           "ua": "",
           "adapterName": "BF_PREBID_S2S",
           "adapterVersion": "0.9.2",
-          "user": {}
+          "user": {},
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
+          }
         }
       },
       "mockResponse": {
diff --git a/adapters/beachfront/beachfronttest/supplemental/bidfloor-below-min.json b/adapters/beachfront/beachfronttest/supplemental/bidfloor-below-min.json
index 7ebe6cf4..70b89e5d 100644
--- a/adapters/beachfront/beachfronttest/supplemental/bidfloor-below-min.json
+++ b/adapters/beachfront/beachfronttest/supplemental/bidfloor-below-min.json
@@ -57,7 +57,12 @@
           "ua": "",
           "adapterName": "BF_PREBID_S2S",
           "adapterVersion": "0.9.2",
-          "user": {}
+          "user": {},
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
+          }
         }
       },
       "mockResponse": {
diff --git a/adapters/beachfront/beachfronttest/supplemental/six-nine-combo.json b/adapters/beachfront/beachfronttest/supplemental/six-nine-combo.json
index 9a8ca734..c2a52be6 100644
--- a/adapters/beachfront/beachfronttest/supplemental/six-nine-combo.json
+++ b/adapters/beachfront/beachfronttest/supplemental/six-nine-combo.json
@@ -202,7 +202,12 @@
           "adapterName": "BF_PREBID_S2S",
           "adapterVersion": "0.9.2",
           "ip": "255.255.255.255",
-          "requestId": "six-nine-combo"
+          "requestId": "six-nine-combo",
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
+          }
         }
       },
       "mockResponse": {
diff --git a/adapters/beachfront/beachfronttest/supplemental/two-four-combo.json b/adapters/beachfront/beachfronttest/supplemental/two-four-combo.json
index ac1ca19f..75f37188 100644
--- a/adapters/beachfront/beachfronttest/supplemental/two-four-combo.json
+++ b/adapters/beachfront/beachfronttest/supplemental/two-four-combo.json
@@ -112,7 +112,12 @@
           "adapterName":"BF_PREBID_S2S",
           "adapterVersion":"0.9.2",
           "ip":"255.255.255.255",
-          "requestId":"two-four-combo"
+          "requestId":"two-four-combo",
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
+          }
 
         }
       },
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/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/pangle/pangletest/exemplary/app_banner.json b/adapters/pangle/pangletest/exemplary/app_banner.json
index 3fa410e5..95694eb3 100644
--- a/adapters/pangle/pangletest/exemplary/app_banner.json
+++ b/adapters/pangle/pangletest/exemplary/app_banner.json
@@ -62,6 +62,7 @@
                 "bidder": {
                   "token": "123"
                 },
+                "is_prebid": true,
                 "prebid": null
               }
             }
diff --git a/adapters/pangle/pangletest/exemplary/app_banner_instl.json b/adapters/pangle/pangletest/exemplary/app_banner_instl.json
index 585d155a..1f11229c 100644
--- a/adapters/pangle/pangletest/exemplary/app_banner_instl.json
+++ b/adapters/pangle/pangletest/exemplary/app_banner_instl.json
@@ -64,6 +64,7 @@
                 "bidder": {
                   "token": "123"
                 },
+                "is_prebid": true,
                 "prebid": null
               }
             }
diff --git a/adapters/pangle/pangletest/exemplary/app_native.json b/adapters/pangle/pangletest/exemplary/app_native.json
index 2502baa4..44d3f98d 100644
--- a/adapters/pangle/pangletest/exemplary/app_native.json
+++ b/adapters/pangle/pangletest/exemplary/app_native.json
@@ -52,6 +52,7 @@
                 "bidder": {
                   "token": "123"
                 },
+                "is_prebid": true,
                 "prebid": null
               }
             }
diff --git a/adapters/pangle/pangletest/exemplary/app_video_instl.json b/adapters/pangle/pangletest/exemplary/app_video_instl.json
index d5af392f..778baca5 100644
--- a/adapters/pangle/pangletest/exemplary/app_video_instl.json
+++ b/adapters/pangle/pangletest/exemplary/app_video_instl.json
@@ -76,6 +76,7 @@
                 "bidder": {
                   "token": "123"
                 },
+                "is_prebid": true,
                 "prebid": null
               }
             }
diff --git a/adapters/pangle/pangletest/exemplary/app_video_rewarded.json b/adapters/pangle/pangletest/exemplary/app_video_rewarded.json
index 2dbf08b9..d23dff1e 100644
--- a/adapters/pangle/pangletest/exemplary/app_video_rewarded.json
+++ b/adapters/pangle/pangletest/exemplary/app_video_rewarded.json
@@ -79,6 +79,7 @@
                   "is_rewarded_inventory": 1,
                   "storedrequest": null
                 },
+                "is_prebid": true,
                 "bidder": {
                   "token": "123"
                 }
diff --git a/adapters/pangle/pangletest/params/race/banner.json b/adapters/pangle/pangletest/params/race/banner.json
index afd1684b..4a9da04c 100644
--- a/adapters/pangle/pangletest/params/race/banner.json
+++ b/adapters/pangle/pangletest/params/race/banner.json
@@ -1,3 +1,5 @@
 {
-    "token": "123"
+    "token": "123",
+    "appid": "123",
+    "placementid": "123"
 }
\ No newline at end of file
diff --git a/adapters/pangle/pangletest/params/race/native.json b/adapters/pangle/pangletest/params/race/native.json
index afd1684b..4a9da04c 100644
--- a/adapters/pangle/pangletest/params/race/native.json
+++ b/adapters/pangle/pangletest/params/race/native.json
@@ -1,3 +1,5 @@
 {
-    "token": "123"
+    "token": "123",
+    "appid": "123",
+    "placementid": "123"
 }
\ No newline at end of file
diff --git a/adapters/pangle/pangletest/params/race/video.json b/adapters/pangle/pangletest/params/race/video.json
index afd1684b..4a9da04c 100644
--- a/adapters/pangle/pangletest/params/race/video.json
+++ b/adapters/pangle/pangletest/params/race/video.json
@@ -1,3 +1,5 @@
 {
-    "token": "123"
+    "token": "123",
+    "appid": "123",
+    "placementid": "123"
 }
\ No newline at end of file
diff --git a/adapters/pangle/pangletest/supplemental/appid_placementid_check.json b/adapters/pangle/pangletest/supplemental/appid_placementid_check.json
new file mode 100644
index 00000000..4808cb2b
--- /dev/null
+++ b/adapters/pangle/pangletest/supplemental/appid_placementid_check.json
@@ -0,0 +1,155 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app": {
+      "bundle": "com.prebid"
+    },
+    "device": {
+      "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "protocols": [
+            2,
+            5
+          ],
+          "w": 1024,
+          "h": 576,
+          "ext": {
+            "foo": "bar"
+          }
+        },
+        "ext": {
+          "prebid": {
+            "is_rewarded_inventory": 1
+          },
+          "bidder": {
+            "token": "123",
+            "appid": "123456",
+            "placementid": "78910"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pangle.io/api/get_ads",
+        "headers": {
+          "Content-Type": [
+            "application/json"
+          ],
+          "TOKEN": [
+            "123"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "app": {
+            "bundle": "com.prebid"
+          },
+          "device": {
+            "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "protocols": [
+                  2,
+                  5
+                ],
+                "w": 1024,
+                "h": 576,
+                "ext": {
+                  "foo": "bar"
+                }
+              },
+              "ext": {
+                "adtype": 7,
+                "prebid": {
+                  "bidder": null,
+                  "is_rewarded_inventory": 1,
+                  "storedrequest": null
+                },
+                "is_prebid": true,
+                "networkids": {
+                  "appid": "123456",
+                  "placementid": "78910"
+                },
+                "bidder": {
+                  "token": "123",
+                  "appid": "123456",
+                  "placementid": "78910"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "seat-id",
+              "bid": [
+                {
+                  "id": "1",
+                  "impid": "test-imp-id",
+                  "adid": "11110126",
+                  "price": 0.500000,
+                  "adm": "some-test-ad",
+                  "crid": "test-crid",
+                  "h": 250,
+                  "w": 300,
+                  "ext": {
+                    "pangle": {
+                      "adtype": 7
+                    }
+                  }
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "1",
+            "impid": "test-imp-id",
+            "adid": "11110126",
+            "price": 0.5,
+            "adm": "some-test-ad",
+            "crid": "test-crid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "pangle": {
+                "adtype": 7
+              }
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/pangle/pangletest/supplemental/missing_appid_or_placementid.json b/adapters/pangle/pangletest/supplemental/missing_appid_or_placementid.json
new file mode 100644
index 00000000..929b3c6e
--- /dev/null
+++ b/adapters/pangle/pangletest/supplemental/missing_appid_or_placementid.json
@@ -0,0 +1,47 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app": {
+      "bundle": "com.prebid"
+    },
+    "device": {
+      "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "protocols": [
+            2,
+            5
+          ],
+          "w": 1024,
+          "h": 576,
+          "ext": {
+            "foo": "bar"
+          }
+        },
+        "ext": {
+          "prebid": {
+            "is_rewarded_inventory": 1
+          },
+          "bidder": {
+            "token": "123",
+            "placementid": "78910"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [],
+  "expectedBidResponses": [],
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "only one of appid or placementid is provided",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/pangle/pangletest/supplemental/pangle_ext_check.json b/adapters/pangle/pangletest/supplemental/pangle_ext_check.json
index c9dc5c28..e80c199a 100644
--- a/adapters/pangle/pangletest/supplemental/pangle_ext_check.json
+++ b/adapters/pangle/pangletest/supplemental/pangle_ext_check.json
@@ -62,6 +62,7 @@
                 "bidder": {
                   "token": "123"
                 },
+                "is_prebid": true,
                 "prebid": null
               }
             }
diff --git a/adapters/pangle/pangletest/supplemental/response_code_204.json b/adapters/pangle/pangletest/supplemental/response_code_204.json
index 16c13bdf..a1e940a0 100644
--- a/adapters/pangle/pangletest/supplemental/response_code_204.json
+++ b/adapters/pangle/pangletest/supplemental/response_code_204.json
@@ -62,6 +62,7 @@
                 "bidder": {
                   "token": "123"
                 },
+                "is_prebid": true,
                 "prebid": null
               }
             }
diff --git a/adapters/pangle/pangletest/supplemental/response_code_400.json b/adapters/pangle/pangletest/supplemental/response_code_400.json
index 0a581032..841dba8d 100644
--- a/adapters/pangle/pangletest/supplemental/response_code_400.json
+++ b/adapters/pangle/pangletest/supplemental/response_code_400.json
@@ -62,6 +62,7 @@
                 "bidder": {
                   "token": "123"
                 },
+                "is_prebid": true,
                 "prebid": null
               }
             }
diff --git a/adapters/pangle/pangletest/supplemental/response_code_non_200.json b/adapters/pangle/pangletest/supplemental/response_code_non_200.json
index 0d1447db..b87e0f51 100644
--- a/adapters/pangle/pangletest/supplemental/response_code_non_200.json
+++ b/adapters/pangle/pangletest/supplemental/response_code_non_200.json
@@ -62,6 +62,7 @@
                 "bidder": {
                   "token": "123"
                 },
+                "is_prebid": true,
                 "prebid": null
               }
             }
diff --git a/adapters/pangle/pangletest/supplemental/unrecognized_adtype.json b/adapters/pangle/pangletest/supplemental/unrecognized_adtype.json
index 451c0ed1..613694a9 100644
--- a/adapters/pangle/pangletest/supplemental/unrecognized_adtype.json
+++ b/adapters/pangle/pangletest/supplemental/unrecognized_adtype.json
@@ -62,6 +62,7 @@
                 "bidder": {
                   "token": "123"
                 },
+                "is_prebid": true,
                 "prebid": null
               }
             }
diff --git a/adapters/pangle/param_test.go b/adapters/pangle/param_test.go
index 7b037bd5..5e1d30b3 100644
--- a/adapters/pangle/param_test.go
+++ b/adapters/pangle/param_test.go
@@ -9,6 +9,7 @@ import (
 
 var validParams = []string{
 	`{"token": "SomeAccessToken"}`,
+	`{"token": "SomeAccessToken", "appid": "12345", "placementid": "12345"}`,
 }
 
 var invalidParams = []string{
@@ -16,6 +17,12 @@ var invalidParams = []string{
 	`{"token": 42}`,
 	`{"token": null}`,
 	`{}`,
+	// appid & placementid
+	`{"appid": "12345", "placementid": "12345"}`,
+	`{"token": "SomeAccessToken", "appid": "12345"}`,
+	`{"token": "SomeAccessToken", "placementid": "12345"}`,
+	`{"token": "SomeAccessToken", "appid": 12345, "placementid": 12345}`,
+	`{"token": "SomeAccessToken", "appid": null, "placementid": null}`,
 }
 
 func TestValidParams(t *testing.T) {
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 3a6a7a15..36b94000 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -115,6 +115,7 @@ 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,