Repository: prebid/prebid-server
Pull request #3000 (2023-08-03 07:27:11)

inherit fields for an alias from parent adapter
This PR makes the following changes - 
- It introduces a new struct `bidderInfoNullableFields` to maintain any nullable fields from BidderInfo struct. For example - the default value of `Disabled` in BidderInfo struct is always false and not `nil`.
- It may be the case where `Disabled` value of parent adapter is `true` but no value is set for an aliased adapter(default is false). In this case, we cannot just outright replace `Disabled` value of alias adapter with parent. Consider the case where aliased adapter `Disabled` value is actually set.
- To handle cases like above, we have introduced `bidderInfoNullableFields` struct.
- The approach is to maintain a map of bidderName to `bidderInfoNullableFields`. 
- Loop information from that map to determine if `Disabled` is actually set in aliased adapter or not since pointer values are defaulted to nil in golang. We do not have more than 20 aliases as of today so this loop is not expensive.
- This PR just inherits the fields from parent adapter to aliased adapter. In the subsequent PR, we will be handling the cases where nullable fields of BidderInfo struct is set.


Patch:
diff --git a/adapters/adhese/adhese.go b/adapters/adhese/adhese.go
index 429dab23..9c84676c 100644
--- a/adapters/adhese/adhese.go
+++ b/adapters/adhese/adhese.go
@@ -10,7 +10,6 @@ import (
 	"strings"
 	"text/template"
 
-	"github.com/golang/glog"
 	"github.com/prebid/openrtb/v19/openrtb2"
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/config"
@@ -201,7 +200,6 @@ func (a *AdheseAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalR
 func convertAdheseBid(adheseBid AdheseBid, adheseExt AdheseExt, adheseOriginData AdheseOriginData) openrtb2.BidResponse {
 	adheseExtJson, err := json.Marshal(adheseOriginData)
 	if err != nil {
-		glog.Error(fmt.Sprintf("Unable to parse adhese Origin Data as JSON due to %v", err))
 		adheseExtJson = make([]byte, 0)
 	}
 	return openrtb2.BidResponse{
diff --git a/adapters/adxcg/adxcg.go b/adapters/adxcg/adxcg.go
index 00874391..6b489d32 100644
--- a/adapters/adxcg/adxcg.go
+++ b/adapters/adxcg/adxcg.go
@@ -84,13 +84,13 @@ func (adapter *adapter) MakeBids(
 		return nil, []error{err}
 	}
 
-	bidsCapacity := len(openRTBBidderResponse.SeatBid[0].Bid)
-	bidderResponse = adapters.NewBidderResponseWithBidsCapacity(bidsCapacity)
+	bidderResponse = adapters.NewBidderResponseWithBidsCapacity(len(openRTBRequest.Imp))
+	bidderResponse.Currency = openRTBBidderResponse.Cur
 	var typedBid *adapters.TypedBid
 	for _, seatBid := range openRTBBidderResponse.SeatBid {
 		for _, bid := range seatBid.Bid {
 			activeBid := bid
-			bidType, err := getMediaTypeForImp(activeBid.ImpID, openRTBRequest.Imp)
+			bidType, err := getReturnTypeFromMtypeForImp(activeBid.MType)
 			if err != nil {
 				errs = append(errs, err)
 				continue
@@ -105,21 +105,17 @@ func (adapter *adapter) MakeBids(
 
 }
 
-func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
-	for _, imp := range imps {
-		if imp.ID == impID {
-			if imp.Native != nil {
-				return openrtb_ext.BidTypeNative, nil
-			} else if imp.Banner != nil {
-				return openrtb_ext.BidTypeBanner, nil
-			} else if imp.Video != nil {
-				return openrtb_ext.BidTypeVideo, nil
-			}
-
-		}
-	}
-
-	return "", &errortypes.BadInput{
-		Message: fmt.Sprintf("Failed to find native/banner/video impression \"%s\" ", impID),
+func getReturnTypeFromMtypeForImp(mType openrtb2.MarkupType) (openrtb_ext.BidType, error) {
+	switch mType {
+	case openrtb2.MarkupBanner:
+		return openrtb_ext.BidTypeBanner, nil
+	case openrtb2.MarkupVideo:
+		return openrtb_ext.BidTypeVideo, nil
+	case openrtb2.MarkupNative:
+		return openrtb_ext.BidTypeNative, nil
+	case openrtb2.MarkupAudio:
+		return openrtb_ext.BidTypeAudio, nil
+	default:
+		return "", &errortypes.BadServerResponse{Message: "Unsupported return type"}
 	}
 }
diff --git a/adapters/axis/axis.go b/adapters/axis/axis.go
index b9901c6c..f03a572e 100644
--- a/adapters/axis/axis.go
+++ b/adapters/axis/axis.go
@@ -4,18 +4,16 @@ import (
 	"encoding/json"
 	"fmt"
 	"net/http"
-	"text/template"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
-	"github.com/prebid/prebid-server/macros"
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
 type adapter struct {
-	endpoint *template.Template
+	endpoint string
 }
 
 type reqBodyExt struct {
@@ -23,13 +21,8 @@ type reqBodyExt struct {
 }
 
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
-	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
-	if err != nil {
-		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
-	}
-
 	bidder := &adapter{
-		endpoint: template,
+		endpoint: config.Endpoint,
 	}
 
 	return bidder, nil
@@ -49,11 +42,6 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E
 			continue
 		}
 
-		url, err := a.buildEndpointURL(&bidderExt)
-		if err != nil {
-			return nil, []error{err}
-		}
-
 		extJson, err := json.Marshal(bidderExt)
 		if err != nil {
 			return nil, []error{err}
@@ -61,7 +49,7 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E
 
 		request.Imp[0].Ext = extJson
 
-		adapterReq, err := buildRequest(request, url)
+		adapterReq, err := a.buildRequest(request)
 		if err != nil {
 			return nil, []error{err}
 		}
@@ -74,16 +62,7 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E
 	return adapterRequests, nil
 }
 
-func (a *adapter) buildEndpointURL(bidderExt *reqBodyExt) (string, error) {
-	endpointParams := macros.EndpointTemplateParams{
-		AccountID: bidderExt.AxisBidderExt.Integration,
-		SourceId:  bidderExt.AxisBidderExt.Token,
-	}
-
-	return macros.ResolveMacros(a.endpoint, endpointParams)
-}
-
-func buildRequest(request *openrtb2.BidRequest, url string) (*adapters.RequestData, error) {
+func (a *adapter) buildRequest(request *openrtb2.BidRequest) (*adapters.RequestData, error) {
 	reqJSON, err := json.Marshal(request)
 	if err != nil {
 		return nil, err
@@ -95,7 +74,7 @@ func buildRequest(request *openrtb2.BidRequest, url string) (*adapters.RequestDa
 
 	return &adapters.RequestData{
 		Method:  "POST",
-		Uri:     url,
+		Uri:     a.endpoint,
 		Body:    reqJSON,
 		Headers: headers,
 	}, nil
diff --git a/adapters/gamoshi/gamoshi.go b/adapters/gamoshi/gamoshi.go
index d130ce96..10c5127e 100644
--- a/adapters/gamoshi/gamoshi.go
+++ b/adapters/gamoshi/gamoshi.go
@@ -6,7 +6,6 @@ import (
 	"net/http"
 	"strconv"
 
-	"github.com/golang/glog"
 	"github.com/prebid/openrtb/v19/openrtb2"
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/config"
@@ -48,7 +47,6 @@ func (a *GamoshiAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada
 			err := &errortypes.BadInput{
 				Message: fmt.Sprintf("Gamoshi only supports banner and video media types. Ignoring imp id=%s", request.Imp[i].ID),
 			}
-			glog.Warning("Gamoshi SUPPORT VIOLATION: only banner and video media types supported")
 			errs = append(errs, err)
 			request.Imp = append(request.Imp[:i], request.Imp[i+1:]...)
 			i--
diff --git a/adapters/lm_kiviads/lmkiviads.go b/adapters/lm_kiviads/lmkiviads.go
new file mode 100644
index 00000000..c121be3b
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviads.go
@@ -0,0 +1,161 @@
+package lmkiviads
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"text/template"
+
+	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type bidType struct {
+	Type string `json:"type"`
+}
+
+type bidExt struct {
+	Prebid bidType `json:"prebid"`
+}
+
+type adapter struct {
+	endpoint *template.Template
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	tmpl, err := template.New("endpointTemplate").Parse(config.Endpoint)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse endpoint URL template: %v", err)
+	}
+
+	bidder := &adapter{
+		endpoint: tmpl,
+	}
+
+	return bidder, nil
+}
+
+func (a *adapter) buildEndpointFromRequest(imp *openrtb2.Imp) (string, error) {
+	var impExt adapters.ExtImpBidder
+	if err := json.Unmarshal(imp.Ext, &impExt); err != nil {
+		return "", &errortypes.BadInput{
+			Message: fmt.Sprintf("Failed to deserialize bidder impression extension: %v", err),
+		}
+	}
+
+	var kiviExt openrtb_ext.ExtLmKiviads
+	if err := json.Unmarshal(impExt.Bidder, &kiviExt); err != nil {
+		return "", &errortypes.BadInput{
+			Message: fmt.Sprintf("Failed to deserialize LmKiviads extension: %v", err),
+		}
+	}
+
+	endpointParams := macros.EndpointTemplateParams{
+		Host:     kiviExt.Env,
+		SourceId: kiviExt.Pid,
+	}
+
+	return macros.ResolveMacros(a.endpoint, endpointParams)
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var requests []*adapters.RequestData
+	var errs []error
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+
+	requestCopy := *request
+	for _, imp := range request.Imp {
+		requestCopy.Imp = []openrtb2.Imp{imp}
+
+		endpoint, err := a.buildEndpointFromRequest(&imp)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		requestJSON, err := json.Marshal(requestCopy)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		request := &adapters.RequestData{
+			Method:  http.MethodPost,
+			Body:    requestJSON,
+			Uri:     endpoint,
+			Headers: headers,
+		}
+
+		requests = append(requests, request)
+	}
+
+	return requests, errs
+}
+
+func (a *adapter) MakeBids(openRTBRequest *openrtb2.BidRequest, requestToBidder *adapters.RequestData, bidderRawResponse *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if adapters.IsResponseStatusCodeNoContent(bidderRawResponse) {
+		return nil, nil
+	}
+
+	if bidderRawResponse.StatusCode == http.StatusServiceUnavailable {
+		return nil, []error{&errortypes.BadInput{
+			Message: "Bidder LmKiviads is unavailable. Please contact the bidder support.",
+		}}
+	}
+
+	if err := adapters.CheckResponseStatusCodeForErrors(bidderRawResponse); err != nil {
+		return nil, []error{err}
+	}
+
+	var bidResp openrtb2.BidResponse
+	if err := json.Unmarshal(bidderRawResponse.Body, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+
+	if len(bidResp.SeatBid) == 0 {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Array SeatBid cannot be empty",
+		}}
+	}
+
+	return prepareBidResponse(bidResp.SeatBid)
+}
+
+func prepareBidResponse(seats []openrtb2.SeatBid) (*adapters.BidderResponse, []error) {
+	errs := []error{}
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(seats))
+
+	for _, seatBid := range seats {
+		for bidId, bid := range seatBid.Bid {
+			var bidExt bidExt
+			if err := json.Unmarshal(bid.Ext, &bidExt); err != nil {
+				errs = append(errs, &errortypes.BadServerResponse{
+					Message: fmt.Sprintf("Failed to parse Bid[%d].Ext: %s", bidId, err.Error()),
+				})
+				continue
+			}
+
+			bidType, err := openrtb_ext.ParseBidType(bidExt.Prebid.Type)
+			if err != nil {
+				errs = append(errs, &errortypes.BadServerResponse{
+					Message: fmt.Sprintf("Bid[%d].Ext.Prebid.Type expects one of the following values: 'banner', 'native', 'video', 'audio', got '%s'", bidId, bidExt.Prebid.Type),
+				})
+				continue
+			}
+
+			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+				Bid:     &seatBid.Bid[bidId],
+				BidType: bidType,
+			})
+		}
+	}
+
+	return bidResponse, errs
+}
diff --git a/adapters/pubmatic/pubmatic.go b/adapters/pubmatic/pubmatic.go
index 8084b2bc..04bd00cb 100644
--- a/adapters/pubmatic/pubmatic.go
+++ b/adapters/pubmatic/pubmatic.go
@@ -10,7 +10,6 @@ import (
 	"strings"
 
 	"github.com/buger/jsonparser"
-	"github.com/golang/glog"
 	"github.com/prebid/openrtb/v19/openrtb2"
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/config"
@@ -390,7 +389,6 @@ func getAlternateBidderCodesFromRequestExt(reqExt *openrtb_ext.ExtRequest) []str
 func addKeywordsToExt(keywords []*openrtb_ext.ExtImpPubmaticKeyVal, extMap map[string]interface{}) {
 	for _, keyVal := range keywords {
 		if len(keyVal.Values) == 0 {
-			logf("No values present for key = %s", keyVal.Key)
 			continue
 		} else {
 			key := keyVal.Key
@@ -614,12 +612,6 @@ func getBidType(bidExt *pubmaticBidExt) openrtb_ext.BidType {
 	return bidType
 }
 
-func logf(msg string, args ...interface{}) {
-	if glog.V(2) {
-		glog.Infof(msg, args...)
-	}
-}
-
 // Builder builds a new instance of the Pubmatic adapter for the given bidder with the given config.
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
 	bidder := &PubmaticAdapter{
diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go
index 99072255..4c625bd0 100644
--- a/adapters/rubicon/rubicon.go
+++ b/adapters/rubicon/rubicon.go
@@ -1047,8 +1047,11 @@ func resolveNativeObject(native *openrtb2.Native, target map[string]interface{})
 		return nil, fmt.Errorf("Eventtrackers are not present or not of array type")
 	}
 
-	if _, ok := target["context"].(float64); !ok {
-		return nil, fmt.Errorf("Context is not present or not of int type")
+	context := target["context"]
+	if context != nil {
+		if _, ok := context.(float64); !ok {
+			return nil, fmt.Errorf("Context is not of int type")
+		}
 	}
 
 	if _, ok := target["plcmttype"].(float64); !ok {
diff --git a/adapters/vrtcal/vrtcal.go b/adapters/vrtcal/vrtcal.go
index 65e20b21..ab47eddb 100644
--- a/adapters/vrtcal/vrtcal.go
+++ b/adapters/vrtcal/vrtcal.go
@@ -71,7 +71,7 @@ func (a *VrtcalAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalR
 	var errs []error
 	for _, sb := range bidResp.SeatBid {
 		for i := range sb.Bid {
-			bidType, err := getReturnTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp)
+			bidType, err := getReturnTypeForImp(sb.Bid[i].MType)
 			if err == nil {
 				bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
 					Bid:     &sb.Bid[i],
@@ -94,25 +94,15 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server co
 	return bidder, nil
 }
 
-func getReturnTypeForImp(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
-			}
-
-			if imp.Video != nil {
-				return openrtb_ext.BidTypeVideo, nil
-			}
-
-			return "", &errortypes.BadServerResponse{
-				Message: fmt.Sprintf("Unsupported return type for ID: \"%s\"", impID),
-			}
-		}
-	}
-
-	//Failsafe default in case impression ID is not found
-	return "", &errortypes.BadServerResponse{
-		Message: fmt.Sprintf("Failed to find impression for ID: \"%s\"", impID),
+func getReturnTypeForImp(mType openrtb2.MarkupType) (openrtb_ext.BidType, error) {
+	if mType == openrtb2.MarkupBanner {
+		return openrtb_ext.BidTypeBanner, nil
+	} else if mType == openrtb2.MarkupVideo {
+		return openrtb_ext.BidTypeVideo, nil
+	} else if mType == openrtb2.MarkupNative {
+		return openrtb_ext.BidTypeNative, nil
+	} else {
+		return "", &errortypes.BadServerResponse{
+			Message: "Unsupported return type"}
 	}
 }
diff --git a/adapters/yeahmobi/yeahmobi.go b/adapters/yeahmobi/yeahmobi.go
index 30792258..d25b2eab 100644
--- a/adapters/yeahmobi/yeahmobi.go
+++ b/adapters/yeahmobi/yeahmobi.go
@@ -7,7 +7,6 @@ import (
 	"net/url"
 	"text/template"
 
-	"github.com/golang/glog"
 	"github.com/prebid/openrtb/v19/openrtb2"
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/config"
@@ -50,7 +49,6 @@ func (adapter *YeahmobiAdapter) makeRequest(request *openrtb2.BidRequest) (*adap
 	yeahmobiExt, errs := getYeahmobiExt(request)
 
 	if yeahmobiExt == nil {
-		glog.Fatal("Invalid ExtImpYeahmobi value")
 		return nil, errs
 	}
 	endPoint, err := adapter.getEndpoint(yeahmobiExt)
diff --git a/config/bidderinfo.go b/config/bidderinfo.go
index 630952a6..02830733 100644
--- a/config/bidderinfo.go
+++ b/config/bidderinfo.go
@@ -52,6 +52,13 @@ type BidderInfo struct {
 	OpenRTB             *OpenRTBInfo `yaml:"openrtb" mapstructure:"openrtb"`
 }
 
+type aliasNillableFields struct {
+	Disabled                *bool                 `yaml:"disabled" mapstructure:"disabled"`
+	ModifyingVastXmlAllowed *bool                 `yaml:"modifyingVastXmlAllowed" mapstructure:"modifyingVastXmlAllowed"`
+	Experiment              *BidderInfoExperiment `yaml:"experiment" mapstructure:"experiment"`
+	XAPI                    *AdapterXAPI          `yaml:"xapi" mapstructure:"xapi"`
+}
+
 // BidderInfoExperiment specifies non-production ready feature config for a bidder
 type BidderInfoExperiment struct {
 	AdsCert BidderAdsCert `yaml:"adsCert" mapstructure:"adsCert"`
@@ -229,8 +236,8 @@ func processBidderInfos(reader InfoReader, normalizeBidderName func(string) (ope
 		return nil, fmt.Errorf("error loading bidders data")
 	}
 
-	infos := BidderInfos{}
-
+	bidderInfos := BidderInfos{}
+	aliasNillableFieldsByBidder := map[string]aliasNillableFields{}
 	for fileName, data := range bidderConfigs {
 		bidderName := strings.Split(fileName, ".")
 		if len(bidderName) == 2 && bidderName[1] == "yaml" {
@@ -243,10 +250,52 @@ func processBidderInfos(reader InfoReader, normalizeBidderName func(string) (ope
 				return nil, fmt.Errorf("error parsing config for bidder %s: %v", fileName, err)
 			}
 
-			infos[string(normalizedBidderName)] = info
+			bidderInfos[string(normalizedBidderName)] = info
+
+			//need to maintain nullable fields from BidderInfo struct into bidderInfoNullableFields
+			//to handle the default values in aliases yaml
+			if len(info.AliasOf) > 0 {
+				aliasFields := aliasNillableFields{}
+				if err := yaml.Unmarshal(data, &aliasFields); err != nil {
+					return nil, fmt.Errorf("error parsing config for aliased bidder %s: %v", fileName, err)
+				}
+
+				aliasNillableFieldsByBidder[string(normalizedBidderName)] = aliasFields
+			}
 		}
 	}
-	return infos, nil
+	return processBidderAliases(aliasNillableFieldsByBidder, bidderInfos)
+}
+
+func processBidderAliases(aliasNillableFieldsByBidder map[string]aliasNillableFields, bidderInfos BidderInfos) (BidderInfos, error) {
+	for bidderName := range aliasNillableFieldsByBidder {
+		aliasBidderInfo, ok := bidderInfos[bidderName]
+		if !ok {
+			return nil, fmt.Errorf("bidder info not found for an alias: %s", bidderName)
+		}
+		if err := validateAliases(aliasBidderInfo, bidderInfos, bidderName); err != nil {
+			return nil, err
+		}
+		parentBidderInfo := bidderInfos[aliasBidderInfo.AliasOf]
+		aliasBidderInfo.AppSecret = parentBidderInfo.AppSecret
+		aliasBidderInfo.Capabilities = parentBidderInfo.Capabilities
+		aliasBidderInfo.Debug = parentBidderInfo.Debug
+		aliasBidderInfo.Disabled = parentBidderInfo.Disabled
+		aliasBidderInfo.Endpoint = parentBidderInfo.Endpoint
+		aliasBidderInfo.EndpointCompression = parentBidderInfo.EndpointCompression
+		aliasBidderInfo.Experiment = parentBidderInfo.Experiment
+		aliasBidderInfo.ExtraAdapterInfo = parentBidderInfo.ExtraAdapterInfo
+		aliasBidderInfo.GVLVendorID = parentBidderInfo.GVLVendorID
+		aliasBidderInfo.Maintainer = parentBidderInfo.Maintainer
+		aliasBidderInfo.ModifyingVastXmlAllowed = parentBidderInfo.ModifyingVastXmlAllowed
+		aliasBidderInfo.OpenRTB = parentBidderInfo.OpenRTB
+		aliasBidderInfo.PlatformID = parentBidderInfo.PlatformID
+		aliasBidderInfo.Syncer = parentBidderInfo.Syncer
+		aliasBidderInfo.UserSyncURL = parentBidderInfo.UserSyncURL
+		aliasBidderInfo.XAPI = parentBidderInfo.XAPI
+		bidderInfos[bidderName] = aliasBidderInfo
+	}
+	return bidderInfos, nil
 }
 
 // ToGVLVendorIDMap transforms a BidderInfos object to a map of bidder names to GVL id.
@@ -274,23 +323,19 @@ func (infos BidderInfos) validate(errs []error) []error {
 			if err := validateSyncer(bidder); err != nil {
 				errs = append(errs, err)
 			}
-
-			if err := validateAliases(bidder, infos, bidderName); err != nil {
-				errs = append(errs, err)
-			}
 		}
 	}
 	return errs
 }
 
-func validateAliases(bidder BidderInfo, infos BidderInfos, bidderName string) error {
-	if len(bidder.AliasOf) > 0 {
-		if parentBidder, ok := infos[bidder.AliasOf]; ok {
+func validateAliases(aliasBidderInfo BidderInfo, infos BidderInfos, bidderName string) error {
+	if len(aliasBidderInfo.AliasOf) > 0 {
+		if parentBidder, ok := infos[aliasBidderInfo.AliasOf]; ok {
 			if len(parentBidder.AliasOf) > 0 {
-				return fmt.Errorf("bidder: %s cannot be an alias of an alias: %s", bidder.AliasOf, bidderName)
+				return fmt.Errorf("bidder: %s cannot be an alias of an alias: %s", aliasBidderInfo.AliasOf, bidderName)
 			}
 		} else {
-			return fmt.Errorf("bidder: %s not found for an alias: %s", bidder.AliasOf, bidderName)
+			return fmt.Errorf("bidder: %s not found for an alias: %s", aliasBidderInfo.AliasOf, bidderName)
 		}
 	}
 	return nil
diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index 49ef7074..f0299d2e 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -528,7 +528,6 @@ func (p usersyncPrivacy) CCPAAllowsBidderSync(bidder string) bool {
 }
 
 func (p usersyncPrivacy) ActivityAllowsUserSync(bidder string) bool {
-	activityResult := p.activityControl.Allow(privacy.ActivitySyncUser,
+	return p.activityControl.Allow(privacy.ActivitySyncUser,
 		privacy.ScopedName{Scope: privacy.ScopeTypeBidder, Name: bidder})
-	return activityResult == privacy.ActivityAllow
 }
diff --git a/endpoints/setuid.go b/endpoints/setuid.go
index 8f691389..58b6f8db 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -113,7 +113,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
 
 		userSyncActivityAllowed := activities.Allow(privacy.ActivitySyncUser,
 			privacy.ScopedName{Scope: privacy.ScopeTypeBidder, Name: bidderName})
-		if userSyncActivityAllowed == privacy.ActivityDeny {
+		if !userSyncActivityAllowed {
 			w.WriteHeader(http.StatusUnavailableForLegalReasons)
 			return
 		}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index ac004cc4..17290917 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -105,6 +105,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/krushmedia"
 	"github.com/prebid/prebid-server/adapters/kubient"
 	"github.com/prebid/prebid-server/adapters/limelightDigital"
+	lmkiviads "github.com/prebid/prebid-server/adapters/lm_kiviads"
 	"github.com/prebid/prebid-server/adapters/lockerdome"
 	"github.com/prebid/prebid-server/adapters/logan"
 	"github.com/prebid/prebid-server/adapters/logicad"
@@ -245,6 +246,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderBmtm:              bmtm.Builder,
 		openrtb_ext.BidderBoldwin:           boldwin.Builder,
 		openrtb_ext.BidderBrave:             brave.Builder,
+		openrtb_ext.BidderCadentApertureMX:  emx_digital.Builder,
 		openrtb_ext.BidderCcx:               ccx.Builder,
 		openrtb_ext.BidderCoinzilla:         coinzilla.Builder,
 		openrtb_ext.BidderColossus:          colossus.Builder,
@@ -297,6 +299,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderKayzen:            kayzen.Builder,
 		openrtb_ext.BidderKidoz:             kidoz.Builder,
 		openrtb_ext.BidderKiviads:           kiviads.Builder,
+		openrtb_ext.BidderLmKiviads:         lmkiviads.Builder,
 		openrtb_ext.BidderKrushmedia:        krushmedia.Builder,
 		openrtb_ext.BidderKubient:           kubient.Builder,
 		openrtb_ext.BidderLimelightDigital:  limelightDigital.Builder,
diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go
index 624a22ce..c863f52a 100644
--- a/exchange/adapter_util.go
+++ b/exchange/adapter_util.go
@@ -86,9 +86,8 @@ func GetActiveBidders(infos config.BidderInfos) map[string]openrtb_ext.BidderNam
 	return activeBidders
 }
 
-// GetDisabledBiddersErrorMessages returns a map of error messages for disabled bidders.
-func GetDisabledBiddersErrorMessages(infos config.BidderInfos) map[string]string {
-	disabledBidders := map[string]string{
+func GetDisabledBidderWarningMessages(infos config.BidderInfos) map[string]string {
+	removed := map[string]string{
 		"lifestreet":     `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`,
 		"adagio":         `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`,
 		"somoaudience":   `Bidder "somoaudience" is no longer available in Prebid Server. Please update your configuration.`,
@@ -100,6 +99,12 @@ func GetDisabledBiddersErrorMessages(infos config.BidderInfos) map[string]string
 		"brightroll":     `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`,
 	}
 
+	return mergeRemovedAndDisabledBidderWarningMessages(removed, infos)
+}
+
+func mergeRemovedAndDisabledBidderWarningMessages(removed map[string]string, infos config.BidderInfos) map[string]string {
+	disabledBidders := removed
+
 	for name, info := range infos {
 		if info.Disabled {
 			msg := fmt.Sprintf(`Bidder "%s" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`, name)
diff --git a/exchange/utils.go b/exchange/utils.go
index e70f5249..17bba334 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -154,7 +154,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 		// fetchBids activity
 		scopedName := privacy.ScopedName{Scope: privacy.ScopeTypeBidder, Name: bidderRequest.BidderName.String()}
 		fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids, scopedName)
-		if fetchBidsActivityAllowed == privacy.ActivityDeny {
+		if !fetchBidsActivityAllowed {
 			// skip the call to a bidder if fetchBids activity is not allowed
 			// do not add this bidder to allowedBidderRequests
 			continue
@@ -174,7 +174,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 		}
 
 		passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName)
-		if passIDActivityAllowed == privacy.ActivityDeny {
+		if !passIDActivityAllowed {
 			privacyEnforcement.UFPD = true
 		} else {
 			// run existing policies (GDPR, CCPA, COPPA, LMT)
@@ -191,7 +191,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 		}
 
 		passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName)
-		if passGeoActivityAllowed == privacy.ActivityDeny {
+		if !passGeoActivityAllowed {
 			privacyEnforcement.PreciseGeo = true
 		} else {
 			// run existing policies (GDPR, CCPA, COPPA, LMT)
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 26eaa422..da262ea1 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -142,6 +142,7 @@ const (
 	BidderBmtm              BidderName = "bmtm"
 	BidderBoldwin           BidderName = "boldwin"
 	BidderBrave             BidderName = "brave"
+	BidderCadentApertureMX  BidderName = "cadent_aperture_mx"
 	BidderCcx               BidderName = "ccx"
 	BidderCoinzilla         BidderName = "coinzilla"
 	BidderColossus          BidderName = "colossus"
@@ -194,6 +195,7 @@ const (
 	BidderKayzen            BidderName = "kayzen"
 	BidderKidoz             BidderName = "kidoz"
 	BidderKiviads           BidderName = "kiviads"
+	BidderLmKiviads         BidderName = "lm_kiviads"
 	BidderKrushmedia        BidderName = "krushmedia"
 	BidderKubient           BidderName = "kubient"
 	BidderLimelightDigital  BidderName = "limelightDigital"
@@ -345,6 +347,7 @@ func CoreBidderNames() []BidderName {
 		BidderBmtm,
 		BidderBoldwin,
 		BidderBrave,
+		BidderCadentApertureMX,
 		BidderCcx,
 		BidderCoinzilla,
 		BidderColossus,
@@ -397,6 +400,7 @@ func CoreBidderNames() []BidderName {
 		BidderKayzen,
 		BidderKidoz,
 		BidderKiviads,
+		BidderLmKiviads,
 		BidderKrushmedia,
 		BidderKubient,
 		BidderLimelightDigital,
diff --git a/openrtb_ext/imp_lmkiviads.go b/openrtb_ext/imp_lmkiviads.go
new file mode 100644
index 00000000..8638749b
--- /dev/null
+++ b/openrtb_ext/imp_lmkiviads.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ExtLmKiviads struct {
+	Env string `json:"env"`
+	Pid string `json:"pid"`
+}
diff --git a/privacy/enforcer.go b/privacy/enforcer.go
index bcd9e948..aeaaab8d 100644
--- a/privacy/enforcer.go
+++ b/privacy/enforcer.go
@@ -2,8 +2,9 @@ package privacy
 
 import (
 	"fmt"
-	"github.com/prebid/prebid-server/config"
 	"strings"
+
+	"github.com/prebid/prebid-server/config"
 )
 
 type ActivityResult int
@@ -22,6 +23,8 @@ const (
 	ScopeTypeGeneral   = "general"
 )
 
+const defaultActivityResult = true
+
 type ActivityControl struct {
 	plans map[Activity]ActivityPlan
 }
@@ -86,15 +89,24 @@ func buildEnforcementPlan(activity config.Activity) (ActivityPlan, error) {
 }
 
 func activityRulesToEnforcementRules(rules []config.ActivityRule) ([]ActivityRule, error) {
-	enfRules := make([]ActivityRule, 0)
+	var enfRules []ActivityRule
+
 	for _, r := range rules {
-		cmpName, err := conditionToRuleComponentName(r.Condition.ComponentName)
+		var result ActivityResult
+		if r.Allow {
+			result = ActivityAllow
+		} else {
+			result = ActivityDeny
+		}
+
+		componentName, err := conditionToRuleComponentNames(r.Condition.ComponentName)
 		if err != nil {
 			return nil, err
 		}
+
 		er := ComponentEnforcementRule{
-			allowed:       r.Allow,
-			componentName: cmpName,
+			result:        result,
+			componentName: componentName,
 			componentType: r.Condition.ComponentType,
 		}
 		enfRules = append(enfRules, er)
@@ -102,7 +114,7 @@ func activityRulesToEnforcementRules(rules []config.ActivityRule) ([]ActivityRul
 	return enfRules, nil
 }
 
-func conditionToRuleComponentName(conditions []string) ([]ScopedName, error) {
+func conditionToRuleComponentNames(conditions []string) ([]ScopedName, error) {
 	sn := make([]ScopedName, 0)
 	for _, condition := range conditions {
 		scope, err := NewScopedName(condition)
@@ -114,87 +126,91 @@ func conditionToRuleComponentName(conditions []string) ([]ScopedName, error) {
 	return sn, nil
 }
 
-func activityDefaultToDefaultResult(activityDefault *bool) ActivityResult {
+func activityDefaultToDefaultResult(activityDefault *bool) bool {
 	if activityDefault == nil {
 		// if default is unspecified, the hardcoded default-default is true.
-		return ActivityAllow
-	} else if *activityDefault {
-		return ActivityAllow
+		return defaultActivityResult
 	}
-	return ActivityDeny
+	return *activityDefault
 }
 
-func (e ActivityControl) Allow(activity Activity, target ScopedName) ActivityResult {
+func (e ActivityControl) Allow(activity Activity, target ScopedName) bool {
 	plan, planDefined := e.plans[activity]
 
 	if !planDefined {
-		return ActivityAbstain
+		return defaultActivityResult
 	}
 
-	return plan.Allow(target)
+	return plan.Evaluate(target)
 }
 
 type ActivityPlan struct {
-	defaultResult ActivityResult
+	defaultResult bool
 	rules         []ActivityRule
 }
 
-func (p ActivityPlan) Allow(target ScopedName) ActivityResult {
+func (p ActivityPlan) Evaluate(target ScopedName) bool {
 	for _, rule := range p.rules {
-		result := rule.Allow(target)
+		result := rule.Evaluate(target)
 		if result == ActivityDeny || result == ActivityAllow {
-			return result
+			return result == ActivityAllow
 		}
 	}
 	return p.defaultResult
 }
 
 type ActivityRule interface {
-	Allow(target ScopedName) ActivityResult
+	Evaluate(target ScopedName) ActivityResult
 }
 
 type ComponentEnforcementRule struct {
+	result        ActivityResult
 	componentName []ScopedName
 	componentType []string
-	// include gppSectionId from 3.5
-	// include geo from 3.5
-	allowed bool
 }
 
-func (r ComponentEnforcementRule) Allow(target ScopedName) ActivityResult {
-	if len(r.componentName) == 0 && len(r.componentType) == 0 {
+func (r ComponentEnforcementRule) Evaluate(target ScopedName) ActivityResult {
+	if matched := evaluateComponentName(target, r.componentName); !matched {
+		return ActivityAbstain
+	}
+
+	if matched := evaluateComponentType(target, r.componentType); !matched {
 		return ActivityAbstain
 	}
 
-	nameClauseExists := len(r.componentName) > 0
-	typeClauseExists := len(r.componentType) > 0
+	return r.result
+}
 
-	componentNameFound := false
-	for _, scope := range r.componentName {
-		if strings.EqualFold(scope.Scope, target.Scope) &&
-			(scope.Name == "*" || strings.EqualFold(scope.Name, target.Name)) {
-			componentNameFound = true
-			break
-		}
+func evaluateComponentName(target ScopedName, componentNames []ScopedName) bool {
+	// no clauses are considered a match
+	if len(componentNames) == 0 {
+		return true
 	}
 
-	componentTypeFound := false
-	for _, componentType := range r.componentType {
-		if strings.EqualFold(componentType, target.Scope) {
-			componentTypeFound = true
-			break
+	// if there are clauses, at least one needs to match
+	for _, n := range componentNames {
+		if strings.EqualFold(n.Scope, target.Scope) && (n.Name == "*" || strings.EqualFold(n.Name, target.Name)) {
+			return true
 		}
 	}
-	// behavior if rule matches: can be either true=allow or false=deny. result is abstain if the rule doesn't match
-	matchFound := (componentNameFound || !nameClauseExists) && (componentTypeFound || !typeClauseExists)
-	if matchFound {
-		if r.allowed {
-			return ActivityAllow
-		} else {
-			return ActivityDeny
+
+	return false
+}
+
+func evaluateComponentType(target ScopedName, componentTypes []string) bool {
+	// no clauses are considered a match
+	if len(componentTypes) == 0 {
+		return true
+	}
+
+	// if there are clauses, at least one needs to match
+	for _, s := range componentTypes {
+		if strings.EqualFold(s, target.Scope) {
+			return true
 		}
 	}
-	return ActivityAbstain
+
+	return false
 }
 
 type ScopedName struct {
diff --git a/router/router.go b/router/router.go
index 12bfbf59..fcf674b4 100644
--- a/router/router.go
+++ b/router/router.go
@@ -190,7 +190,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	}
 
 	activeBidders := exchange.GetActiveBidders(cfg.BidderInfos)
-	disabledBidders := exchange.GetDisabledBiddersErrorMessages(cfg.BidderInfos)
+	disabledBidders := exchange.GetDisabledBidderWarningMessages(cfg.BidderInfos)
 
 	defaultAliases, defReqJSON := readDefaultRequest(cfg.DefReqConfig)
 	if err := validateDefaultAliases(defaultAliases); err != nil {
diff --git a/static/bidder-info/axis.yaml b/static/bidder-info/axis.yaml
index 494091a1..fe04b601 100644
--- a/static/bidder-info/axis.yaml
+++ b/static/bidder-info/axis.yaml
@@ -1,4 +1,4 @@
-endpoint: "http://prebid.axis-marketplace.com/{{.AccountID}}?token={{.SourceId}}"
+endpoint: "http://prebid.axis-marketplace.com/pbserver"
 maintainer:
   email: "help@axis-marketplace.com"
 capabilities:
diff --git a/static/bidder-info/cadent_aperture_mx.yaml b/static/bidder-info/cadent_aperture_mx.yaml
new file mode 100644
index 00000000..b5c72bd9
--- /dev/null
+++ b/static/bidder-info/cadent_aperture_mx.yaml
@@ -0,0 +1,17 @@
+endpoint: "https://hb.emxdgt.com"
+maintainer:
+  email: "contactaperturemx@cadent.tv"
+gvlVendorID: 183
+capabilities:
+  site:
+    mediaTypes:
+      - banner
+      - video
+  app:
+    mediaTypes:
+      - banner
+      - video
+userSync:
+  iframe:
+    url: "https://cs.emxdgt.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}"
+    userMacro: "$UID"
diff --git a/static/bidder-info/emx_digital.yaml b/static/bidder-info/emx_digital.yaml
index 0d8aa7f3..b5c72bd9 100644
--- a/static/bidder-info/emx_digital.yaml
+++ b/static/bidder-info/emx_digital.yaml
@@ -1,6 +1,6 @@
 endpoint: "https://hb.emxdgt.com"
 maintainer:
-  email: "adops@emxdigital.com"
+  email: "contactaperturemx@cadent.tv"
 gvlVendorID: 183
 capabilities:
   site:
diff --git a/static/bidder-info/lm_kiviads.yaml b/static/bidder-info/lm_kiviads.yaml
new file mode 100644
index 00000000..dcb252a4
--- /dev/null
+++ b/static/bidder-info/lm_kiviads.yaml
@@ -0,0 +1,19 @@
+endpoint: "http://pbs.kiviads.live/?pid={{.SourceId}}&host={{.Host}}"
+maintainer:
+  email: "prebid@kiviads.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
+userSync:
+  # lmkiviads supports user syncing, but requires configuration by the host. contact this
+  # bidder directly at the email address in this file to ask about enabling user sync.
+  supports:
+    - redirect
\ No newline at end of file
diff --git a/static/bidder-info/vrtcal.yaml b/static/bidder-info/vrtcal.yaml
index 0983d546..2aeb292a 100644
--- a/static/bidder-info/vrtcal.yaml
+++ b/static/bidder-info/vrtcal.yaml
@@ -7,13 +7,14 @@ capabilities:
     mediaTypes:
       - banner
       - video
+      - native
   site:
     mediaTypes:
       - banner
       - video
+      - native
 userSync:
   # vrtcal supports user syncing, but requires configuration by the host. contact this
   # bidder directly at the email address in this file to ask about enabling user sync.
   supports:
     - redirect
-
diff --git a/static/bidder-params/cadent_aperture_mx.json b/static/bidder-params/cadent_aperture_mx.json
new file mode 100644
index 00000000..4e3ff51f
--- /dev/null
+++ b/static/bidder-params/cadent_aperture_mx.json
@@ -0,0 +1,18 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "Cadent Aperture MX Adapter Params",
+    "description": "A schema which validates params accepted by the Cadent Aperture MX adapter",
+    "type": "object",
+    "properties": {
+      "tagid" : {
+        "type": "string",
+        "description": "The id of an inventory target"
+      },
+      "bidfloor": {
+        "type": "string",
+        "description": "The minimum price acceptable for a bid"
+      }
+    },
+
+    "required": ["tagid"]
+  }
diff --git a/static/bidder-params/lm_kiviads.json b/static/bidder-params/lm_kiviads.json
new file mode 100644
index 00000000..901a1a8e
--- /dev/null
+++ b/static/bidder-params/lm_kiviads.json
@@ -0,0 +1,22 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Lm_Kiviads Adapter Params",
+  "description": "A schema which validates params accepted by the lmkiviads adapter",
+  "type": "object",
+  "properties": {
+    "env": {
+      "type": "string",
+      "description": "kivi environment",
+      "minLength": 1
+    },
+    "pid": {
+      "type": "string",
+      "description": "Unique placement ID",
+      "minLength": 1
+    }
+  },
+  "required": [
+    "env",
+    "pid"
+  ]
+}


Test patch:
diff --git a/adapters/adxcg/adxcgtest/exemplary/simple-banner-currency.json b/adapters/adxcg/adxcgtest/exemplary/simple-banner-currency.json
new file mode 100644
index 00000000..8678a62c
--- /dev/null
+++ b/adapters/adxcg/adxcgtest/exemplary/simple-banner-currency.json
@@ -0,0 +1,87 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "cur": ["EUR"],
+        "site": {
+            "page": "https://good.site/url"
+        },
+        "imp": [{
+            "id": "test-imp-id",
+            "banner": {
+                "format": [{
+                    "w": 300,
+                    "h": 250
+                }]
+            },
+            "ext": {
+                "bidder": {}
+            }
+        }]
+    },
+
+    "httpCalls": [{
+        "expectedRequest": {
+            "uri": "http://localhost/prebid_server",
+            "body": {
+                "id": "test-request-id",
+                "site": {
+                    "page": "https://good.site/url"
+                },
+                "cur": ["EUR"],
+                "imp": [{
+                    "id": "test-imp-id",
+                    "banner": {
+                        "format": [{
+                            "w": 300,
+                            "h": 250
+                        }]
+                    },
+                    "ext": {
+                        "bidder": {}
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "id": "test-request-id",
+                "seatbid": [{
+                    "seat": "adxcg",
+                    "bid": [{
+                        "id": "randomid",
+                        "impid": "test-imp-id",
+                        "price": 0.500000,
+                        "adid": "12345678",
+                        "adm": "some-test-ad",
+                        "cid": "987",
+                        "crid": "12345678",
+                        "h": 250,
+                        "w": 300,
+                        "mtype":1
+                    }]
+                }],
+                "cur": "EUR"
+            }
+        }
+    }],
+
+    "expectedBidResponses": [{
+        "currency": "EUR",
+        "bids": [{
+            "bid": {
+                "id": "randomid",
+                "impid": "test-imp-id",
+                "price": 0.5,
+                "adm": "some-test-ad",
+                "adid": "12345678",
+                "cid": "987",
+                "crid": "12345678",
+                "w": 300,
+                "h": 250,
+                "mtype": 1
+            },
+            "type": "banner"
+        }]
+    }]
+}
diff --git a/adapters/adxcg/adxcgtest/exemplary/simple-banner.json b/adapters/adxcg/adxcgtest/exemplary/simple-banner.json
index 85ed18ff..42902014 100644
--- a/adapters/adxcg/adxcgtest/exemplary/simple-banner.json
+++ b/adapters/adxcg/adxcgtest/exemplary/simple-banner.json
@@ -55,7 +55,8 @@
                         "cid": "987",
                         "crid": "12345678",
                         "h": 250,
-                        "w": 300
+                        "w": 300,
+                        "mtype":1
                     }]
                 }],
                 "cur": "USD"
@@ -75,7 +76,8 @@
                 "cid": "987",
                 "crid": "12345678",
                 "w": 300,
-                "h": 250
+                "h": 250,
+                "mtype":1
             },
             "type": "banner"
         }]
diff --git a/adapters/adxcg/adxcgtest/exemplary/simple-native.json b/adapters/adxcg/adxcgtest/exemplary/simple-native.json
index 1a449e60..1b0d5b59 100644
--- a/adapters/adxcg/adxcgtest/exemplary/simple-native.json
+++ b/adapters/adxcg/adxcgtest/exemplary/simple-native.json
@@ -42,7 +42,8 @@
                   "price": 1.16,
                   "adm": "native-ad",
                   "w": 1,
-                  "h": 1
+                  "h": 1,
+                  "mtype":4
                 }
               ]
             }
@@ -63,7 +64,8 @@
             "price": 1.16,
             "adm": "native-ad",
             "w": 1,
-            "h": 1
+            "h": 1,
+            "mtype":4
           },
           "type": "native"
         }
diff --git a/adapters/adxcg/adxcgtest/exemplary/simple-video.json b/adapters/adxcg/adxcgtest/exemplary/simple-video.json
index b5f54b9b..735f1003 100644
--- a/adapters/adxcg/adxcgtest/exemplary/simple-video.json
+++ b/adapters/adxcg/adxcgtest/exemplary/simple-video.json
@@ -74,7 +74,8 @@
                   "price": 1.16,
                   "adm": "some-test-ad",
                   "w": 300,
-                  "h": 250
+                  "h": 250,
+                  "mtype":2
                 }
               ]
             }
@@ -95,7 +96,8 @@
             "price": 1.16,
             "adm": "some-test-ad",
             "w": 300,
-            "h": 250
+            "h": 250,
+            "mtype":2
           },
           "type": "video"
         }
diff --git a/adapters/axis/axis_test.go b/adapters/axis/axis_test.go
index 14163157..56cb7f0b 100644
--- a/adapters/axis/axis_test.go
+++ b/adapters/axis/axis_test.go
@@ -10,7 +10,7 @@ import (
 
 func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderAxis, config.Adapter{
-		Endpoint: "http://prebid.axis-marketplace.com/{{.AccountID}}?token={{.SourceId}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+		Endpoint: "http://prebid.axis-marketplace.com/pbserver"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
diff --git a/adapters/axis/axistest/exemplary/simple-banner.json b/adapters/axis/axistest/exemplary/simple-banner.json
index cd68d432..2c276c49 100644
--- a/adapters/axis/axistest/exemplary/simple-banner.json
+++ b/adapters/axis/axistest/exemplary/simple-banner.json
@@ -37,7 +37,7 @@
     "httpCalls": [
         {
             "expectedRequest": {
-                "uri": "http://prebid.axis-marketplace.com/000000?token=000000",
+                "uri": "http://prebid.axis-marketplace.com/pbserver",
                 "body": {
                     "id": "test-request-id",
                     "imp": [
diff --git a/adapters/axis/axistest/exemplary/simple-native.json b/adapters/axis/axistest/exemplary/simple-native.json
index 60f8621a..ce337ec6 100644
--- a/adapters/axis/axistest/exemplary/simple-native.json
+++ b/adapters/axis/axistest/exemplary/simple-native.json
@@ -29,7 +29,7 @@
     "httpCalls": [
         {
             "expectedRequest": {
-                "uri": "http://prebid.axis-marketplace.com/000000?token=000000",
+                "uri": "http://prebid.axis-marketplace.com/pbserver",
                 "body": {
                     "id": "test-request-id",
                     "imp": [
diff --git a/adapters/axis/axistest/exemplary/simple-video.json b/adapters/axis/axistest/exemplary/simple-video.json
index 78c90cc3..291a4f42 100644
--- a/adapters/axis/axistest/exemplary/simple-video.json
+++ b/adapters/axis/axistest/exemplary/simple-video.json
@@ -36,7 +36,7 @@
     "httpCalls": [
         {
             "expectedRequest": {
-                "uri": "http://prebid.axis-marketplace.com/000000?token=000000",
+                "uri": "http://prebid.axis-marketplace.com/pbserver",
                 "body": {
                     "id": "test-request-id",
                     "device": {
diff --git a/adapters/axis/axistest/exemplary/simple-web-banner.json b/adapters/axis/axistest/exemplary/simple-web-banner.json
index a6e325dc..06ba5bc5 100644
--- a/adapters/axis/axistest/exemplary/simple-web-banner.json
+++ b/adapters/axis/axistest/exemplary/simple-web-banner.json
@@ -37,7 +37,7 @@
     "httpCalls": [
         {
             "expectedRequest": {
-                "uri": "http://prebid.axis-marketplace.com/000000?token=000000",
+                "uri": "http://prebid.axis-marketplace.com/pbserver",
                 "body": {
                     "id": "test-request-id",
                     "imp": [
diff --git a/adapters/axis/axistest/supplemental/bad_media_type.json b/adapters/axis/axistest/supplemental/bad_media_type.json
index eb2abc86..bf26adc5 100644
--- a/adapters/axis/axistest/supplemental/bad_media_type.json
+++ b/adapters/axis/axistest/supplemental/bad_media_type.json
@@ -23,7 +23,7 @@
     },
 	"httpCalls": [{
 		"expectedRequest": {
-			"uri": "http://prebid.axis-marketplace.com/000000?token=000000",
+			"uri": "http://prebid.axis-marketplace.com/pbserver",
 			"body": {
                 "id": "test-request-id",
                 "imp": [
diff --git a/adapters/axis/axistest/supplemental/bad_response.json b/adapters/axis/axistest/supplemental/bad_response.json
index 4467c2a5..ae1ad6a6 100644
--- a/adapters/axis/axistest/supplemental/bad_response.json
+++ b/adapters/axis/axistest/supplemental/bad_response.json
@@ -35,7 +35,7 @@
     },
 	"httpCalls": [{
 		"expectedRequest": {
-			"uri": "http://prebid.axis-marketplace.com/000000?token=000000",
+			"uri": "http://prebid.axis-marketplace.com/pbserver",
 			"body": {
                 "id": "test-request-id",
                 "imp": [
diff --git a/adapters/axis/axistest/supplemental/status-204.json b/adapters/axis/axistest/supplemental/status-204.json
index 0f778c44..db8cdf2a 100644
--- a/adapters/axis/axistest/supplemental/status-204.json
+++ b/adapters/axis/axistest/supplemental/status-204.json
@@ -35,7 +35,7 @@
     },
 	"httpCalls": [{
 		"expectedRequest": {
-			"uri": "http://prebid.axis-marketplace.com/000000?token=000000",
+			"uri": "http://prebid.axis-marketplace.com/pbserver",
 			"body": {
                 "id": "test-request-id",
                 "imp": [
diff --git a/adapters/axis/axistest/supplemental/status-not-200.json b/adapters/axis/axistest/supplemental/status-not-200.json
index 5b4888fb..2d27dcfc 100644
--- a/adapters/axis/axistest/supplemental/status-not-200.json
+++ b/adapters/axis/axistest/supplemental/status-not-200.json
@@ -35,7 +35,7 @@
     },
 	"httpCalls": [{
 		"expectedRequest": {
-			"uri": "http://prebid.axis-marketplace.com/000000?token=000000",
+			"uri": "http://prebid.axis-marketplace.com/pbserver",
 			"body": {
                 "id": "test-request-id",
                 "imp": [
diff --git a/adapters/lm_kiviads/lmkiviads_test.go b/adapters/lm_kiviads/lmkiviads_test.go
new file mode 100644
index 00000000..dfc8a5db
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviads_test.go
@@ -0,0 +1,27 @@
+package lmkiviads
+
+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.BidderSmartHub,
+		config.Adapter{
+			Endpoint: "http://pbs.kiviads.live/?pid={{.SourceId}}&host={{.Host}}",
+		},
+		config.Server{
+			ExternalUrl: "http://hosturl.com",
+			GvlID:       1,
+			DataCenter:  "2",
+		},
+	)
+
+	assert.NoError(t, buildErr)
+	adapterstest.RunJSONBidderTest(t, "lmkiviadstest", bidder)
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/exemplary/banner.json b/adapters/lm_kiviads/lmkiviadstest/exemplary/banner.json
new file mode 100644
index 00000000..68bbd815
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/exemplary/banner.json
@@ -0,0 +1,279 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "1",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250,
+          "pos": 0
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "533faf0754cd43ceab591077781b909b"
+          }
+        }
+      },
+      {
+        "id": "2",
+        "secure": 1,
+        "bidfloor": 0.2,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250,
+          "pos": 4
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "533faf0754cd43ceab591077781b909b"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": ["IAB12"],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=533faf0754cd43ceab591077781b909b&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "1",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250,
+                "pos": 0
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "533faf0754cd43ceab591077781b909b"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": ["IAB12"],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "1",
+                  "price": 1.2,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test2</span>",
+                  "adomain": ["test.com"],
+                  "cat": ["IAB1"],
+                  "cid": "cid",
+                  "crid": "crid1",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=533faf0754cd43ceab591077781b909b&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "2",
+              "secure": 1,
+              "bidfloor": 0.2,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250,
+                "pos": 4
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "533faf0754cd43ceab591077781b909b"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": ["IAB12"],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "2",
+                  "price": 2.4,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test3</span>",
+                  "adomain": ["test.com"],
+                  "cat": ["IAB1"],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "id",
+            "impid": "1",
+            "price": 1.2,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test2</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid1",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    },
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "id",
+            "impid": "2",
+            "price": 2.4,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test3</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/exemplary/native.json b/adapters/lm_kiviads/lmkiviadstest/exemplary/native.json
new file mode 100644
index 00000000..efd8a80f
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/exemplary/native.json
@@ -0,0 +1,162 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "native": {
+          "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+          "ver": "1.1"
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "533faf0754cd43ceab591077781b909b"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=533faf0754cd43ceab591077781b909b&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "native": {
+                "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+                "ver": "1.1"
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "533faf0754cd43ceab591077781b909b"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "id",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "{}",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "ext": {
+                    "prebid": {
+                      "type": "native"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "id",
+            "impid": "id",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "{}",
+            "adomain": [
+              "test.com"
+            ],
+            "cat": [
+              "IAB1"
+            ],
+            "cid": "cid",
+            "crid": "crid",
+            "ext": {
+              "prebid": {
+                "type": "native"
+              }
+            }
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/exemplary/video.json b/adapters/lm_kiviads/lmkiviadstest/exemplary/video.json
new file mode 100644
index 00000000..1f694d51
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/exemplary/video.json
@@ -0,0 +1,202 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "video": {
+          "mimes": [
+            "video/mp4",
+            "video/ogg",
+            "video/webm"
+          ],
+          "minduration": 3,
+          "maxduration": 3000,
+          "protocols": [
+            2,
+            3,
+            5,
+            6,
+            7,
+            8
+          ],
+          "w": 480,
+          "h": 320,
+          "linearity": 1,
+          "playbackmethod": [
+            2
+          ],
+          "pos": 0
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "533faf0754cd43ceab591077781b909b"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=533faf0754cd43ceab591077781b909b&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "video": {
+                "mimes": [
+                  "video/mp4",
+                  "video/ogg",
+                  "video/webm"
+                ],
+                "minduration": 3,
+                "maxduration": 3000,
+                "protocols": [
+                  2,
+                  3,
+                  5,
+                  6,
+                  7,
+                  8
+                ],
+                "w": 480,
+                "h": 320,
+                "linearity": 1,
+                "playbackmethod": [
+                  2
+                ],
+                "pos": 0
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "533faf0754cd43ceab591077781b909b"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "id",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<VAST>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "ext": {
+                    "prebid": {
+                      "type": "video"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "id",
+            "impid": "id",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<VAST>",
+            "adomain": [
+              "test.com"
+            ],
+            "cat": [
+              "IAB1"
+            ],
+            "cid": "cid",
+            "crid": "crid",
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/supplemental/bad-response.json b/adapters/lm_kiviads/lmkiviadstest/supplemental/bad-response.json
new file mode 100644
index 00000000..7380a979
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/supplemental/bad-response.json
@@ -0,0 +1,106 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "533faf0754cd43ceab591077781b902"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=533faf0754cd43ceab591077781b902&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "533faf0754cd43ceab591077781b902"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": ""
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/supplemental/empty-mediatype.json b/adapters/lm_kiviads/lmkiviadstest/supplemental/empty-mediatype.json
new file mode 100644
index 00000000..40c8e78d
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/supplemental/empty-mediatype.json
@@ -0,0 +1,188 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "533faf0754cd43ceab591077781b902"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=533faf0754cd43ceab591077781b902&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "533faf0754cd43ceab591077781b902"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "1",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test1</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }, {
+                  "id": "id",
+                  "impid": "2",
+                  "price": 1.2,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test2</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "some": "value"
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bid[1].Ext.Prebid.Type expects one of the following values: 'banner', 'native', 'video', 'audio', got ''",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency":"USD",
+      "bids":[
+        {
+          "bid": {
+            "id": "id",
+            "impid": "1",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test1</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/supplemental/empty-seatbid-0-bid.json b/adapters/lm_kiviads/lmkiviadstest/supplemental/empty-seatbid-0-bid.json
new file mode 100644
index 00000000..15e9a1df
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/supplemental/empty-seatbid-0-bid.json
@@ -0,0 +1,111 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [],
+  "expectedBidResponses": [{"currency":"USD","bids":[]}]
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/supplemental/empty-seatbid.json b/adapters/lm_kiviads/lmkiviadstest/supplemental/empty-seatbid.json
new file mode 100644
index 00000000..6d6daebc
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/supplemental/empty-seatbid.json
@@ -0,0 +1,111 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "a1580f2f-be6d-11eb-a150-d094662c1c35",
+          "bidid": "359da97d0384d8a14767029c18fd840d",
+          "seatbid": [],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Array SeatBid cannot be empty",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/supplemental/invalid-ext-bidder-object.json b/adapters/lm_kiviads/lmkiviadstest/supplemental/invalid-ext-bidder-object.json
new file mode 100644
index 00000000..87ade656
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/supplemental/invalid-ext-bidder-object.json
@@ -0,0 +1,49 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": []
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [],
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Failed to deserialize LmKiviads extension: json: cannot unmarshal array into Go value of type openrtb_ext.ExtLmKiviads",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/supplemental/invalid-ext-object.json b/adapters/lm_kiviads/lmkiviadstest/supplemental/invalid-ext-object.json
new file mode 100644
index 00000000..aa215eb3
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/supplemental/invalid-ext-object.json
@@ -0,0 +1,47 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": ""
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [],
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Failed to deserialize bidder impression extension: json: cannot unmarshal string into Go value of type adapters.ExtImpBidder",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/supplemental/invalid-mediatype.json b/adapters/lm_kiviads/lmkiviadstest/supplemental/invalid-mediatype.json
new file mode 100644
index 00000000..bdf41988
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/supplemental/invalid-mediatype.json
@@ -0,0 +1,190 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "1",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test1</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }, {
+                  "id": "id",
+                  "impid": "2",
+                  "price": 1.2,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test2</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {
+                      "type": "wrong"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bid[1].Ext.Prebid.Type expects one of the following values: 'banner', 'native', 'video', 'audio', got 'wrong'",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency":"USD",
+      "bids":[
+        {
+          "bid": {
+            "id": "id",
+            "impid": "1",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test1</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/supplemental/status-204.json b/adapters/lm_kiviads/lmkiviadstest/supplemental/status-204.json
new file mode 100644
index 00000000..c040fb79
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/supplemental/status-204.json
@@ -0,0 +1,100 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 204,
+        "body": {}
+      }
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/supplemental/status-400.json b/adapters/lm_kiviads/lmkiviadstest/supplemental/status-400.json
new file mode 100644
index 00000000..256f1134
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/supplemental/status-400.json
@@ -0,0 +1,106 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 400,
+        "body": "The Key has a different ad format"
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/supplemental/status-503.json b/adapters/lm_kiviads/lmkiviadstest/supplemental/status-503.json
new file mode 100644
index 00000000..e0ca7f65
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/supplemental/status-503.json
@@ -0,0 +1,105 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 503
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bidder LmKiviads is unavailable. Please contact the bidder support.",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/lm_kiviads/lmkiviadstest/supplemental/unexpected-status.json b/adapters/lm_kiviads/lmkiviadstest/supplemental/unexpected-status.json
new file mode 100644
index 00000000..d176e641
--- /dev/null
+++ b/adapters/lm_kiviads/lmkiviadstest/supplemental/unexpected-status.json
@@ -0,0 +1,106 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "kivi-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://pbs.kiviads.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=kivi-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "kivi-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 403,
+        "body": "Access is denied"
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 403. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/lm_kiviads/params_test.go b/adapters/lm_kiviads/params_test.go
new file mode 100644
index 00000000..f40ad516
--- /dev/null
+++ b/adapters/lm_kiviads/params_test.go
@@ -0,0 +1,53 @@
+package lmkiviads
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+var validParams = []string{
+	`{"env":"kivi-stage", "pid":"123456"}`,
+}
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderLmKiviads, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected lmkiviads params: %s", validParam)
+		}
+	}
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`[]`,
+	`{}`,
+	`{"some": "param"}`,
+	`{"env":"kivi-stage"}`,
+	`{"pid":"1234"}`,
+	`{"othervalue":"Lorem ipsum"}`,
+	`{"env":"kivi-stage", pid:""}`,
+	`{"env":"", pid:"1234"}`,
+}
+
+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.BidderLmKiviads, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
diff --git a/adapters/rubicon/rubicon_test.go b/adapters/rubicon/rubicon_test.go
index 5f75ea5f..63474155 100644
--- a/adapters/rubicon/rubicon_test.go
+++ b/adapters/rubicon/rubicon_test.go
@@ -123,7 +123,12 @@ func TestResolveNativeObject(t *testing.T) {
 		{
 			nativeObject:  openrtb2.Native{Ver: "1", Request: "{\"eventtrackers\": [], \"context\": \"someWrongValue\"}"},
 			target:        map[string]interface{}{},
-			expectedError: fmt.Errorf("Context is not present or not of int type"),
+			expectedError: fmt.Errorf("Context is not of int type"),
+		},
+		{
+			nativeObject:  openrtb2.Native{Ver: "1", Request: "{\"eventtrackers\": [], \"plcmttype\": 2}"},
+			target:        map[string]interface{}{},
+			expectedError: nil,
 		},
 		{
 			nativeObject:  openrtb2.Native{Ver: "1", Request: "{\"eventtrackers\": [], \"context\": 1}"},
diff --git a/adapters/vrtcal/vrtcaltest/exemplary/simple-banner.json b/adapters/vrtcal/vrtcaltest/exemplary/simple-banner.json
index 81808a3b..27682f7e 100644
--- a/adapters/vrtcal/vrtcaltest/exemplary/simple-banner.json
+++ b/adapters/vrtcal/vrtcaltest/exemplary/simple-banner.json
@@ -68,7 +68,8 @@
                   "adm": "some-test-ad",
                   "crid": "crid_10",
                   "h": 250,
-                  "w": 300
+                  "w": 300,
+		  "mtype":1
                 }
               ]
             }
@@ -90,7 +91,8 @@
             "adm": "some-test-ad",
             "crid": "crid_10",
             "w": 300,
-            "h": 250
+            "h": 250,
+	    "mtype":1
           },
           "type": "banner"
         }
diff --git a/adapters/vrtcal/vrtcaltest/exemplary/simple-native.json b/adapters/vrtcal/vrtcaltest/exemplary/simple-native.json
new file mode 100644
index 00000000..3c63914c
--- /dev/null
+++ b/adapters/vrtcal/vrtcaltest/exemplary/simple-native.json
@@ -0,0 +1,90 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+
+        "native": {
+          "ver": "1.2",
+          "request": "{\"assets\":[{\"id\":1,\"img\":{\"ext\":{\"image1\":\"image2\"},\"h\": 250,\"mimes\":[\"image\/gif\",\"image\/png\"],\"type\":3,\"w\":300},\"required\":1}]}"
+        },
+        "ext": {
+          "bidder": {
+            "Just_an_unused_vrtcal_param": "unused_data_for_this"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "fake-app-id"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+            "native": {
+              "ver": "1.2",
+              "request": "{\"assets\":[{\"id\":1,\"img\":{\"ext\":{\"image1\":\"image2\"},\"h\": 250,\"mimes\":[\"image\/gif\",\"image\/png\"],\"type\":3,\"w\":300},\"required\":1}]}"
+            },
+            "ext": {
+              "bidder": {
+                "Just_an_unused_vrtcal_param": "unused_data_for_this"
+              }
+            }
+          }
+	  ],
+          "app": {
+            "id": "fake-app-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "vrtcal",
+              "bid": [
+                {
+                  "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                  "impid": "test-imp-id",
+                  "price": 0.500000,
+                  "adm": "some-test-native-ad",
+                  "crid": "crid_10",
+		  "mtype":4
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-native-ad",
+            "crid": "crid_10",
+	    "mtype":4
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/vrtcal/vrtcaltest/exemplary/simple-video.json b/adapters/vrtcal/vrtcaltest/exemplary/simple-video.json
index 365c892b..2baaf1aa 100644
--- a/adapters/vrtcal/vrtcaltest/exemplary/simple-video.json
+++ b/adapters/vrtcal/vrtcaltest/exemplary/simple-video.json
@@ -64,7 +64,8 @@
                   "adm": "some-test-video-ad",
                   "crid": "crid_10",
                   "h": 250,
-                  "w": 300
+                  "w": 300,
+		  "mtype":2
                 }
               ]
             }
@@ -86,7 +87,8 @@
             "adm": "some-test-video-ad",
             "crid": "crid_10",
             "w": 300,
-            "h": 250
+            "h": 250,
+	    "mtype":2
           },
           "type": "video"
         }
diff --git a/adapters/vrtcal/vrtcaltest/exemplary/web-simple-banner.json b/adapters/vrtcal/vrtcaltest/exemplary/web-simple-banner.json
index 62fd90cf..eb7d2310 100644
--- a/adapters/vrtcal/vrtcaltest/exemplary/web-simple-banner.json
+++ b/adapters/vrtcal/vrtcaltest/exemplary/web-simple-banner.json
@@ -70,7 +70,8 @@
                   "adm": "some-test-ad",
                   "crid": "crid_10",
                   "h": 250,
-                  "w": 300
+                  "w": 300,
+		  "mtype":1
                 }
               ]
             }
@@ -92,7 +93,8 @@
             "adm": "some-test-ad",
             "crid": "crid_10",
             "w": 300,
-            "h": 250
+            "h": 250,
+	    "mtype":1
           },
           "type": "banner"
         }
diff --git a/adapters/vrtcal/vrtcaltest/exemplary/web-simple-native.json b/adapters/vrtcal/vrtcaltest/exemplary/web-simple-native.json
new file mode 100644
index 00000000..6b416ab0
--- /dev/null
+++ b/adapters/vrtcal/vrtcaltest/exemplary/web-simple-native.json
@@ -0,0 +1,91 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "native": {
+          "ver": "1.2",
+          "request": "{\"assets\":[{\"id\":1,\"img\":{\"ext\":{\"image1\":\"image2\"},\"h\": 250,\"mimes\":[\"image\/gif\",\"image\/png\"],\"type\":3,\"w\":300},\"required\":1}]}"
+        },
+        "ext": {
+          "bidder": {
+            "Just_an_unused_vrtcal_param": "unused_data_for_this"
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "fake-site-id",
+      "page": "http://www.vrtcal.com"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+            "native": {
+              "ver": "1.2",
+              "request": "{\"assets\":[{\"id\":1,\"img\":{\"ext\":{\"image1\":\"image2\"},\"h\": 250,\"mimes\":[\"image\/gif\",\"image\/png\"],\"type\":3,\"w\":300},\"required\":1}]}"
+            },
+            "ext": {
+              "bidder": {
+                "Just_an_unused_vrtcal_param": "unused_data_for_this"
+              }
+            }
+          }
+	  ],
+          "site": {
+            "id": "fake-site-id",
+            "page": "http://www.vrtcal.com"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "vrtcal",
+              "bid": [
+                {
+                  "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                  "impid": "test-imp-id",
+                  "price": 0.500000,
+                  "adm": "some-test-native-ad",
+                  "crid": "crid_10",
+		  "mtype":4
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-native-ad",
+            "crid": "crid_10",
+	    "mtype":4
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/vrtcal/vrtcaltest/exemplary/web-simple-video.json b/adapters/vrtcal/vrtcaltest/exemplary/web-simple-video.json
index f70c547a..96ffd709 100644
--- a/adapters/vrtcal/vrtcaltest/exemplary/web-simple-video.json
+++ b/adapters/vrtcal/vrtcaltest/exemplary/web-simple-video.json
@@ -66,7 +66,8 @@
                   "adm": "some-test-video-ad",
                   "crid": "crid_10",
                   "h": 250,
-                  "w": 300
+                  "w": 300,
+		  "mtype":2
                 }
               ]
             }
@@ -88,7 +89,8 @@
             "adm": "some-test-video-ad",
             "crid": "crid_10",
             "w": 300,
-            "h": 250
+            "h": 250,
+	    "mtype":2
           },
           "type": "video"
         }
diff --git a/adapters/vrtcal/vrtcaltest/supplemental/unmatched_response_impression_id.json b/adapters/vrtcal/vrtcaltest/supplemental/unsupported_return_type.json
similarity index 90%
rename from adapters/vrtcal/vrtcaltest/supplemental/unmatched_response_impression_id.json
rename to adapters/vrtcal/vrtcaltest/supplemental/unsupported_return_type.json
index b3bfc060..e2fc18f3 100644
--- a/adapters/vrtcal/vrtcaltest/supplemental/unmatched_response_impression_id.json
+++ b/adapters/vrtcal/vrtcaltest/supplemental/unsupported_return_type.json
@@ -63,13 +63,14 @@
               "bid": [
                 {
                   "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
-                  "impid": "WRONG-IMPRESSION_ID",
+                  "impid": "test-imp-id",
                   "price": 0.500000,
                   "adm": "some-test-ad",
                   "crid": "crid_10",
                   "h": 250,
-                  "w": 300
-                }
+                  "w": 300,
+                  "mtype": 0
+		}
               ]
             }
           ],
@@ -81,7 +82,7 @@
   "expectedBidResponses": [{"currency":"USD","bids":[]}],
     "expectedMakeBidsErrors": [
       {
-        "value": "Failed to find impression for ID: \"WRONG-IMPRESSION_ID\"",
+        "value": "Unsupported return type",
         "comparison": "literal"
       }
     ]
diff --git a/config/bidderinfo_test.go b/config/bidderinfo_test.go
index f83d942c..4d7ae8f3 100644
--- a/config/bidderinfo_test.go
+++ b/config/bidderinfo_test.go
@@ -42,6 +42,27 @@ endpointCompression: GZIP
 openrtb:
   version: 2.6
   gpp-supported: true
+endpoint: https://endpoint.com
+disabled: false
+extra_info: extra-info
+app_secret: app-secret
+platform_id: 123
+usersync_url: user-url
+userSync:
+  key: foo
+  default: iframe
+  iframe:
+    url: https://foo.com/sync?mode=iframe&r={{.RedirectURL}}
+    redirectUrl: https://redirect/setuid/iframe
+    externalUrl: https://iframe.host
+    userMacro: UID
+xapi:
+  username: uname
+  password: pwd
+  tracker: tracker
+`
+const testSimpleAliasYAML = `
+aliasOf: bidderA
 `
 
 func TestLoadBidderInfoFromDisk(t *testing.T) {
@@ -131,6 +152,112 @@ func TestProcessBidderInfo(t *testing.T) {
 			expectedBidderInfos: nil,
 			expectError:         "error parsing config for bidder bidderA.yaml",
 		},
+		{
+			description: "Valid aliases",
+			bidderInfos: map[string][]byte{
+				"bidderA.yaml": []byte(fullBidderYAMLConfig),
+				"bidderB.yaml": []byte(testSimpleAliasYAML),
+			},
+			expectedBidderInfos: BidderInfos{
+				"bidderA": BidderInfo{
+					AppSecret: "app-secret",
+					Capabilities: &CapabilitiesInfo{
+						App: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+						Site: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+					},
+					Debug: &DebugInfo{
+						Allow: true,
+					},
+					Disabled:            false,
+					Endpoint:            "https://endpoint.com",
+					EndpointCompression: "GZIP",
+					Experiment: BidderInfoExperiment{
+						AdsCert: BidderAdsCert{
+							Enabled: true,
+						},
+					},
+					ExtraAdapterInfo: "extra-info",
+					GVLVendorID:      42,
+					Maintainer: &MaintainerInfo{
+						Email: "some-email@domain.com",
+					},
+					ModifyingVastXmlAllowed: true,
+					OpenRTB: &OpenRTBInfo{
+						GPPSupported: true,
+						Version:      "2.6",
+					},
+					PlatformID: "123",
+					Syncer: &Syncer{
+						Key: "foo",
+						IFrame: &SyncerEndpoint{
+							URL:         "https://foo.com/sync?mode=iframe&r={{.RedirectURL}}",
+							RedirectURL: "https://redirect/setuid/iframe",
+							ExternalURL: "https://iframe.host",
+							UserMacro:   "UID",
+						},
+					},
+					UserSyncURL: "user-url",
+					XAPI: AdapterXAPI{
+						Username: "uname",
+						Password: "pwd",
+						Tracker:  "tracker",
+					},
+				},
+				"bidderB": BidderInfo{
+					AliasOf:   "bidderA",
+					AppSecret: "app-secret",
+					Capabilities: &CapabilitiesInfo{
+						App: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+						Site: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+					},
+					Debug: &DebugInfo{
+						Allow: true,
+					},
+					Disabled:            false,
+					Endpoint:            "https://endpoint.com",
+					EndpointCompression: "GZIP",
+					Experiment: BidderInfoExperiment{
+						AdsCert: BidderAdsCert{
+							Enabled: true,
+						},
+					},
+					ExtraAdapterInfo: "extra-info",
+					GVLVendorID:      42,
+					Maintainer: &MaintainerInfo{
+						Email: "some-email@domain.com",
+					},
+					ModifyingVastXmlAllowed: true,
+					OpenRTB: &OpenRTBInfo{
+						GPPSupported: true,
+						Version:      "2.6",
+					},
+					PlatformID: "123",
+					Syncer: &Syncer{
+						Key: "foo",
+						IFrame: &SyncerEndpoint{
+							URL:         "https://foo.com/sync?mode=iframe&r={{.RedirectURL}}",
+							RedirectURL: "https://redirect/setuid/iframe",
+							ExternalURL: "https://iframe.host",
+							UserMacro:   "UID",
+						},
+					},
+					UserSyncURL: "user-url",
+					XAPI: AdapterXAPI{
+						Username: "uname",
+						Password: "pwd",
+						Tracker:  "tracker",
+					},
+				},
+			},
+		},
 	}
 	for _, test := range testCases {
 		reader := StubInfoReader{test.bidderInfos}
@@ -145,6 +272,204 @@ func TestProcessBidderInfo(t *testing.T) {
 
 }
 
+func TestProcessAliasBidderInfo(t *testing.T) {
+	testCases := []struct {
+		description         string
+		aliasFields         map[string]aliasNillableFields
+		bidderInfos         BidderInfos
+		expectedBidderInfos BidderInfos
+		expectError         string
+	}{
+		{
+			description: "valid alias - replace alias with parent info",
+			aliasFields: map[string]aliasNillableFields{
+				"bidderB": {},
+			},
+			bidderInfos: BidderInfos{
+				"bidderA": BidderInfo{
+					AppSecret: "app-secret",
+					Capabilities: &CapabilitiesInfo{
+						App: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+						Site: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+					},
+					Debug: &DebugInfo{
+						Allow: true,
+					},
+					Disabled:            false,
+					Endpoint:            "https://endpoint.com",
+					EndpointCompression: "GZIP",
+					Experiment: BidderInfoExperiment{
+						AdsCert: BidderAdsCert{
+							Enabled: true,
+						},
+					},
+					ExtraAdapterInfo: "extra-info",
+					GVLVendorID:      42,
+					Maintainer: &MaintainerInfo{
+						Email: "some-email@domain.com",
+					},
+					ModifyingVastXmlAllowed: true,
+					OpenRTB: &OpenRTBInfo{
+						GPPSupported: true,
+						Version:      "2.6",
+					},
+					PlatformID: "123",
+					Syncer: &Syncer{
+						Key: "foo",
+						IFrame: &SyncerEndpoint{
+							URL:         "https://foo.com/sync?mode=iframe&r={{.RedirectURL}}",
+							RedirectURL: "https://redirect/setuid/iframe",
+							ExternalURL: "https://iframe.host",
+							UserMacro:   "UID",
+						},
+					},
+					UserSyncURL: "user-url",
+					XAPI: AdapterXAPI{
+						Username: "uname",
+						Password: "pwd",
+						Tracker:  "tracker",
+					},
+				},
+				"bidderB": BidderInfo{
+					AliasOf: "bidderA",
+				},
+			},
+			expectedBidderInfos: BidderInfos{
+				"bidderA": BidderInfo{
+					AppSecret: "app-secret",
+					Capabilities: &CapabilitiesInfo{
+						App: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+						Site: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+					},
+					Debug: &DebugInfo{
+						Allow: true,
+					},
+					Disabled:            false,
+					Endpoint:            "https://endpoint.com",
+					EndpointCompression: "GZIP",
+					Experiment: BidderInfoExperiment{
+						AdsCert: BidderAdsCert{
+							Enabled: true,
+						},
+					},
+					ExtraAdapterInfo: "extra-info",
+					GVLVendorID:      42,
+					Maintainer: &MaintainerInfo{
+						Email: "some-email@domain.com",
+					},
+					ModifyingVastXmlAllowed: true,
+					OpenRTB: &OpenRTBInfo{
+						GPPSupported: true,
+						Version:      "2.6",
+					},
+					PlatformID: "123",
+					Syncer: &Syncer{
+						Key: "foo",
+						IFrame: &SyncerEndpoint{
+							URL:         "https://foo.com/sync?mode=iframe&r={{.RedirectURL}}",
+							RedirectURL: "https://redirect/setuid/iframe",
+							ExternalURL: "https://iframe.host",
+							UserMacro:   "UID",
+						},
+					},
+					UserSyncURL: "user-url",
+					XAPI: AdapterXAPI{
+						Username: "uname",
+						Password: "pwd",
+						Tracker:  "tracker",
+					},
+				},
+				"bidderB": BidderInfo{
+					AliasOf:   "bidderA",
+					AppSecret: "app-secret",
+					Capabilities: &CapabilitiesInfo{
+						App: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+						Site: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+					},
+					Debug: &DebugInfo{
+						Allow: true,
+					},
+					Disabled:            false,
+					Endpoint:            "https://endpoint.com",
+					EndpointCompression: "GZIP",
+					Experiment: BidderInfoExperiment{
+						AdsCert: BidderAdsCert{
+							Enabled: true,
+						},
+					},
+					ExtraAdapterInfo: "extra-info",
+					GVLVendorID:      42,
+					Maintainer: &MaintainerInfo{
+						Email: "some-email@domain.com",
+					},
+					ModifyingVastXmlAllowed: true,
+					OpenRTB: &OpenRTBInfo{
+						GPPSupported: true,
+						Version:      "2.6",
+					},
+					PlatformID: "123",
+					Syncer: &Syncer{
+						Key: "foo",
+						IFrame: &SyncerEndpoint{
+							URL:         "https://foo.com/sync?mode=iframe&r={{.RedirectURL}}",
+							RedirectURL: "https://redirect/setuid/iframe",
+							ExternalURL: "https://iframe.host",
+							UserMacro:   "UID",
+						},
+					},
+					UserSyncURL: "user-url",
+					XAPI: AdapterXAPI{
+						Username: "uname",
+						Password: "pwd",
+						Tracker:  "tracker",
+					},
+				},
+			},
+		},
+		{
+			description: "invalid alias",
+			aliasFields: map[string]aliasNillableFields{
+				"bidderB": {},
+			},
+			bidderInfos: BidderInfos{
+				"bidderB": BidderInfo{
+					AliasOf: "bidderA",
+				},
+			},
+			expectError: "bidderA not found for an alias: bidderB",
+		},
+		{
+			description: "bidder info not found for an alias",
+			aliasFields: map[string]aliasNillableFields{
+				"bidderB": {},
+			},
+			expectError: "bidder info not found for an alias: bidderB",
+		},
+	}
+
+	for _, test := range testCases {
+		bidderInfos, err := processBidderAliases(test.aliasFields, test.bidderInfos)
+		if test.expectError != "" {
+			assert.ErrorContains(t, err, test.expectError)
+		} else {
+			assert.Equal(t, test.expectedBidderInfos, bidderInfos)
+		}
+
+	}
+}
+
 type StubInfoReader struct {
 	mockBidderInfos map[string][]byte
 }
@@ -265,6 +590,57 @@ func TestBidderInfoValidationPositive(t *testing.T) {
 	assert.Len(t, errs, 0, "All bidder infos should be correct")
 }
 
+func TestValidateAliases(t *testing.T) {
+	testCase := struct {
+		description  string
+		bidderInfos  BidderInfos
+		expectErrors []error
+	}{
+		description: "invalid aliases",
+		bidderInfos: BidderInfos{
+			"bidderA": BidderInfo{
+				Endpoint: "http://bidderA.com/openrtb2",
+				Maintainer: &MaintainerInfo{
+					Email: "maintainer@bidderA.com",
+				},
+				Capabilities: &CapabilitiesInfo{
+					Site: &PlatformInfo{
+						MediaTypes: []openrtb_ext.BidType{
+							openrtb_ext.BidTypeVideo,
+						},
+					},
+				},
+				AliasOf: "bidderB",
+			},
+			"bidderB": BidderInfo{
+				Endpoint: "http://bidderA.com/openrtb2",
+				Maintainer: &MaintainerInfo{
+					Email: "maintainer@bidderA.com",
+				},
+				Capabilities: &CapabilitiesInfo{
+					Site: &PlatformInfo{
+						MediaTypes: []openrtb_ext.BidType{
+							openrtb_ext.BidTypeVideo,
+						},
+					},
+				},
+				AliasOf: "bidderC",
+			},
+		},
+		expectErrors: []error{
+			errors.New("bidder: bidderB cannot be an alias of an alias: bidderA"),
+			errors.New("bidder: bidderC not found for an alias: bidderB"),
+		},
+	}
+
+	var errs []error
+	for bidderName, bidderInfo := range testCase.bidderInfos {
+		errs = append(errs, validateAliases(bidderInfo, testCase.bidderInfos, bidderName))
+	}
+
+	assert.ElementsMatch(t, errs, testCase.expectErrors)
+}
+
 func TestBidderInfoValidationNegative(t *testing.T) {
 	testCases := []struct {
 		description  string
@@ -483,43 +859,6 @@ func TestBidderInfoValidationNegative(t *testing.T) {
 				errors.New("syncer could not be created, invalid supported endpoint: incorrect"),
 			},
 		},
-		{
-			"Invalid aliases",
-			BidderInfos{
-				"bidderA": BidderInfo{
-					Endpoint: "http://bidderA.com/openrtb2",
-					Maintainer: &MaintainerInfo{
-						Email: "maintainer@bidderA.com",
-					},
-					Capabilities: &CapabilitiesInfo{
-						Site: &PlatformInfo{
-							MediaTypes: []openrtb_ext.BidType{
-								openrtb_ext.BidTypeVideo,
-							},
-						},
-					},
-					AliasOf: "bidderB",
-				},
-				"bidderB": BidderInfo{
-					Endpoint: "http://bidderA.com/openrtb2",
-					Maintainer: &MaintainerInfo{
-						Email: "maintainer@bidderA.com",
-					},
-					Capabilities: &CapabilitiesInfo{
-						Site: &PlatformInfo{
-							MediaTypes: []openrtb_ext.BidType{
-								openrtb_ext.BidTypeVideo,
-							},
-						},
-					},
-					AliasOf: "bidderC",
-				},
-			},
-			[]error{
-				errors.New("bidder: bidderB cannot be an alias of an alias: bidderA"),
-				errors.New("bidder: bidderC not found for an alias: bidderB"),
-			},
-		},
 		{
 			"Two bidders, one with incorrect url",
 			BidderInfos{
@@ -1027,7 +1366,6 @@ func TestReadFullYamlBidderConfig(t *testing.T) {
 
 	expectedBidderInfo := BidderInfos{
 		bidder: {
-			Disabled: false,
 			Maintainer: &MaintainerInfo{
 				Email: "some-email@domain.com",
 			},
@@ -1040,17 +1378,41 @@ func TestReadFullYamlBidderConfig(t *testing.T) {
 					MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
 				},
 			},
-			Debug:                   &DebugInfo{Allow: true},
 			ModifyingVastXmlAllowed: true,
-			Syncer: &Syncer{
-				Supports: []string{"iframe"},
+			Debug: &DebugInfo{
+				Allow: true,
+			},
+			Experiment: BidderInfoExperiment{
+				AdsCert: BidderAdsCert{
+					Enabled: true,
+				},
 			},
-			Experiment:          BidderInfoExperiment{AdsCert: BidderAdsCert{Enabled: true}},
 			EndpointCompression: "GZIP",
 			OpenRTB: &OpenRTBInfo{
-				Version:      "2.6",
 				GPPSupported: true,
+				Version:      "2.6",
+			},
+			Disabled:         false,
+			ExtraAdapterInfo: "extra-info",
+			AppSecret:        "app-secret",
+			PlatformID:       "123",
+			UserSyncURL:      "user-url",
+			Syncer: &Syncer{
+				Key: "foo",
+				IFrame: &SyncerEndpoint{
+					URL:         "user-url",
+					RedirectURL: "https://redirect/setuid/iframe",
+					ExternalURL: "https://iframe.host",
+					UserMacro:   "UID",
+				},
+				Supports: []string{"iframe"},
+			},
+			XAPI: AdapterXAPI{
+				Username: "uname",
+				Password: "pwd",
+				Tracker:  "tracker",
 			},
+			Endpoint: "https://endpoint.com",
 		},
 	}
 	assert.Equalf(t, expectedBidderInfo, actualBidderInfo, "Bidder info objects aren't matching")
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index e9d812b8..22ecc1fa 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -1909,27 +1909,32 @@ func TestCookieSyncActivityControlIntegration(t *testing.T) {
 	testCases := []struct {
 		name           string
 		bidderName     string
-		allow          bool
+		accountPrivacy *config.AccountPrivacy
 		expectedResult bool
 	}{
 		{
 			name:           "activity_is_allowed",
 			bidderName:     "bidderA",
-			allow:          true,
+			accountPrivacy: getDefaultActivityConfig("bidderA", true),
 			expectedResult: true,
 		},
 		{
 			name:           "activity_is_denied",
 			bidderName:     "bidderA",
-			allow:          false,
+			accountPrivacy: getDefaultActivityConfig("bidderA", false),
 			expectedResult: false,
 		},
+		{
+			name:           "activity_is_abstain",
+			bidderName:     "bidderA",
+			accountPrivacy: nil,
+			expectedResult: true,
+		},
 	}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			privacyConfig := getDefaultActivityConfig(test.bidderName, test.allow)
-			activities, err := privacy.NewActivityControl(privacyConfig)
+			activities, err := privacy.NewActivityControl(test.accountPrivacy)
 			assert.NoError(t, err)
 			up := usersyncPrivacy{
 				activityControl: activities,
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index b39f6e03..c42209e0 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -495,7 +495,7 @@ func doBadAliasRequest(t *testing.T, filename string, expectMsg string) {
 	bidderInfos := getBidderInfos(nil, openrtb_ext.CoreBidderNames())
 
 	bidderMap := exchange.GetActiveBidders(bidderInfos)
-	disabledBidders := exchange.GetDisabledBiddersErrorMessages(bidderInfos)
+	disabledBidders := exchange.GetDisabledBidderWarningMessages(bidderInfos)
 
 	// NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it.
 	// As a side effect this gives us some coverage of the go_metrics piece of the metrics engine.
diff --git a/endpoints/openrtb2/test_utils.go b/endpoints/openrtb2/test_utils.go
index 401fbf62..3d249dc8 100644
--- a/endpoints/openrtb2/test_utils.go
+++ b/endpoints/openrtb2/test_utils.go
@@ -1246,7 +1246,7 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han
 
 	bidderInfos := getBidderInfos(test.Config.DisabledAdapters, openrtb_ext.CoreBidderNames())
 	bidderMap := exchange.GetActiveBidders(bidderInfos)
-	disabledBidders := exchange.GetDisabledBiddersErrorMessages(bidderInfos)
+	disabledBidders := exchange.GetDisabledBidderWarningMessages(bidderInfos)
 	met := &metricsConfig.NilMetricsEngine{}
 	mockFetcher := empty_fetcher.EmptyFetcher{}
 
diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go
index 2f3c8e9a..f860a1df 100644
--- a/exchange/adapter_util_test.go
+++ b/exchange/adapter_util_test.go
@@ -13,6 +13,7 @@ import (
 	metrics "github.com/prebid/prebid-server/metrics/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 var (
@@ -204,79 +205,71 @@ func TestGetActiveBidders(t *testing.T) {
 	}
 }
 
-func TestGetDisabledBiddersErrorMessages(t *testing.T) {
+func TestGetDisabledBidderWarningMessages(t *testing.T) {
+	t.Run("removed", func(t *testing.T) {
+		result := GetDisabledBidderWarningMessages(nil)
+
+		// test proper construction by verifying one expected bidder is in the list
+		require.Contains(t, result, "groupm")
+		assert.Equal(t, result["groupm"], `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`)
+	})
+
+	t.Run("removed-and-disabled", func(t *testing.T) {
+		result := GetDisabledBidderWarningMessages(map[string]config.BidderInfo{"bidderA": infoDisabled})
+
+		// test proper construction by verifying one expected bidder is in the list with the disabled bidder
+		require.Contains(t, result, "groupm")
+		assert.Equal(t, result["groupm"], `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`)
+
+		require.Contains(t, result, "bidderA")
+		assert.Equal(t, result["bidderA"], `Bidder "bidderA" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`)
+	})
+}
+
+func TestMergeRemovedAndDisabledBidderWarningMessages(t *testing.T) {
 	testCases := []struct {
-		description string
-		bidderInfos map[string]config.BidderInfo
-		expected    map[string]string
+		name             string
+		givenRemoved     map[string]string
+		givenBidderInfos map[string]config.BidderInfo
+		expected         map[string]string
 	}{
 		{
-			description: "None",
-			bidderInfos: map[string]config.BidderInfo{},
-			expected: map[string]string{
-				"lifestreet":     `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`,
-				"adagio":         `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`,
-				"somoaudience":   `Bidder "somoaudience" is no longer available in Prebid Server. Please update your configuration.`,
-				"yssp":           `Bidder "yssp" is no longer available in Prebid Server. If you're looking to use the Yahoo SSP adapter, please rename it to "yahooAds" in your configuration.`,
-				"andbeyondmedia": `Bidder "andbeyondmedia" is no longer available in Prebid Server. If you're looking to use the AndBeyond.Media SSP adapter, please rename it to "beyondmedia" in your configuration.`,
-				"oftmedia":       `Bidder "oftmedia" is no longer available in Prebid Server. Please update your configuration.`,
-				"groupm":         `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`,
-				"verizonmedia":   `Bidder "verizonmedia" is no longer available in Prebid Server. Please update your configuration.`,
-				"brightroll":     `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`,
-			},
+			name:             "none",
+			givenRemoved:     map[string]string{},
+			givenBidderInfos: map[string]config.BidderInfo{},
+			expected:         map[string]string{},
 		},
 		{
-			description: "Enabled",
-			bidderInfos: map[string]config.BidderInfo{"appnexus": infoEnabled},
-			expected: map[string]string{
-				"lifestreet":     `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`,
-				"adagio":         `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`,
-				"somoaudience":   `Bidder "somoaudience" is no longer available in Prebid Server. Please update your configuration.`,
-				"yssp":           `Bidder "yssp" is no longer available in Prebid Server. If you're looking to use the Yahoo SSP adapter, please rename it to "yahooAds" in your configuration.`,
-				"andbeyondmedia": `Bidder "andbeyondmedia" is no longer available in Prebid Server. If you're looking to use the AndBeyond.Media SSP adapter, please rename it to "beyondmedia" in your configuration.`,
-				"oftmedia":       `Bidder "oftmedia" is no longer available in Prebid Server. Please update your configuration.`,
-				"groupm":         `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`,
-				"verizonmedia":   `Bidder "verizonmedia" is no longer available in Prebid Server. Please update your configuration.`,
-				"brightroll":     `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`,
-			},
+			name:             "removed",
+			givenRemoved:     map[string]string{"bidderA": `Bidder A Message`},
+			givenBidderInfos: map[string]config.BidderInfo{},
+			expected:         map[string]string{"bidderA": `Bidder A Message`},
 		},
 		{
-			description: "Disabled",
-			bidderInfos: map[string]config.BidderInfo{"appnexus": infoDisabled},
-			expected: map[string]string{
-				"lifestreet":     `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`,
-				"adagio":         `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`,
-				"somoaudience":   `Bidder "somoaudience" is no longer available in Prebid Server. Please update your configuration.`,
-				"yssp":           `Bidder "yssp" is no longer available in Prebid Server. If you're looking to use the Yahoo SSP adapter, please rename it to "yahooAds" in your configuration.`,
-				"appnexus":       `Bidder "appnexus" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`,
-				"andbeyondmedia": `Bidder "andbeyondmedia" is no longer available in Prebid Server. If you're looking to use the AndBeyond.Media SSP adapter, please rename it to "beyondmedia" in your configuration.`,
-				"oftmedia":       `Bidder "oftmedia" is no longer available in Prebid Server. Please update your configuration.`,
-				"groupm":         `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`,
-				"verizonmedia":   `Bidder "verizonmedia" is no longer available in Prebid Server. Please update your configuration.`,
-				"brightroll":     `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`,
-			},
+			name:             "enabled",
+			givenRemoved:     map[string]string{},
+			givenBidderInfos: map[string]config.BidderInfo{"bidderA": infoEnabled},
+			expected:         map[string]string{},
 		},
 		{
-			description: "Mixed",
-			bidderInfos: map[string]config.BidderInfo{"appnexus": infoDisabled, "openx": infoEnabled},
-			expected: map[string]string{
-				"lifestreet":     `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`,
-				"adagio":         `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`,
-				"somoaudience":   `Bidder "somoaudience" is no longer available in Prebid Server. Please update your configuration.`,
-				"yssp":           `Bidder "yssp" is no longer available in Prebid Server. If you're looking to use the Yahoo SSP adapter, please rename it to "yahooAds" in your configuration.`,
-				"appnexus":       `Bidder "appnexus" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`,
-				"andbeyondmedia": `Bidder "andbeyondmedia" is no longer available in Prebid Server. If you're looking to use the AndBeyond.Media SSP adapter, please rename it to "beyondmedia" in your configuration.`,
-				"oftmedia":       `Bidder "oftmedia" is no longer available in Prebid Server. Please update your configuration.`,
-				"groupm":         `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`,
-				"verizonmedia":   `Bidder "verizonmedia" is no longer available in Prebid Server. Please update your configuration.`,
-				"brightroll":     `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`,
-			},
+			name:             "disabled",
+			givenRemoved:     map[string]string{},
+			givenBidderInfos: map[string]config.BidderInfo{"bidderA": infoDisabled},
+			expected:         map[string]string{"bidderA": `Bidder "bidderA" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`},
+		},
+		{
+			name:             "mixed",
+			givenRemoved:     map[string]string{"bidderA": `Bidder A Message`},
+			givenBidderInfos: map[string]config.BidderInfo{"bidderB": infoEnabled, "bidderC": infoDisabled},
+			expected:         map[string]string{"bidderA": `Bidder A Message`, "bidderC": `Bidder "bidderC" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`},
 		},
 	}
 
 	for _, test := range testCases {
-		result := GetDisabledBiddersErrorMessages(test.bidderInfos)
-		assert.Equal(t, test.expected, result, test.description)
+		t.Run(test.name, func(t *testing.T) {
+			result := mergeRemovedAndDisabledBidderWarningMessages(test.givenRemoved, test.givenBidderInfos)
+			assert.Equal(t, test.expected, result, test.name)
+		})
 	}
 }
 
diff --git a/privacy/enforcer_test.go b/privacy/enforcer_test.go
index 2b9715f8..864964c2 100644
--- a/privacy/enforcer_test.go
+++ b/privacy/enforcer_test.go
@@ -2,10 +2,11 @@ package privacy
 
 import (
 	"errors"
+	"testing"
+
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/util/ptrutil"
 	"github.com/stretchr/testify/assert"
-	"testing"
 )
 
 func TestNewActivityControl(t *testing.T) {
@@ -144,26 +145,25 @@ func TestNewActivityControl(t *testing.T) {
 }
 
 func TestActivityDefaultToDefaultResult(t *testing.T) {
-
 	testCases := []struct {
 		name            string
 		activityDefault *bool
-		expectedResult  ActivityResult
+		expectedResult  bool
 	}{
 		{
-			name:            "activityDefault_is_nil",
+			name:            "nil",
 			activityDefault: nil,
-			expectedResult:  ActivityAllow,
+			expectedResult:  true,
 		},
 		{
-			name:            "activityDefault_is_true",
+			name:            "true",
 			activityDefault: ptrutil.ToPtr(true),
-			expectedResult:  ActivityAllow,
+			expectedResult:  true,
 		},
 		{
-			name:            "activityDefault_is_false",
+			name:            "false",
 			activityDefault: ptrutil.ToPtr(false),
-			expectedResult:  ActivityDeny,
+			expectedResult:  false,
 		},
 	}
 
@@ -182,14 +182,14 @@ func TestAllowActivityControl(t *testing.T) {
 		activityControl ActivityControl
 		activity        Activity
 		target          ScopedName
-		activityResult  ActivityResult
+		activityResult  bool
 	}{
 		{
 			name:            "plans_is_nil",
 			activityControl: ActivityControl{plans: nil},
 			activity:        ActivityFetchBids,
 			target:          ScopedName{Scope: "bidder", Name: "bidderA"},
-			activityResult:  ActivityAbstain,
+			activityResult:  true,
 		},
 		{
 			name: "activity_not_defined",
@@ -197,7 +197,7 @@ func TestAllowActivityControl(t *testing.T) {
 				ActivitySyncUser: getDefaultActivityPlan()}},
 			activity:       ActivityFetchBids,
 			target:         ScopedName{Scope: "bidder", Name: "bidderA"},
-			activityResult: ActivityAbstain,
+			activityResult: true,
 		},
 		{
 			name: "activity_defined_but_not_found_default_returned",
@@ -205,7 +205,7 @@ func TestAllowActivityControl(t *testing.T) {
 				ActivityFetchBids: getDefaultActivityPlan()}},
 			activity:       ActivityFetchBids,
 			target:         ScopedName{Scope: "bidder", Name: "bidderB"},
-			activityResult: ActivityAllow,
+			activityResult: true,
 		},
 		{
 			name: "activity_defined_and_allowed",
@@ -213,7 +213,7 @@ func TestAllowActivityControl(t *testing.T) {
 				ActivityFetchBids: getDefaultActivityPlan()}},
 			activity:       ActivityFetchBids,
 			target:         ScopedName{Scope: "bidder", Name: "bidderA"},
-			activityResult: ActivityAllow,
+			activityResult: true,
 		},
 	}
 
@@ -226,8 +226,7 @@ func TestAllowActivityControl(t *testing.T) {
 	}
 }
 
-func TestAllowComponentEnforcementRule(t *testing.T) {
-
+func TestComponentEnforcementRuleEvaluate(t *testing.T) {
 	testCases := []struct {
 		name           string
 		componentRule  ComponentEnforcementRule
@@ -237,7 +236,7 @@ func TestAllowComponentEnforcementRule(t *testing.T) {
 		{
 			name: "activity_is_allowed",
 			componentRule: ComponentEnforcementRule{
-				allowed: true,
+				result: ActivityAllow,
 				componentName: []ScopedName{
 					{Scope: "bidder", Name: "bidderA"},
 				},
@@ -249,7 +248,7 @@ func TestAllowComponentEnforcementRule(t *testing.T) {
 		{
 			name: "activity_is_not_allowed",
 			componentRule: ComponentEnforcementRule{
-				allowed: false,
+				result: ActivityDeny,
 				componentName: []ScopedName{
 					{Scope: "bidder", Name: "bidderA"},
 				},
@@ -261,7 +260,7 @@ func TestAllowComponentEnforcementRule(t *testing.T) {
 		{
 			name: "abstain_both_clauses_do_not_match",
 			componentRule: ComponentEnforcementRule{
-				allowed: true,
+				result: ActivityAllow,
 				componentName: []ScopedName{
 					{Scope: "bidder", Name: "bidderA"},
 				},
@@ -273,7 +272,7 @@ func TestAllowComponentEnforcementRule(t *testing.T) {
 		{
 			name: "activity_is_not_allowed_componentName_only",
 			componentRule: ComponentEnforcementRule{
-				allowed: true,
+				result: ActivityAllow,
 				componentName: []ScopedName{
 					{Scope: "bidder", Name: "bidderA"},
 				},
@@ -284,25 +283,33 @@ func TestAllowComponentEnforcementRule(t *testing.T) {
 		{
 			name: "activity_is_allowed_componentType_only",
 			componentRule: ComponentEnforcementRule{
-				allowed:       true,
+				result:        ActivityAllow,
 				componentType: []string{"bidder"},
 			},
 			target:         ScopedName{Scope: "bidder", Name: "bidderB"},
 			activityResult: ActivityAllow,
 		},
 		{
-			name: "abstain_activity_no_componentType_and_no_componentName",
+			name: "no-conditions-allow",
 			componentRule: ComponentEnforcementRule{
-				allowed: true,
+				result: ActivityAllow,
 			},
-			target:         ScopedName{Scope: "bidder", Name: "bidderB"},
-			activityResult: ActivityAbstain,
+			target:         ScopedName{Scope: "bidder", Name: "bidderA"},
+			activityResult: ActivityAllow,
+		},
+		{
+			name: "no-conditions-deny",
+			componentRule: ComponentEnforcementRule{
+				result: ActivityDeny,
+			},
+			target:         ScopedName{Scope: "bidder", Name: "bidderA"},
+			activityResult: ActivityDeny,
 		},
 	}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualResult := test.componentRule.Allow(test.target)
+			actualResult := test.componentRule.Evaluate(test.target)
 			assert.Equal(t, test.activityResult, actualResult)
 
 		})
@@ -310,7 +317,6 @@ func TestAllowComponentEnforcementRule(t *testing.T) {
 }
 
 func TestNewScopedName(t *testing.T) {
-
 	testCases := []struct {
 		name              string
 		condition         string
@@ -318,49 +324,49 @@ func TestNewScopedName(t *testing.T) {
 		err               error
 	}{
 		{
-			name:              "condition_is_empty",
+			name:              "empty",
 			condition:         "",
 			expectedScopeName: ScopedName{},
 			err:               errors.New("unable to parse empty condition"),
 		},
 		{
-			name:              "condition_is_incorrect",
+			name:              "incorrect",
 			condition:         "bidder.bidderA.bidderB",
 			expectedScopeName: ScopedName{},
 			err:               errors.New("unable to parse condition: bidder.bidderA.bidderB"),
 		},
 		{
-			name:              "condition_is_scoped_to_bidder",
+			name:              "scope-bidder",
 			condition:         "bidder.bidderA",
 			expectedScopeName: ScopedName{Scope: "bidder", Name: "bidderA"},
 			err:               nil,
 		},
 		{
-			name:              "condition_is_scoped_to_analytics",
+			name:              "scope-analytics",
 			condition:         "analytics.bidderA",
 			expectedScopeName: ScopedName{Scope: "analytics", Name: "bidderA"},
 			err:               nil,
 		},
 		{
-			name:              "condition_is_scoped_to_userid",
+			name:              "scope-userid",
 			condition:         "userid.bidderA",
 			expectedScopeName: ScopedName{Scope: "userid", Name: "bidderA"},
 			err:               nil,
 		},
 		{
-			name:              "condition_is_bidder_name",
+			name:              "scope-default",
 			condition:         "bidderA",
 			expectedScopeName: ScopedName{Scope: "bidder", Name: "bidderA"},
 			err:               nil,
 		},
 		{
-			name:              "condition_is_module_tag_rtd",
+			name:              "scope-rtf",
 			condition:         "rtd.test",
 			expectedScopeName: ScopedName{Scope: "rtd", Name: "test"},
 			err:               nil,
 		},
 		{
-			name:              "condition_scope_defaults_to_genera",
+			name:              "scope-general",
 			condition:         "test.test",
 			expectedScopeName: ScopedName{Scope: "general", Name: "test"},
 			err:               nil,
@@ -398,10 +404,10 @@ func getDefaultActivityConfig() config.Activity {
 
 func getDefaultActivityPlan() ActivityPlan {
 	return ActivityPlan{
-		defaultResult: ActivityAllow,
+		defaultResult: true,
 		rules: []ActivityRule{
 			ComponentEnforcementRule{
-				allowed: true,
+				result: ActivityAllow,
 				componentName: []ScopedName{
 					{Scope: "bidder", Name: "bidderA"},
 				},