Repository: prebid/prebid-server
Pull request #2998 (2023-08-02 10:05:02)

New Adapter: LM KiviAds


Patch:
diff --git a/adapters/adocean/adocean.go b/adapters/adocean/adocean.go
index ce0f78e8..a4e6223b 100644
--- a/adapters/adocean/adocean.go
+++ b/adapters/adocean/adocean.go
@@ -21,7 +21,7 @@ import (
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
-const adapterVersion = "1.2.0"
+const adapterVersion = "1.3.0"
 const maxUriLength = 8000
 const measurementCode = `
 	<script>
@@ -95,13 +95,13 @@ func (a *AdOceanAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada
 		}
 	}
 
-	var errors []error
+	var reqCreationErrors []error
 	var err error
 	requestsData := make([]*requestData, 0, len(request.Imp))
 	for _, auction := range request.Imp {
 		requestsData, err = a.addNewBid(requestsData, &auction, request, consentString)
 		if err != nil {
-			errors = append(errors, err)
+			reqCreationErrors = append(reqCreationErrors, err)
 		}
 	}
 
@@ -114,7 +114,7 @@ func (a *AdOceanAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada
 		})
 	}
 
-	return httpRequests, errors
+	return httpRequests, reqCreationErrors
 }
 
 func (a *AdOceanAdapter) addNewBid(
@@ -137,6 +137,12 @@ func (a *AdOceanAdapter) addNewBid(
 		}
 	}
 
+	if adOceanExt.EmitterPrefix == "" {
+		return requestsData, &errortypes.BadInput{
+			Message: "No emitterPrefix param",
+		}
+	}
+
 	addedToExistingRequest := addToExistingRequest(requestsData, &adOceanExt, imp, (request.Test == 1))
 	if addedToExistingRequest {
 		return requestsData, nil
@@ -196,7 +202,7 @@ func (a *AdOceanAdapter) makeURL(
 	slaveSizes map[string]string,
 	consentString string,
 ) (*url.URL, error) {
-	endpointParams := macros.EndpointTemplateParams{Host: params.EmitterDomain}
+	endpointParams := macros.EndpointTemplateParams{Host: params.EmitterPrefix}
 	host, err := macros.ResolveMacros(a.endpointTemplate, endpointParams)
 	if err != nil {
 		return nil, &errortypes.BadInput{
@@ -344,7 +350,7 @@ func (a *AdOceanAdapter) MakeBids(
 	}
 
 	var parsedResponses = adapters.NewBidderResponseWithBidsCapacity(len(auctionIDs))
-	var errors []error
+	var parsingErrors []error
 	var slaveToAuctionIDMap = make(map[string]string, len(auctionIDs))
 
 	for _, auctionFullID := range auctionIDs {
@@ -363,7 +369,7 @@ func (a *AdOceanAdapter) MakeBids(
 			height, _ := strconv.ParseInt(bid.Height, 10, 64)
 			adCode, err := a.prepareAdCodeForBid(bid)
 			if err != nil {
-				errors = append(errors, err)
+				parsingErrors = append(parsingErrors, err)
 				continue
 			}
 
@@ -383,7 +389,7 @@ func (a *AdOceanAdapter) MakeBids(
 		}
 	}
 
-	return parsedResponses, errors
+	return parsedResponses, parsingErrors
 }
 
 func (a *AdOceanAdapter) prepareAdCodeForBid(bid ResponseAdUnit) (string, error) {
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/ix/ix.go b/adapters/ix/ix.go
index 796fefbb..300858e3 100644
--- a/adapters/ix/ix.go
+++ b/adapters/ix/ix.go
@@ -83,7 +83,7 @@ func (a *IxAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters
 	}
 	requestCopy.Imp = filteredImps
 
-	setSitePublisherId(&requestCopy, uniqueSiteIDs, ixDiag)
+	setPublisherId(&requestCopy, uniqueSiteIDs, ixDiag)
 
 	err := setIxDiagIntoExtRequest(&requestCopy, ixDiag)
 	if err != nil {
@@ -101,7 +101,11 @@ func (a *IxAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters
 	return requests, errs
 }
 
-func setSitePublisherId(requestCopy *openrtb2.BidRequest, uniqueSiteIDs map[string]struct{}, ixDiag *IxDiag) {
+func setPublisherId(requestCopy *openrtb2.BidRequest, uniqueSiteIDs map[string]struct{}, ixDiag *IxDiag) {
+	siteIDs := make([]string, 0, len(uniqueSiteIDs))
+	for key := range uniqueSiteIDs {
+		siteIDs = append(siteIDs, key)
+	}
 	if requestCopy.Site != nil {
 		site := *requestCopy.Site
 		if site.Publisher == nil {
@@ -110,22 +114,33 @@ func setSitePublisherId(requestCopy *openrtb2.BidRequest, uniqueSiteIDs map[stri
 			publisher := *site.Publisher
 			site.Publisher = &publisher
 		}
-
-		siteIDs := make([]string, 0, len(uniqueSiteIDs))
-		for key := range uniqueSiteIDs {
-			siteIDs = append(siteIDs, key)
-		}
 		if len(siteIDs) == 1 {
 			site.Publisher.ID = siteIDs[0]
 		}
-		if len(siteIDs) > 1 {
-			// Sorting siteIDs for predictable output as Go maps don't guarantee order
-			sort.Strings(siteIDs)
-			multipleSiteIDs := strings.Join(siteIDs, ", ")
-			ixDiag.MultipleSiteIds = multipleSiteIDs
-		}
 		requestCopy.Site = &site
 	}
+
+	if requestCopy.App != nil {
+		app := *requestCopy.App
+
+		if app.Publisher == nil {
+			app.Publisher = &openrtb2.Publisher{}
+		} else {
+			publisher := *app.Publisher
+			app.Publisher = &publisher
+		}
+		if len(siteIDs) == 1 {
+			app.Publisher.ID = siteIDs[0]
+		}
+		requestCopy.App = &app
+	}
+
+	if len(siteIDs) > 1 {
+		// Sorting siteIDs for predictable output as Go maps don't guarantee order
+		sort.Strings(siteIDs)
+		multipleSiteIDs := strings.Join(siteIDs, ", ")
+		ixDiag.MultipleSiteIds = multipleSiteIDs
+	}
 }
 
 func unmarshalToIxExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpIx, error) {
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/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index 49ef7074..c7dc23e1 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -528,7 +528,7 @@ func (p usersyncPrivacy) CCPAAllowsBidderSync(bidder string) bool {
 }
 
 func (p usersyncPrivacy) ActivityAllowsUserSync(bidder string) bool {
-	activityResult := p.activityControl.Allow(privacy.ActivitySyncUser,
+	activityResult := p.activityControl.Evaluate(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..4fae58cb 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -111,7 +111,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
 			}
 		}
 
-		userSyncActivityAllowed := activities.Allow(privacy.ActivitySyncUser,
+		userSyncActivityAllowed := activities.Evaluate(privacy.ActivitySyncUser,
 			privacy.ScopedName{Scope: privacy.ScopeTypeBidder, Name: bidderName})
 		if userSyncActivityAllowed == privacy.ActivityDeny {
 			w.WriteHeader(http.StatusUnavailableForLegalReasons)
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index c360ad91..b9ca199c 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"
@@ -297,6 +298,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,
@@ -377,6 +379,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderVox:               vox.Builder,
 		openrtb_ext.BidderVrtcal:            vrtcal.Builder,
 		openrtb_ext.BidderXeworks:           xeworks.Builder,
+		openrtb_ext.BidderXtrmqb:            limelightDigital.Builder,
 		openrtb_ext.BidderYahooAds:          yahooAds.Builder,
 		openrtb_ext.BidderYahooAdvertising:  yahooAds.Builder,
 		openrtb_ext.BidderYahooSSP:          yahooAds.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/bidder.go b/exchange/bidder.go
index 03cd81f5..838c46f7 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -548,6 +548,7 @@ func (bidder *bidderAdapter) doRequestImpl(ctx context.Context, req *adapters.Re
 
 	if tmaxAdjustments != nil && tmaxAdjustments.IsEnforced {
 		if hasShorterDurationThanTmax(&bidderTmaxCtx{ctx}, *tmaxAdjustments) {
+			bidder.me.RecordTMaxTimeout()
 			return &httpCallInfo{
 				request: req,
 				err:     errTmaxTimeout,
diff --git a/exchange/utils.go b/exchange/utils.go
index e70f5249..8f6f00b2 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -153,7 +153,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)
+		fetchBidsActivityAllowed := auctionReq.Activities.Evaluate(privacy.ActivityFetchBids, scopedName)
 		if fetchBidsActivityAllowed == privacy.ActivityDeny {
 			// skip the call to a bidder if fetchBids activity is not allowed
 			// do not add this bidder to allowedBidderRequests
@@ -173,7 +173,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 			}
 		}
 
-		passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName)
+		passIDActivityAllowed := auctionReq.Activities.Evaluate(privacy.ActivityTransmitUserFPD, scopedName)
 		if passIDActivityAllowed == privacy.ActivityDeny {
 			privacyEnforcement.UFPD = true
 		} else {
@@ -190,7 +190,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 			privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String())
 		}
 
-		passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName)
+		passGeoActivityAllowed := auctionReq.Activities.Evaluate(privacy.ActivityTransmitPreciseGeo, scopedName)
 		if passGeoActivityAllowed == privacy.ActivityDeny {
 			privacyEnforcement.PreciseGeo = true
 		} else {
diff --git a/metrics/config/metrics.go b/metrics/config/metrics.go
index 0cf0533d..bf173253 100644
--- a/metrics/config/metrics.go
+++ b/metrics/config/metrics.go
@@ -80,6 +80,12 @@ func (me *MultiMetricsEngine) RecordConnectionAccept(success bool) {
 	}
 }
 
+func (me *MultiMetricsEngine) RecordTMaxTimeout() {
+	for _, thisME := range *me {
+		thisME.RecordTMaxTimeout()
+	}
+}
+
 func (me *MultiMetricsEngine) RecordConnectionClose(success bool) {
 	for _, thisME := range *me {
 		thisME.RecordConnectionClose(success)
@@ -391,6 +397,10 @@ func (me *NilMetricsEngine) RecordRequest(labels metrics.Labels) {
 func (me *NilMetricsEngine) RecordConnectionAccept(success bool) {
 }
 
+// RecordTMaxTimeout as a noop
+func (me *NilMetricsEngine) RecordTMaxTimeout() {
+}
+
 // RecordConnectionClose as a noop
 func (me *NilMetricsEngine) RecordConnectionClose(success bool) {
 }
diff --git a/metrics/go_metrics.go b/metrics/go_metrics.go
index 64726e30..382dc64c 100644
--- a/metrics/go_metrics.go
+++ b/metrics/go_metrics.go
@@ -14,6 +14,7 @@ import (
 type Metrics struct {
 	MetricsRegistry                metrics.Registry
 	ConnectionCounter              metrics.Counter
+	TMaxTimeoutCounter             metrics.Counter
 	ConnectionAcceptErrorMeter     metrics.Meter
 	ConnectionCloseErrorMeter      metrics.Meter
 	ImpMeter                       metrics.Meter
@@ -288,6 +289,7 @@ func getModuleNames(moduleStageNames map[string][]string) []string {
 func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, disableAccountMetrics config.DisabledMetrics, syncerKeys []string, moduleStageNames map[string][]string) *Metrics {
 	newMetrics := NewBlankMetrics(registry, exchanges, disableAccountMetrics, moduleStageNames)
 	newMetrics.ConnectionCounter = metrics.GetOrRegisterCounter("active_connections", registry)
+	newMetrics.TMaxTimeoutCounter = metrics.GetOrRegisterCounter("tmax_timeout", registry)
 	newMetrics.ConnectionAcceptErrorMeter = metrics.GetOrRegisterMeter("connection_accept_errors", registry)
 	newMetrics.ConnectionCloseErrorMeter = metrics.GetOrRegisterMeter("connection_close_errors", registry)
 	newMetrics.ImpMeter = metrics.GetOrRegisterMeter("imps_requested", registry)
@@ -713,6 +715,10 @@ func (me *Metrics) RecordConnectionAccept(success bool) {
 	}
 }
 
+func (m *Metrics) RecordTMaxTimeout() {
+	m.TMaxTimeoutCounter.Inc(1)
+}
+
 func (me *Metrics) RecordConnectionClose(success bool) {
 	if success {
 		me.ConnectionCounter.Dec(1)
diff --git a/metrics/metrics.go b/metrics/metrics.go
index 214e7c1d..c30f8167 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -421,6 +421,7 @@ func SyncerSetUidStatuses() []SyncerSetUidStatus {
 // is generally not useful.
 type MetricsEngine interface {
 	RecordConnectionAccept(success bool)
+	RecordTMaxTimeout()
 	RecordConnectionClose(success bool)
 	RecordRequest(labels Labels)                           // ignores adapter. only statusOk and statusErr fom status
 	RecordImps(labels ImpLabels)                           // RecordImps across openRTB2 engines that support the 'Native' Imp Type
diff --git a/metrics/metrics_mock.go b/metrics/metrics_mock.go
index 0ce238c5..a518b98d 100644
--- a/metrics/metrics_mock.go
+++ b/metrics/metrics_mock.go
@@ -22,6 +22,11 @@ func (me *MetricsEngineMock) RecordConnectionAccept(success bool) {
 	me.Called(success)
 }
 
+// RecordTMaxTimeout mock
+func (me *MetricsEngineMock) RecordTMaxTimeout() {
+	me.Called()
+}
+
 // RecordConnectionClose mock
 func (me *MetricsEngineMock) RecordConnectionClose(success bool) {
 	me.Called(success)
diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go
index f9d0c4d9..c57ba221 100644
--- a/metrics/prometheus/prometheus.go
+++ b/metrics/prometheus/prometheus.go
@@ -18,6 +18,7 @@ type Metrics struct {
 	Gatherer   *prometheus.Registry
 
 	// General Metrics
+	tmaxTimeout                  prometheus.Counter
 	connectionsClosed            prometheus.Counter
 	connectionsError             *prometheus.CounterVec
 	connectionsOpened            prometheus.Counter
@@ -197,6 +198,10 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
 		"connections_opened",
 		"Count of successful connections opened to Prebid Server.")
 
+	metrics.tmaxTimeout = newCounterWithoutLabels(cfg, reg,
+		"tmax_timeout",
+		"Count of requests rejected due to Tmax timeout exceed.")
+
 	metrics.cookieSync = newCounter(cfg, reg,
 		"cookie_sync_requests",
 		"Count of cookie sync requests to Prebid Server.",
@@ -685,6 +690,10 @@ func (m *Metrics) RecordConnectionAccept(success bool) {
 	}
 }
 
+func (m *Metrics) RecordTMaxTimeout() {
+	m.tmaxTimeout.Inc()
+}
+
 func (m *Metrics) RecordConnectionClose(success bool) {
 	if success {
 		m.connectionsClosed.Inc()
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index b42966ee..35682760 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -194,6 +194,7 @@ const (
 	BidderKayzen            BidderName = "kayzen"
 	BidderKidoz             BidderName = "kidoz"
 	BidderKiviads           BidderName = "kiviads"
+	BidderLmKiviads         BidderName = "lm_kiviads"
 	BidderKrushmedia        BidderName = "krushmedia"
 	BidderKubient           BidderName = "kubient"
 	BidderLimelightDigital  BidderName = "limelightDigital"
@@ -274,6 +275,7 @@ const (
 	BidderVox               BidderName = "vox"
 	BidderVrtcal            BidderName = "vrtcal"
 	BidderXeworks           BidderName = "xeworks"
+	BidderXtrmqb            BidderName = "xtrmqb"
 	BidderYahooAds          BidderName = "yahooAds"
 	BidderYahooAdvertising  BidderName = "yahooAdvertising"
 	BidderYahooSSP          BidderName = "yahoossp"
@@ -396,6 +398,7 @@ func CoreBidderNames() []BidderName {
 		BidderKayzen,
 		BidderKidoz,
 		BidderKiviads,
+		BidderLmKiviads,
 		BidderKrushmedia,
 		BidderKubient,
 		BidderLimelightDigital,
@@ -476,6 +479,7 @@ func CoreBidderNames() []BidderName {
 		BidderVox,
 		BidderVrtcal,
 		BidderXeworks,
+		BidderXtrmqb,
 		BidderYahooAds,
 		BidderYahooAdvertising,
 		BidderYahooSSP,
diff --git a/openrtb_ext/imp_adocean.go b/openrtb_ext/imp_adocean.go
index e690e929..1dd64d28 100644
--- a/openrtb_ext/imp_adocean.go
+++ b/openrtb_ext/imp_adocean.go
@@ -1,7 +1,7 @@
 package openrtb_ext
 
 type ExtImpAdOcean struct {
-	EmitterDomain string `json:"emiter"`
+	EmitterPrefix string `json:"emitterPrefix"`
 	MasterID      string `json:"masterId"`
 	SlaveID       string `json:"slaveId"`
 }
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..3a360c5d 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
@@ -86,15 +87,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 +112,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)
@@ -124,14 +134,14 @@ func activityDefaultToDefaultResult(activityDefault *bool) ActivityResult {
 	return ActivityDeny
 }
 
-func (e ActivityControl) Allow(activity Activity, target ScopedName) ActivityResult {
+func (e ActivityControl) Evaluate(activity Activity, target ScopedName) ActivityResult {
 	plan, planDefined := e.plans[activity]
 
 	if !planDefined {
 		return ActivityAbstain
 	}
 
-	return plan.Allow(target)
+	return plan.Evaluate(target)
 }
 
 type ActivityPlan struct {
@@ -139,9 +149,9 @@ type ActivityPlan struct {
 	rules         []ActivityRule
 }
 
-func (p ActivityPlan) Allow(target ScopedName) ActivityResult {
+func (p ActivityPlan) Evaluate(target ScopedName) ActivityResult {
 	for _, rule := range p.rules {
-		result := rule.Allow(target)
+		result := rule.Evaluate(target)
 		if result == ActivityDeny || result == ActivityAllow {
 			return result
 		}
@@ -150,51 +160,57 @@ func (p ActivityPlan) Allow(target ScopedName) ActivityResult {
 }
 
 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/adocean.yaml b/static/bidder-info/adocean.yaml
index 50013095..3932a7a7 100644
--- a/static/bidder-info/adocean.yaml
+++ b/static/bidder-info/adocean.yaml
@@ -1,4 +1,4 @@
-endpoint: "https://{{.Host}}"
+endpoint: "https://{{.Host}}.adocean.pl"
 maintainer:
   email: "aoteam@gemius.com"
 gvlVendorID: 328
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/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/xtrmqb.yaml b/static/bidder-info/xtrmqb.yaml
new file mode 100644
index 00000000..c1f0da3f
--- /dev/null
+++ b/static/bidder-info/xtrmqb.yaml
@@ -0,0 +1,16 @@
+endpoint: "http://ads-pbs.ortb.tech/openrtb/{{.PublisherID}}?host={{.Host}}"
+maintainer:
+  email: "engineering@project-limelight.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - audio
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - audio
+      - native
diff --git a/static/bidder-params/adocean.json b/static/bidder-params/adocean.json
index 7530c647..a3c5cbc9 100644
--- a/static/bidder-params/adocean.json
+++ b/static/bidder-params/adocean.json
@@ -6,9 +6,14 @@
   "properties": {
     "emiter": {
       "type": "string",
-      "description": "AdOcean emiter",
+      "description": "Deprecated, use emitterPrefix instead. AdOcean emiter",
       "pattern": ".+"
     },
+    "emitterPrefix": {
+      "type": "string",
+      "description": "AdOcean emitter prefix",
+      "pattern": "^[\\w\\-]+$"
+    },
     "masterId": {
       "type": "string",
       "description": "Master's id",
@@ -20,5 +25,8 @@
       "pattern": "^adocean[\\w.]+$"
     }
   },
-  "required": ["emiter", "masterId", "slaveId"]
+  "oneOf": [
+    { "required": ["emiter", "masterId", "slaveId"] },
+    { "required": ["emitterPrefix", "masterId", "slaveId"] }
+  ]
 }
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"
+  ]
+}
diff --git a/static/bidder-params/xtrmqb.json b/static/bidder-params/xtrmqb.json
new file mode 100644
index 00000000..59e711e9
--- /dev/null
+++ b/static/bidder-params/xtrmqb.json
@@ -0,0 +1,23 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "XTRM QB Adapter Params",
+  "description": "A schema which validates params accepted by the XTRM QB adapter",
+  "type": "object",
+
+  "properties": {
+    "host": {
+      "type": "string",
+      "description": "Ad network's RTB host",
+      "format": "hostname",
+      "pattern": "^.+\\..+$"
+    },
+    "publisherId": {
+      "type": ["integer", "string"],
+      "description": "Publisher ID",
+      "minimum": 1,
+      "pattern": "^[1-9][0-9]*$"
+    }
+  },
+
+  "required": ["host", "publisherId"]
+}


Test patch:
diff --git a/adapters/adocean/adocean_test.go b/adapters/adocean/adocean_test.go
index 47fe5848..8d646cb9 100644
--- a/adapters/adocean/adocean_test.go
+++ b/adapters/adocean/adocean_test.go
@@ -11,7 +11,7 @@ import (
 
 func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderAdOcean, config.Adapter{
-		Endpoint: "https://{{.Host}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+		Endpoint: "https://{{.Host}}.adocean.pl"}, 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/adocean/adoceantest/exemplary/multi-banner-impression.json b/adapters/adocean/adoceantest/exemplary/multi-banner-impression.json
index bdb5d969..f70bc467 100644
--- a/adapters/adocean/adoceantest/exemplary/multi-banner-impression.json
+++ b/adapters/adocean/adoceantest/exemplary/multi-banner-impression.json
@@ -9,7 +9,7 @@
       "id": "ao-test",
       "ext": {
         "bidder": {
-          "emiter": "myao.adocean.pl",
+          "emitterPrefix": "myao",
           "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
           "slaveId": "adoceanmyaozpniqismex"
         }
@@ -27,7 +27,7 @@
       "id": "secod-twelve",
       "ext": {
         "bidder": {
-          "emiter": "myao.adocean.pl",
+          "emitterPrefix": "myao",
           "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
           "slaveId": "adoceanmyaowafpdwlrks"
         }
@@ -72,7 +72,7 @@
   },
   "httpCalls": [{
     "expectedRequest": {
-      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Asecod-twelve&aosspsizes=myaowafpdwlrks~300x250-myaozpniqismex~300x250_320x600&devmake=&devmodel=&devos=&devosv=&dpidmd5=f2ba45ece57cff9477d5a8083b138c9g&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.2.0"
+      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Asecod-twelve&aosspsizes=myaowafpdwlrks~300x250-myaozpniqismex~300x250_320x600&devmake=&devmodel=&devos=&devosv=&dpidmd5=f2ba45ece57cff9477d5a8083b138c9g&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.3.0"
     },
     "mockResponse": {
       "status": 200,
diff --git a/adapters/adocean/adoceantest/exemplary/single-banner-impression.json b/adapters/adocean/adoceantest/exemplary/single-banner-impression.json
index d4fabebb..19b95281 100644
--- a/adapters/adocean/adoceantest/exemplary/single-banner-impression.json
+++ b/adapters/adocean/adoceantest/exemplary/single-banner-impression.json
@@ -10,7 +10,7 @@
         "id": "ao-test",
         "ext": {
           "bidder": {
-            "emiter": "myao.adocean.pl",
+            "emitterPrefix": "myao",
             "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
             "slaveId": "adoceanmyaozpniqismex"
           }
@@ -60,7 +60,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aosspsizes=myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&ifa=f2ba45ece57cff9477d5a8083b138c9a&nc=1&nosecure=1&pbsrv_v=1.2.0"
+        "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aosspsizes=myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&ifa=f2ba45ece57cff9477d5a8083b138c9a&nc=1&nosecure=1&pbsrv_v=1.3.0"
       },
       "mockResponse": {
         "status": 200,
diff --git a/adapters/adocean/adoceantest/supplemental/app.json b/adapters/adocean/adoceantest/supplemental/app.json
index efb53692..994495d5 100644
--- a/adapters/adocean/adoceantest/supplemental/app.json
+++ b/adapters/adocean/adoceantest/supplemental/app.json
@@ -10,7 +10,7 @@
         "id": "ao-test",
         "ext": {
           "bidder": {
-            "emiter": "myao.adocean.pl",
+            "emitterPrefix": "myao",
             "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
             "slaveId": "adoceanmyaozpniqismex"
           }
@@ -54,7 +54,7 @@
   "httpCalls": [
     {
         "expectedRequest": {
-          "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aosspsizes=myaozpniqismex~300x250&app=1&appbundle=12345&appdomain=example.com&appname=Weather+App&devmake=&devmodel=&devos=iOS&devosv=&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&ifa=f2ba45ece57cff9477d5a8083b138c9a&nc=1&nosecure=1&pbsrv_v=1.2.0"
+          "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aosspsizes=myaozpniqismex~300x250&app=1&appbundle=12345&appdomain=example.com&appname=Weather+App&devmake=&devmodel=&devos=iOS&devosv=&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&ifa=f2ba45ece57cff9477d5a8083b138c9a&nc=1&nosecure=1&pbsrv_v=1.3.0"
         },
       "mockResponse": {
         "status": 200,
diff --git a/adapters/adocean/adoceantest/supplemental/bad-response.json b/adapters/adocean/adoceantest/supplemental/bad-response.json
index 62d23669..43e62d6f 100644
--- a/adapters/adocean/adoceantest/supplemental/bad-response.json
+++ b/adapters/adocean/adoceantest/supplemental/bad-response.json
@@ -10,7 +10,7 @@
         "id": "ao-test",
         "ext": {
           "bidder": {
-            "emiter": "myao.adocean.pl",
+            "emitterPrefix": "myao",
             "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
             "slaveId": "adoceanmyaozpniqismex"
           }
@@ -49,7 +49,7 @@
   "httpCalls": [
     {
         "expectedRequest": {
-          "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aosspsizes=myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.2.0"
+          "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aosspsizes=myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.3.0"
         },
       "mockResponse": {
         "status": 200,
diff --git a/adapters/adocean/adoceantest/supplemental/emiter-param-error.json b/adapters/adocean/adoceantest/supplemental/emiter-param-error.json
new file mode 100644
index 00000000..154c92e8
--- /dev/null
+++ b/adapters/adocean/adoceantest/supplemental/emiter-param-error.json
@@ -0,0 +1,55 @@
+{
+  "mockBidRequest": {
+    "id": "9ed903f4-383d-406b-8011-4f06526cb02c",
+    "source": {
+      "tid": "9ed903f4-383d-406b-8011-4f06526cb02c"
+    },
+    "tmax": 1000,
+    "imp": [
+      {
+        "id": "ao-test",
+        "ext": {
+          "bidder": {
+            "emiter": "myao.adocean.pl",
+            "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
+            "slaveId": "adoceanmyaozpniqismex"
+          }
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        }
+      }
+    ],
+    "test": 1,
+    "ext": {
+      "prebid": {
+        "targeting": {
+          "includewinners": true,
+          "includebidderkeys": false
+        }
+      }
+    },
+    "site": {
+      "publisher": {
+        "id": "1"
+      },
+      "page": "http://example.com/test.html"
+    },
+    "device": {
+      "w": 1280,
+      "h": 720,
+      "ip": "192.168.1.1"
+    }
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "No emitterPrefix param",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adocean/adoceantest/supplemental/encode-error.json b/adapters/adocean/adoceantest/supplemental/encode-error.json
index c5dce777..98b977fd 100644
--- a/adapters/adocean/adoceantest/supplemental/encode-error.json
+++ b/adapters/adocean/adoceantest/supplemental/encode-error.json
@@ -10,7 +10,7 @@
         "id": "ao-test",
         "ext": {
           "bidder": {
-            "emiter": "myao.adocean.pl",
+            "emitterPrefix": "myao",
             "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
             "slaveId": "adoceanmyaozpniqismex"
           }
@@ -49,7 +49,7 @@
   "httpCalls": [
     {
         "expectedRequest": {
-          "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aosspsizes=myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.2.0"
+          "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aosspsizes=myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.3.0"
         },
       "mockResponse": {
         "status": 200,
diff --git a/adapters/adocean/adoceantest/supplemental/network-error.json b/adapters/adocean/adoceantest/supplemental/network-error.json
index 567179ab..c18ddf16 100644
--- a/adapters/adocean/adoceantest/supplemental/network-error.json
+++ b/adapters/adocean/adoceantest/supplemental/network-error.json
@@ -10,7 +10,7 @@
         "id": "ao-test",
         "ext": {
           "bidder": {
-            "emiter": "myao.adocean.pl",
+            "emitterPrefix": "myao",
             "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
             "slaveId": "adoceanmyaozpniqismex"
           }
@@ -49,7 +49,7 @@
   "httpCalls": [
     {
         "expectedRequest": {
-          "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aosspsizes=myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.2.0"
+          "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aosspsizes=myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.3.0"
         },
       "mockResponse": {
         "status": 500,
diff --git a/adapters/adocean/adoceantest/supplemental/no-bid.json b/adapters/adocean/adoceantest/supplemental/no-bid.json
index 19778b1e..caf2cc9e 100644
--- a/adapters/adocean/adoceantest/supplemental/no-bid.json
+++ b/adapters/adocean/adoceantest/supplemental/no-bid.json
@@ -9,7 +9,7 @@
       "id": "ao-test",
       "ext": {
         "bidder": {
-          "emiter": "myao.adocean.pl",
+          "emitterPrefix": "myao",
           "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
           "slaveId": "adoceanmyaozpniqismex"
         }
@@ -24,7 +24,7 @@
       "id": "ao-test-two",
       "ext": {
         "bidder": {
-          "emiter": "myao.adocean.pl",
+          "emitterPrefix": "myao",
           "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
           "slaveId": "adoceanmyaowafpdwlrks"
         }
@@ -39,7 +39,7 @@
       "id": "ao-test-three",
       "ext": {
         "bidder": {
-          "emiter": "myao.adocean.pl",
+          "emitterPrefix": "myao",
           "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
           "slaveId": "adoceanmyaowafpdwlrks"
         }
@@ -83,7 +83,7 @@
   },
   "httpCalls": [{
     "expectedRequest": {
-      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Aao-test-two&aosspsizes=myaowafpdwlrks~300x250-myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.2.0"
+      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Aao-test-two&aosspsizes=myaowafpdwlrks~300x250-myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.3.0"
     },
     "mockResponse": {
       "status": 200,
@@ -108,7 +108,7 @@
     }
   }, {
     "expectedRequest": {
-      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaowafpdwlrks%3Aao-test-three&aosspsizes=myaowafpdwlrks~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.2.0"
+      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaowafpdwlrks%3Aao-test-three&aosspsizes=myaowafpdwlrks~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.3.0"
     },
     "mockResponse": {
       "status": 200,
diff --git a/adapters/adocean/adoceantest/supplemental/no-sizes.json b/adapters/adocean/adoceantest/supplemental/no-sizes.json
index 25503190..465698fd 100644
--- a/adapters/adocean/adoceantest/supplemental/no-sizes.json
+++ b/adapters/adocean/adoceantest/supplemental/no-sizes.json
@@ -9,7 +9,7 @@
       "id": "ao-test",
       "ext": {
         "bidder": {
-          "emiter": "myao.adocean.pl",
+          "emitterPrefix": "myao",
           "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
           "slaveId": "adoceanmyaozpniqismex"
         }
@@ -18,7 +18,7 @@
       "id": "ao-test-two",
       "ext": {
         "bidder": {
-          "emiter": "myao.adocean.pl",
+          "emitterPrefix": "myao",
           "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
           "slaveId": "adoceanmyaowafpdwlrks"
         }
@@ -30,7 +30,7 @@
       "id": "ao-test-three",
       "ext": {
         "bidder": {
-          "emiter": "myao.adocean.pl",
+          "emitterPrefix": "myao",
           "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
           "slaveId": "adoceanmyaowafpdwlrks"
         }
@@ -72,7 +72,7 @@
   },
   "httpCalls": [{
     "expectedRequest": {
-      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Aao-test-two&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.2.0"
+      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Aao-test-two&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.3.0"
     },
     "mockResponse": {
       "status": 200,
@@ -106,7 +106,7 @@
     }
   }, {
     "expectedRequest": {
-      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaowafpdwlrks%3Aao-test-three&aosspsizes=myaowafpdwlrks~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.2.0"
+      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaowafpdwlrks%3Aao-test-three&aosspsizes=myaowafpdwlrks~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.3.0"
     },
     "mockResponse": {
       "status": 200,
diff --git a/adapters/adocean/adoceantest/supplemental/requests-merge.json b/adapters/adocean/adoceantest/supplemental/requests-merge.json
index eed67321..9021bf66 100644
--- a/adapters/adocean/adoceantest/supplemental/requests-merge.json
+++ b/adapters/adocean/adoceantest/supplemental/requests-merge.json
@@ -9,7 +9,7 @@
       "id": "ao-test",
       "ext": {
         "bidder": {
-          "emiter": "myao.adocean.pl",
+          "emitterPrefix": "myao",
           "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
           "slaveId": "adoceanmyaozpniqismex"
         }
@@ -24,7 +24,7 @@
       "id": "ao-test-two",
       "ext": {
         "bidder": {
-          "emiter": "myao.adocean.pl",
+          "emitterPrefix": "myao",
           "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
           "slaveId": "adoceanmyaowafpdwlrks"
         }
@@ -39,7 +39,7 @@
       "id": "ao-test-three",
       "ext": {
         "bidder": {
-          "emiter": "myao.adocean.pl",
+          "emitterPrefix": "myao",
           "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7",
           "slaveId": "adoceanmyaowafpdwlrks"
         }
@@ -83,7 +83,7 @@
   },
   "httpCalls": [{
     "expectedRequest": {
-      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Aao-test-two&aosspsizes=myaowafpdwlrks~300x250-myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.2.0"
+      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Aao-test-two&aosspsizes=myaowafpdwlrks~300x250-myaozpniqismex~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.3.0"
     },
     "mockResponse": {
       "status": 200,
@@ -117,7 +117,7 @@
     }
   }, {
     "expectedRequest": {
-      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaowafpdwlrks%3Aao-test-three&aosspsizes=myaowafpdwlrks~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.2.0"
+      "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaowafpdwlrks%3Aao-test-three&aosspsizes=myaowafpdwlrks~300x250&devmake=&devmodel=&devos=&devosv=&dpidmd5=&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.3.0"
     },
     "mockResponse": {
       "status": 200,
diff --git a/adapters/adocean/params_test.go b/adapters/adocean/params_test.go
index 1a88c471..18625b5e 100644
--- a/adapters/adocean/params_test.go
+++ b/adapters/adocean/params_test.go
@@ -34,17 +34,20 @@ func TestInvalidParams(t *testing.T) {
 }
 
 var validParams = []string{
+	`{"emitterPrefix": "myao", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`,
 	`{"emiter": "myao.adocean.pl", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`,
 }
 
 var invalidParams = []string{
 	`{}`,
-	`{"masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`,
+	`{"emitterPrefix": "myao", "slaveId": "adoceanmyaozpniqismex"}`,
+	`{"emitterPrefix": "myao", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7"}`,
+	`{"emitterPrefix": "", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`,
+	`{"emitterPrefix": "myao", "", "slaveId": "adoceanmyaozpniqismex"}`,
+	`{"emitterPrefix": "myao", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": ""}`,
+	`{"emitterPrefix": "myao", "masterId": "tmYF.DMl7Z utQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`,
+	`{"emitterPrefix": "myao", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmy iqismex"}`,
+	`{"emitterPrefix": "myao.adocean.pl", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`,
 	`{"emiter": "myao.adocean.pl", "slaveId": "adoceanmyaozpniqismex"}`,
-	`{"emiter": "myao.adocean.pl", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7"}`,
 	`{"emiter": "", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`,
-	`{"emiter": "myao.adocean.pl", "", "slaveId": "adoceanmyaozpniqismex"}`,
-	`{"emiter": "myao.adocean.pl", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": ""}`,
-	`{"emiter": "myao.adocean.pl", "masterId": "tmYF.DMl7Z utQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`,
-	`{"emiter": "myao.adocean.pl", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmy iqismex"}`,
 }
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/ix/ixtest/exemplary/app-site-id.json b/adapters/ix/ixtest/exemplary/app-site-id.json
new file mode 100644
index 00000000..80441cc4
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/app-site-id.json
@@ -0,0 +1,140 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id-1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ],
+        "app": {
+            "domain": "https://www.example.com/"
+        },
+        "ext": {
+            "prebid": {
+                "channel": {
+                    "name": "web",
+                    "version": "7.0.0"
+                }
+            }
+        }
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id-1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ],
+                    "app": {
+                        "domain": "https://www.example.com/",
+                        "publisher": {
+                            "id": "569749"
+                        }
+                    },
+                    "ext": {
+                        "ixdiag": {
+                          "pbjsv": "7.0.0"
+                        },
+                        "prebid": {
+                          "channel": {
+                            "name": "web",
+                            "version": "7.0.0"
+                          }
+                        }
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id-1",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id-1",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/supplemental/app-site-id-publisher.json b/adapters/ix/ixtest/supplemental/app-site-id-publisher.json
new file mode 100644
index 00000000..aec2c124
--- /dev/null
+++ b/adapters/ix/ixtest/supplemental/app-site-id-publisher.json
@@ -0,0 +1,144 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id-1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ],
+        "app": {
+            "domain": "https://www.example.com/",
+            "publisher": {
+                "name": "https://www.example.com/"
+            }
+        },
+        "ext": {
+            "prebid": {
+                "channel": {
+                    "name": "web",
+                    "version": "7.0.0"
+                }
+            }
+        }
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id-1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ],
+                    "app": {
+                        "domain": "https://www.example.com/",
+                        "publisher": {
+                            "id": "569749",
+                            "name": "https://www.example.com/"
+                        }
+                    },
+                    "ext": {
+                        "ixdiag": {
+                          "pbjsv": "7.0.0"
+                        },
+                        "prebid": {
+                          "channel": {
+                            "name": "web",
+                            "version": "7.0.0"
+                          }
+                        }
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id-1",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id-1",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/supplemental/sid.json b/adapters/ix/ixtest/supplemental/sid.json
index c2156b6b..3d6862d1 100644
--- a/adapters/ix/ixtest/supplemental/sid.json
+++ b/adapters/ix/ixtest/supplemental/sid.json
@@ -61,6 +61,12 @@
       "expectedRequest": {
         "uri": "http://host/endpoint",
         "body": {
+          "ext": {
+            "ixdiag": {
+              "multipleSiteIds": "569749, 569750"
+            },
+            "prebid": null
+          },
           "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/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/exchange/bidder_test.go b/exchange/bidder_test.go
index 9d58e947..e2d476c0 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -3262,3 +3262,57 @@ func TestDoRequestImplWithTmax(t *testing.T) {
 		test.assertFn(httpCallInfo.err)
 	}
 }
+
+func TestDoRequestImplWithTmaxTimeout(t *testing.T) {
+	respStatus := 200
+	respBody := "{\"bid\":false}"
+	server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody))
+	defer server.Close()
+	requestStartTime := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
+
+	bidRequest := adapters.RequestData{
+		Method: "POST",
+		Uri:    server.URL,
+		Body:   []byte(`{"id":"this-id","app":{"publisher":{"id":"pub-id"}}}`),
+	}
+
+	metricsMock := &metrics.MetricsEngineMock{}
+	metricsMock.On("RecordOverheadTime", metrics.PreBidder, mock.Anything).Once()
+	metricsMock.On("RecordTMaxTimeout").Once()
+
+	bidderAdapter := bidderAdapter{
+		me:     metricsMock,
+		Client: server.Client(),
+	}
+	logger := func(msg string, args ...interface{}) {}
+
+	tests := []struct {
+		ctxDeadline     time.Time
+		description     string
+		tmaxAdjustments *TmaxAdjustmentsPreprocessed
+		assertFn        func(err error)
+	}{
+		{
+			ctxDeadline:     time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
+			description:     "returns-tmax-timeout-error",
+			tmaxAdjustments: &TmaxAdjustmentsPreprocessed{IsEnforced: true, PBSResponsePreparationDuration: 100, BidderNetworkLatencyBuffer: 10, BidderResponseDurationMin: 5000},
+			assertFn:        func(err error) { assert.Equal(t, errTmaxTimeout, err) },
+		},
+	}
+	for _, test := range tests {
+		var (
+			ctx      context.Context
+			cancelFn context.CancelFunc
+		)
+
+		if test.ctxDeadline.IsZero() {
+			ctx = context.Background()
+		} else {
+			ctx, cancelFn = context.WithDeadline(context.Background(), test.ctxDeadline)
+			defer cancelFn()
+		}
+
+		httpCallInfo := bidderAdapter.doRequestImpl(ctx, &bidRequest, logger, requestStartTime, test.tmaxAdjustments)
+		test.assertFn(httpCallInfo.err)
+	}
+}
diff --git a/metrics/go_metrics_test.go b/metrics/go_metrics_test.go
index 1ab22ac1..515691fe 100644
--- a/metrics/go_metrics_test.go
+++ b/metrics/go_metrics_test.go
@@ -84,6 +84,7 @@ func TestNewMetrics(t *testing.T) {
 	ensureContains(t, registry, "request_over_head_time.make-auction-response", m.OverheadTimer[MakeAuctionResponse])
 	ensureContains(t, registry, "request_over_head_time.make-bidder-requests", m.OverheadTimer[MakeBidderRequests])
 	ensureContains(t, registry, "bidder_server_response_time_seconds", m.BidderServerResponseTimer)
+	ensureContains(t, registry, "tmax_timeout", m.TMaxTimeoutCounter)
 
 	for module, stages := range moduleStageNames {
 		for _, stage := range stages {
diff --git a/privacy/enforcer_test.go b/privacy/enforcer_test.go
index 2b9715f8..49f3a507 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,24 +145,23 @@ func TestNewActivityControl(t *testing.T) {
 }
 
 func TestActivityDefaultToDefaultResult(t *testing.T) {
-
 	testCases := []struct {
 		name            string
 		activityDefault *bool
 		expectedResult  ActivityResult
 	}{
 		{
-			name:            "activityDefault_is_nil",
+			name:            "nil",
 			activityDefault: nil,
 			expectedResult:  ActivityAllow,
 		},
 		{
-			name:            "activityDefault_is_true",
+			name:            "true",
 			activityDefault: ptrutil.ToPtr(true),
 			expectedResult:  ActivityAllow,
 		},
 		{
-			name:            "activityDefault_is_false",
+			name:            "false",
 			activityDefault: ptrutil.ToPtr(false),
 			expectedResult:  ActivityDeny,
 		},
@@ -219,15 +219,14 @@ func TestAllowActivityControl(t *testing.T) {
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualResult := test.activityControl.Allow(test.activity, test.target)
+			actualResult := test.activityControl.Evaluate(test.activity, test.target)
 			assert.Equal(t, test.activityResult, actualResult)
 
 		})
 	}
 }
 
-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,
@@ -401,7 +407,7 @@ func getDefaultActivityPlan() ActivityPlan {
 		defaultResult: ActivityAllow,
 		rules: []ActivityRule{
 			ComponentEnforcementRule{
-				allowed: true,
+				result: ActivityAllow,
 				componentName: []ScopedName{
 					{Scope: "bidder", Name: "bidderA"},
 				},