Repository: prebid/prebid-server
Pull request #2251 (2022-05-19 11:12:33)

The http.ProxyFromEnvironment as Proxy func in custom http.Transport
This PR addresses this issue: #2245  

This change sets the http.ProxyFromEnvironment as Proxy function in all custom http.Transport used in the prebid-server as the http.DefaultTransport does.
https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/net/http/transport.go;l=43
![obraz](https://user-images.githubusercontent.com/104571800/169279277-dee868eb-918c-490c-b95a-7c45b1745e9b.png)

With this change it is possible using prebid-server with an external proxy.


Patch:
diff --git a/adapters/automatad/automatad.go b/adapters/automatad/automatad.go
new file mode 100644
index 00000000..69286b42
--- /dev/null
+++ b/adapters/automatad/automatad.go
@@ -0,0 +1,77 @@
+package automatad
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	endpoint string
+}
+
+// Builder builds a new instance of the Automatad adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+	return bidder, nil
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	requestJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	requestData := &adapters.RequestData{
+		Method: "POST",
+		Uri:    a.endpoint,
+		Body:   requestJSON,
+	}
+
+	return []*adapters.RequestData{requestData}, nil
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if responseData.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if responseData.StatusCode == http.StatusBadRequest {
+		err := &errortypes.BadInput{
+			Message: "Unexpected status code: 400. Malformed request syntax received.",
+		}
+		return nil, []error{err}
+	}
+
+	if responseData.StatusCode != http.StatusOK {
+		err := &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Something went wrong on the bidder's side.", responseData.StatusCode),
+		}
+		return nil, []error{err}
+	}
+
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(responseData.Body, &response); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+	bidResponse.Currency = response.Cur
+	for _, seatBid := range response.SeatBid {
+		for i := range seatBid.Bid {
+			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: openrtb_ext.BidTypeBanner,
+			})
+		}
+	}
+	return bidResponse, nil
+}
diff --git a/adapters/bliink/bliink.go b/adapters/bliink/bliink.go
new file mode 100644
index 00000000..6e179a3f
--- /dev/null
+++ b/adapters/bliink/bliink.go
@@ -0,0 +1,120 @@
+package bliink
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"net/http"
+)
+
+type adapter struct {
+	endpoint string
+}
+
+// Builder builds a new instance of the Bliink adapter for the given bidder with the given config.
+func Builder(_ openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+
+	return bidder, nil
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	requestJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	headers.Add("x-openrtb-version", "2.5")
+
+	requestData := &adapters.RequestData{
+		Method:  "POST",
+		Uri:     a.endpoint,
+		Body:    requestJSON,
+		Headers: headers,
+	}
+
+	return []*adapters.RequestData{requestData}, nil
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if responseData.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if responseData.StatusCode == http.StatusBadRequest {
+		err := &errortypes.BadInput{
+			Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
+		}
+		return nil, []error{err}
+	}
+
+	if responseData.StatusCode != http.StatusOK {
+		err := &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
+		}
+		return nil, []error{err}
+	}
+
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(responseData.Body, &response); err != nil {
+		return nil, []error{err}
+	}
+
+	if len(response.SeatBid) == 0 {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Empty SeatBid array",
+		}}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(response.SeatBid[0].Bid))
+	bidResponse.Currency = response.Cur
+	seatBid := response.SeatBid[0]
+	var errs []error
+	for i, bid := range seatBid.Bid {
+		mediaType, err := getMediaTypeForImp(bid.ImpID, request.Imp)
+		if err != nil {
+			errs = append(errs, err)
+		} else {
+			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: mediaType,
+			})
+		}
+	}
+
+	return bidResponse, errs
+}
+
+func getMediaTypeForImp(impId string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
+	var mediaType openrtb_ext.BidType
+	var typeCnt = 0
+	for _, imp := range imps {
+		if imp.ID == impId {
+			if imp.Banner != nil {
+				typeCnt += 1
+				mediaType = openrtb_ext.BidTypeBanner
+			}
+			if imp.Native != nil {
+				typeCnt += 1
+				mediaType = openrtb_ext.BidTypeNative
+			}
+			if imp.Video != nil {
+				typeCnt += 1
+				mediaType = openrtb_ext.BidTypeVideo
+			}
+		}
+	}
+	if typeCnt == 1 {
+		return mediaType, nil
+	}
+	return mediaType, fmt.Errorf("unable to fetch mediaType in multi-format: %s", impId)
+}
diff --git a/adapters/nextmillennium/nextmillennium.go b/adapters/nextmillennium/nextmillennium.go
index 68ca4dc2..bcb0b287 100644
--- a/adapters/nextmillennium/nextmillennium.go
+++ b/adapters/nextmillennium/nextmillennium.go
@@ -99,12 +99,35 @@ func (adapter *adapter) buildAdapterRequest(prebidBidRequest *openrtb2.BidReques
 }
 
 func createBidRequest(prebidBidRequest *openrtb2.BidRequest, params *openrtb_ext.ImpExtNextMillennium) *NextMillenniumBidRequest {
+	placementID := params.PlacementID
 	bidRequest := NextMillenniumBidRequest{
 		ID:   prebidBidRequest.ID,
 		Test: uint8(prebidBidRequest.Test),
 	}
-	bidRequest.Ext.Prebid.StoredRequest.ID = params.PlacementID
 
+	if params.GroupID != "" {
+		domain := ""
+		size := ""
+
+		if prebidBidRequest.Site != nil {
+			domain = prebidBidRequest.Site.Domain
+		}
+		if prebidBidRequest.App != nil {
+			domain = prebidBidRequest.App.Domain
+		}
+
+		if banner := prebidBidRequest.Imp[0].Banner; banner != nil {
+			if len(banner.Format) > 0 {
+				size = fmt.Sprintf("%dx%d", banner.Format[0].W, banner.Format[0].H)
+			} else if banner.W != nil && banner.H != nil {
+				size = fmt.Sprintf("%dx%d", *banner.W, *banner.H)
+			}
+		}
+
+		placementID = fmt.Sprintf("g%s;%s;%s", params.GroupID, size, domain)
+	}
+
+	bidRequest.Ext.Prebid.StoredRequest.ID = placementID
 	return &bidRequest
 }
 
diff --git a/adapters/stroeerCore/stroeercore.go b/adapters/stroeerCore/stroeercore.go
new file mode 100644
index 00000000..2f9f39ec
--- /dev/null
+++ b/adapters/stroeerCore/stroeercore.go
@@ -0,0 +1,114 @@
+package stroeerCore
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"net/http"
+)
+
+type adapter struct {
+	URL string `json:"url"`
+}
+
+type response struct {
+	Bids []bidResponse `json:"bids"`
+}
+
+type bidResponse struct {
+	ID     string  `json:"id"`
+	BidID  string  `json:"bidId"`
+	CPM    float64 `json:"cpm"`
+	Width  int64   `json:"width"`
+	Height int64   `json:"height"`
+	Ad     string  `json:"ad"`
+	CrID   string  `json:"crid"`
+}
+
+func (a *adapter) MakeBids(bidRequest *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if responseData.StatusCode != http.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected http status code: %d.", responseData.StatusCode),
+		}}
+	}
+
+	var errors []error
+	stroeerResponse := response{}
+
+	if err := json.Unmarshal(responseData.Body, &stroeerResponse); err != nil {
+		errors = append(errors, err)
+		return nil, errors
+	}
+
+	bidderResponse := adapters.NewBidderResponseWithBidsCapacity(len(stroeerResponse.Bids))
+	bidderResponse.Currency = "EUR"
+
+	for _, bid := range stroeerResponse.Bids {
+		openRtbBid := openrtb2.Bid{
+			ID:    bid.ID,
+			ImpID: bid.BidID,
+			W:     bid.Width,
+			H:     bid.Height,
+			Price: bid.CPM,
+			AdM:   bid.Ad,
+			CrID:  bid.CrID,
+		}
+
+		bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
+			Bid:     &openRtbBid,
+			BidType: openrtb_ext.BidTypeBanner,
+		})
+	}
+
+	return bidderResponse, errors
+}
+
+func (a *adapter) MakeRequests(bidRequest *openrtb2.BidRequest, extraRequestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var errors []error
+
+	for idx := range bidRequest.Imp {
+		imp := &bidRequest.Imp[idx]
+		var bidderExt adapters.ExtImpBidder
+		if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+			errors = append(errors, err)
+			continue
+		}
+
+		var stroeerExt openrtb_ext.ExtImpStroeerCore
+		if err := json.Unmarshal(bidderExt.Bidder, &stroeerExt); err != nil {
+			errors = append(errors, err)
+			continue
+		}
+
+		imp.TagID = stroeerExt.Sid
+	}
+
+	reqJSON, err := json.Marshal(bidRequest)
+	if err != nil {
+		errors = append(errors, err)
+		return nil, errors
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+
+	return []*adapters.RequestData{{
+		Method:  "POST",
+		Uri:     a.URL,
+		Body:    reqJSON,
+		Headers: headers,
+	}}, errors
+}
+
+// Builder builds a new instance of the StroeerCore adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	bidder := &adapter{
+		URL: config.Endpoint,
+	}
+	return bidder, nil
+}
diff --git a/adapters/trafficgate/trafficgate.go b/adapters/trafficgate/trafficgate.go
new file mode 100644
index 00000000..30ec08b0
--- /dev/null
+++ b/adapters/trafficgate/trafficgate.go
@@ -0,0 +1,181 @@
+package trafficgate
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"text/template"
+
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	EndpointTemplate *template.Template
+}
+
+type BidResponseExt struct {
+	Prebid struct {
+		Type string
+	}
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+
+	headers := http.Header{
+		"Content-Type": {"application/json"},
+		"Accept":       {"application/json"},
+	}
+
+	// Pull the host and source ID info from the bidder params.
+	reqImps, err := splitImpressions(request.Imp)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	requests := []*adapters.RequestData{}
+
+	var errs []error
+	for reqExt, reqImp := range reqImps {
+		request.Imp = reqImp
+		reqJson, err := json.Marshal(request)
+
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		urlParams := macros.EndpointTemplateParams{Host: reqExt.Host}
+		url, err := macros.ResolveMacros(a.EndpointTemplate, urlParams)
+
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		request := adapters.RequestData{
+			Method:  "POST",
+			Uri:     url,
+			Body:    reqJson,
+			Headers: headers}
+
+		requests = append(requests, &request)
+	}
+
+	return requests, errs
+}
+
+/*
+internal original request in OpenRTB, external = result of us having converted it (what comes out of MakeRequests)
+*/
+func (a *adapter) MakeBids(
+	internalRequest *openrtb2.BidRequest,
+	externalRequest *adapters.RequestData,
+	response *adapters.ResponseData,
+) (*adapters.BidderResponse, []error) {
+
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if response.StatusCode != http.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Error response with status %d", response.StatusCode),
+		}}
+	}
+
+	var bidResp openrtb2.BidResponse
+
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: err.Error(),
+		}}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(internalRequest.Imp))
+	bidResponse.Currency = bidResp.Cur
+
+	for _, seatBid := range bidResp.SeatBid {
+		for i := range seatBid.Bid {
+			var bidExt BidResponseExt
+			if err := json.Unmarshal(seatBid.Bid[i].Ext, &bidExt); err != nil {
+				return nil, []error{&errortypes.BadServerResponse{
+					Message: fmt.Sprintf("Missing response ext"),
+				}}
+			}
+			if len(bidExt.Prebid.Type) < 1 {
+				return nil, []error{&errortypes.BadServerResponse{
+					Message: fmt.Sprintf("Unable to read bid.ext.prebid.type"),
+				}}
+			}
+			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: getMediaTypeForImp(bidExt.Prebid.Type),
+			})
+		}
+	}
+
+	return bidResponse, nil
+}
+
+func splitImpressions(imps []openrtb2.Imp) (map[openrtb_ext.ExtImpTrafficGate][]openrtb2.Imp, error) {
+
+	var multipleImps = make(map[openrtb_ext.ExtImpTrafficGate][]openrtb2.Imp)
+
+	for _, imp := range imps {
+		bidderParams, err := getBidderParams(&imp)
+		if err != nil {
+			return nil, err
+		}
+
+		multipleImps[*bidderParams] = append(multipleImps[*bidderParams], imp)
+	}
+
+	return multipleImps, nil
+}
+
+func getBidderParams(imp *openrtb2.Imp) (*openrtb_ext.ExtImpTrafficGate, error) {
+	var bidderExt adapters.ExtImpBidder
+	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: fmt.Sprintf("Missing bidder ext"),
+		}
+	}
+	var TrafficGateExt openrtb_ext.ExtImpTrafficGate
+	if err := json.Unmarshal(bidderExt.Bidder, &TrafficGateExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: fmt.Sprintf("Bidder parameters required"),
+		}
+	}
+
+	return &TrafficGateExt, nil
+}
+
+func getMediaTypeForImp(bidType string) openrtb_ext.BidType {
+	switch bidType {
+	case "video":
+		return openrtb_ext.BidTypeVideo
+	case "native":
+		return openrtb_ext.BidTypeNative
+	case "audio":
+		return openrtb_ext.BidTypeAudio
+	}
+	return openrtb_ext.BidTypeBanner
+}
+
+// Builder builds a new instance of the TrafficGate adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
+	}
+
+	bidder := &adapter{
+		EndpointTemplate: template,
+	}
+	return bidder, nil
+}
diff --git a/config/accounts.go b/config/accounts.go
index e475da8a..14e52180 100644
--- a/config/accounts.go
+++ b/config/accounts.go
@@ -33,9 +33,9 @@ type Account struct {
 
 // CookieSync represents the account-level defaults for the cookie sync endpoint.
 type CookieSync struct {
-	DefaultLimit    int  `mapstructure:"default_limit" json:"default_limit"`
-	MaxLimit        int  `mapstructure:"max_limit" json:"max_limit"`
-	DefaultCoopSync bool `mapstructure:"default_coop_sync" json:"default_coop_sync"`
+	DefaultLimit    *int  `mapstructure:"default_limit" json:"default_limit"`
+	MaxLimit        *int  `mapstructure:"max_limit" json:"max_limit"`
+	DefaultCoopSync *bool `mapstructure:"default_coop_sync" json:"default_coop_sync"`
 }
 
 // AccountCCPA represents account-specific CCPA configuration
diff --git a/config/config.go b/config/config.go
index a8bf31c0..2f1515d4 100644
--- a/config/config.go
+++ b/config/config.go
@@ -487,6 +487,9 @@ type DisabledMetrics struct {
 	// True if we want to stop collecting account-to-adapter metrics
 	AccountAdapterDetails bool `mapstructure:"account_adapter_details"`
 
+	// True if we want to stop collecting account debug request metrics
+	AccountDebug bool `mapstructure:"account_debug"`
+
 	// True if we don't want to collect metrics about the connections prebid
 	// server establishes with bidder servers such as the number of connections
 	// that were created or reused.
@@ -780,6 +783,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("http_client_cache.idle_connection_timeout_seconds", 60)
 	// no metrics configured by default (metrics{host|database|username|password})
 	v.SetDefault("metrics.disabled_metrics.account_adapter_details", false)
+	v.SetDefault("metrics.disabled_metrics.account_debug", true)
 	v.SetDefault("metrics.disabled_metrics.adapter_connections_metrics", true)
 	v.SetDefault("metrics.disabled_metrics.adapter_gdpr_request_blocked", false)
 	v.SetDefault("metrics.influxdb.host", "")
@@ -932,6 +936,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.appnexus.platform_id", "5")
 	v.SetDefault("adapters.audiencenetwork.disabled", true)
 	v.SetDefault("adapters.audiencenetwork.endpoint", "https://an.facebook.com/placementbid.ortb")
+	v.SetDefault("adapters.automatad.endpoint", "https://s2s.atmtd.com")
 	v.SetDefault("adapters.avocet.disabled", true)
 	v.SetDefault("adapters.axonix.endpoint", "https://openrtb-us-east-1.axonix.com/supply/prebid-server/{{.AccountID}}")
 	v.SetDefault("adapters.beachfront.endpoint", "https://display.bfmio.com/prebid_display")
@@ -942,6 +947,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.bidmyadz.endpoint", "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc")
 	v.SetDefault("adapters.bidscube.endpoint", "http://supply.bidscube.com/?c=o&m=rtb")
 	v.SetDefault("adapters.bizzclick.endpoint", "http://us-e-node1.bizzclick.com/bid?rtb_seat_id={{.SourceId}}&secret_key={{.AccountID}}")
+	v.SetDefault("adapters.bliink.endpoint", "http://engine.bliink.io/bid")
 	v.SetDefault("adapters.bmtm.endpoint", "https://one.elitebidder.com/api/pbs")
 	v.SetDefault("adapters.brightroll.endpoint", "http://east-bid.ybp.yahoo.com/bid/appnexuspbs")
 	v.SetDefault("adapters.coinzilla.endpoint", "http://request-global.czilladx.com/serve/prebid-server.php")
@@ -1028,9 +1034,12 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.sonobi.endpoint", "https://apex.go.sonobi.com/prebid?partnerid=71d9d3d8af")
 	v.SetDefault("adapters.sovrn.endpoint", "http://ap.lijit.com/rtb/bid?src=prebid_server")
 	v.SetDefault("adapters.streamkey.endpoint", "http://ghb.hb.streamkey.net/pbs/ortb")
+	v.SetDefault("adapters.stroeercore.disabled", true)
+	v.SetDefault("adapters.stroeercore.endpoint", "http://mhb.adscale.de/s2sdsh")
 	v.SetDefault("adapters.synacormedia.endpoint", "http://{{.Host}}.technoratimedia.com/openrtb/bids/{{.Host}}")
 	v.SetDefault("adapters.tappx.endpoint", "http://{{.Host}}")
 	v.SetDefault("adapters.telaria.endpoint", "https://ads.tremorhub.com/ad/rtb/prebid")
+	v.SetDefault("adapters.trafficgate.endpoint", "http://{{.Host}}.bc-plugin.com/?c=o&m=rtb")
 	v.SetDefault("adapters.triplelift_native.disabled", true)
 	v.SetDefault("adapters.triplelift_native.extra_info", "{\"publisher_whitelist\":[]}")
 	v.SetDefault("adapters.triplelift.endpoint", "https://tlx.3lift.com/s2s/auction?sra=1&supplier_id=20")
diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index 7cb1d2d7..da20457b 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -113,6 +113,14 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr
 		return usersync.Request{}, privacy.Policies{}, fmt.Errorf("JSON parsing failed: %s", err.Error())
 	}
 
+	if request.Account == "" {
+		request.Account = metrics.PublisherUnknown
+	}
+	account, fetchErrs := accountService.GetAccount(context.Background(), c.config, c.accountsFetcher, request.Account)
+	if len(fetchErrs) > 0 {
+		return usersync.Request{}, privacy.Policies{}, combineErrors(fetchErrs)
+	}
+
 	var gdprString string
 	if request.GDPR != nil {
 		gdprString = strconv.Itoa(*request.GDPR)
@@ -132,10 +140,8 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr
 		}
 	}
 
-	request, err = c.setLimit(request)
-	if err != nil {
-		return usersync.Request{}, privacy.Policies{}, err
-	}
+	request = c.setLimit(request, account.CookieSync)
+	request = c.setCooperativeSync(request, account.CookieSync)
 
 	privacyPolicies := privacy.Policies{
 		GDPR: gdprPrivacy.Policy{
@@ -192,26 +198,26 @@ func (c *cookieSyncEndpoint) writeParseRequestErrorMetrics(err error) {
 	}
 }
 
-func (c *cookieSyncEndpoint) setLimit(request cookieSyncRequest) (cookieSyncRequest, error) {
-	if request.Account != "" {
-		accountInfo, errs := accountService.GetAccount(context.Background(), c.config, c.accountsFetcher, request.Account)
-		if len(errs) > 0 {
-			return request, combineErrors(errs)
-		}
-		if request.Limit <= 0 {
-			request.Limit = accountInfo.CookieSync.DefaultLimit
-		}
-		if request.Limit <= 0 || request.Limit > accountInfo.CookieSync.MaxLimit {
-			request.Limit = accountInfo.CookieSync.MaxLimit
-		}
-		if request.Limit < 0 {
-			request.Limit = 0
-		}
-		if request.CooperativeSync == nil {
-			request.CooperativeSync = &accountInfo.CookieSync.DefaultCoopSync
-		}
+func (c *cookieSyncEndpoint) setLimit(request cookieSyncRequest, cookieSyncConfig config.CookieSync) cookieSyncRequest {
+	if request.Limit <= 0 && cookieSyncConfig.DefaultLimit != nil {
+		request.Limit = *cookieSyncConfig.DefaultLimit
+	}
+	if cookieSyncConfig.MaxLimit != nil && (request.Limit <= 0 || request.Limit > *cookieSyncConfig.MaxLimit) {
+		request.Limit = *cookieSyncConfig.MaxLimit
 	}
-	return request, nil
+	if request.Limit < 0 {
+		request.Limit = 0
+	}
+
+	return request
+}
+
+func (c *cookieSyncEndpoint) setCooperativeSync(request cookieSyncRequest, cookieSyncConfig config.CookieSync) cookieSyncRequest {
+	if request.CooperativeSync == nil && cookieSyncConfig.DefaultCoopSync != nil {
+		request.CooperativeSync = cookieSyncConfig.DefaultCoopSync
+	}
+
+	return request
 }
 
 func parseTypeFilter(request *cookieSyncRequestFilterSettings) (usersync.SyncTypeFilter, error) {
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 1d5f8a5e..a22d1200 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -17,7 +17,6 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/exchange"
-	aggregatedGDPR "github.com/prebid/prebid-server/gdpr"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/privacy"
@@ -202,10 +201,9 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 		StartTime:                  start,
 		LegacyLabels:               labels,
 		GlobalPrivacyControlHeader: secGPC,
-		TCF2ConfigBuilder:          aggregatedGDPR.NewTCF2Config,
-		GDPRPermissionsBuilder:     aggregatedGDPR.NewPermissions,
 		StoredAuctionResponses:     storedAuctionResponses,
 		StoredBidResponses:         storedBidResponses,
+		PubID:                      labels.PubID,
 	}
 
 	response, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 05a020a8..ffbd358a 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -5,7 +5,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/prebid/prebid-server/gdpr"
 	"io"
 	"io/ioutil"
 	"net/http"
@@ -212,10 +211,8 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 		ImpExtInfoMap:              impExtInfoMap,
 		StoredAuctionResponses:     storedAuctionResponses,
 		StoredBidResponses:         storedBidResponses,
-		TCF2ConfigBuilder:          gdpr.NewTCF2Config,
-		GDPRPermissionsBuilder:     gdpr.NewPermissions,
+		PubID:                      labels.PubID,
 	}
-
 	response, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
 	ao.Request = req.BidRequest
 	ao.Response = response
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index ec5b1c57..a7cf02cb 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -26,7 +26,6 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/exchange"
-	"github.com/prebid/prebid-server/gdpr"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/prebid_cache_client"
@@ -294,9 +293,8 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		RequestType:                labels.RType,
 		StartTime:                  start,
 		LegacyLabels:               labels,
-		TCF2ConfigBuilder:          gdpr.NewTCF2Config,
-		GDPRPermissionsBuilder:     gdpr.NewPermissions,
 		GlobalPrivacyControlHeader: secGPC,
+		PubID:                      labels.PubID,
 	}
 
 	response, err := deps.ex.HoldAuction(ctx, auctionRequest, &debugLog)
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 24a2f0b8..5db9c884 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -32,6 +32,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/applogy"
 	"github.com/prebid/prebid-server/adapters/appnexus"
 	"github.com/prebid/prebid-server/adapters/audienceNetwork"
+	"github.com/prebid/prebid-server/adapters/automatad"
 	"github.com/prebid/prebid-server/adapters/avocet"
 	"github.com/prebid/prebid-server/adapters/axonix"
 	"github.com/prebid/prebid-server/adapters/beachfront"
@@ -41,6 +42,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/bidmyadz"
 	"github.com/prebid/prebid-server/adapters/bidscube"
 	"github.com/prebid/prebid-server/adapters/bizzclick"
+	"github.com/prebid/prebid-server/adapters/bliink"
 	"github.com/prebid/prebid-server/adapters/bmtm"
 	"github.com/prebid/prebid-server/adapters/brightroll"
 	"github.com/prebid/prebid-server/adapters/coinzilla"
@@ -116,9 +118,11 @@ import (
 	"github.com/prebid/prebid-server/adapters/smilewanted"
 	"github.com/prebid/prebid-server/adapters/sonobi"
 	"github.com/prebid/prebid-server/adapters/sovrn"
+	"github.com/prebid/prebid-server/adapters/stroeerCore"
 	"github.com/prebid/prebid-server/adapters/synacormedia"
 	"github.com/prebid/prebid-server/adapters/tappx"
 	"github.com/prebid/prebid-server/adapters/telaria"
+	"github.com/prebid/prebid-server/adapters/trafficgate"
 	"github.com/prebid/prebid-server/adapters/triplelift"
 	"github.com/prebid/prebid-server/adapters/triplelift_native"
 	"github.com/prebid/prebid-server/adapters/ucfunnel"
@@ -173,6 +177,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderApplogy:           applogy.Builder,
 		openrtb_ext.BidderAppnexus:          appnexus.Builder,
 		openrtb_ext.BidderAudienceNetwork:   audienceNetwork.Builder,
+		openrtb_ext.BidderAutomatad:         automatad.Builder,
 		openrtb_ext.BidderAvocet:            avocet.Builder,
 		openrtb_ext.BidderAxonix:            axonix.Builder,
 		openrtb_ext.BidderBeachfront:        beachfront.Builder,
@@ -182,6 +187,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderBidmyadz:          bidmyadz.Builder,
 		openrtb_ext.BidderBidsCube:          bidscube.Builder,
 		openrtb_ext.BidderBizzclick:         bizzclick.Builder,
+		openrtb_ext.BidderBliink:            bliink.Builder,
 		openrtb_ext.BidderBmtm:              bmtm.Builder,
 		openrtb_ext.BidderBrightroll:        brightroll.Builder,
 		openrtb_ext.BidderCoinzilla:         coinzilla.Builder,
@@ -262,10 +268,12 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderSmileWanted:       smilewanted.Builder,
 		openrtb_ext.BidderSonobi:            sonobi.Builder,
 		openrtb_ext.BidderSovrn:             sovrn.Builder,
+		openrtb_ext.BidderStroeerCore:       stroeerCore.Builder,
 		openrtb_ext.BidderStreamkey:         adtelligent.Builder,
 		openrtb_ext.BidderSynacormedia:      synacormedia.Builder,
 		openrtb_ext.BidderTappx:             tappx.Builder,
 		openrtb_ext.BidderTelaria:           telaria.Builder,
+		openrtb_ext.BidderTrafficGate:       trafficgate.Builder,
 		openrtb_ext.BidderTriplelift:        triplelift.Builder,
 		openrtb_ext.BidderTripleliftNative:  triplelift_native.Builder,
 		openrtb_ext.BidderTrustX:            grid.Builder,
diff --git a/exchange/exchange.go b/exchange/exchange.go
index d2362107..edbad99d 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -57,14 +57,14 @@ type exchange struct {
 	me                metrics.MetricsEngine
 	cache             prebid_cache_client.Client
 	cacheTime         time.Duration
-	vendorListFetcher gdpr.VendorListFetcher
+	gdprPermsBuilder  gdpr.PermissionsBuilder
+	tcf2ConfigBuilder gdpr.TCF2ConfigBuilder
 	currencyConverter *currency.RateConverter
 	externalURL       string
 	gdprDefaultValue  gdpr.Signal
 	privacyConfig     config.Privacy
 	categoriesFetcher stored_requests.CategoryFetcher
 	bidIDGenerator    BidIDGenerator
-	gvlVendorIDs      map[openrtb_ext.BidderName]uint16
 }
 
 // Container to pass out response ext data from the GetAllBids goroutines back into the main thread
@@ -111,7 +111,7 @@ func (randomDeduplicateBidBooleanGenerator) Generate() bool {
 	return rand.Intn(100) < 50
 }
 
-func NewExchange(adapters map[openrtb_ext.BidderName]AdaptedBidder, cache prebid_cache_client.Client, cfg *config.Configuration, syncersByBidder map[string]usersync.Syncer, metricsEngine metrics.MetricsEngine, infos config.BidderInfos, vendorListFetcher gdpr.VendorListFetcher, currencyConverter *currency.RateConverter, categoriesFetcher stored_requests.CategoryFetcher) Exchange {
+func NewExchange(adapters map[openrtb_ext.BidderName]AdaptedBidder, cache prebid_cache_client.Client, cfg *config.Configuration, syncersByBidder map[string]usersync.Syncer, metricsEngine metrics.MetricsEngine, infos config.BidderInfos, gdprPermsBuilder gdpr.PermissionsBuilder, tcf2CfgBuilder gdpr.TCF2ConfigBuilder, currencyConverter *currency.RateConverter, categoriesFetcher stored_requests.CategoryFetcher) Exchange {
 	bidderToSyncerKey := map[string]string{}
 	for bidder, syncer := range syncersByBidder {
 		bidderToSyncerKey[bidder] = syncer.Key()
@@ -131,7 +131,8 @@ func NewExchange(adapters map[openrtb_ext.BidderName]AdaptedBidder, cache prebid
 		categoriesFetcher: categoriesFetcher,
 		currencyConverter: currencyConverter,
 		externalURL:       cfg.ExternalURL,
-		vendorListFetcher: vendorListFetcher,
+		gdprPermsBuilder:  gdprPermsBuilder,
+		tcf2ConfigBuilder: tcf2CfgBuilder,
 		me:                metricsEngine,
 		gdprDefaultValue:  gdprDefaultValue,
 		privacyConfig: config.Privacy{
@@ -140,7 +141,6 @@ func NewExchange(adapters map[openrtb_ext.BidderName]AdaptedBidder, cache prebid
 			LMT:  cfg.LMT,
 		},
 		bidIDGenerator: &bidIDGenerator{cfg.GenerateBidID},
-		gvlVendorIDs:   infos.ToGVLVendorIDMap(),
 	}
 }
 
@@ -168,10 +168,9 @@ type AuctionRequest struct {
 	FirstPartyData map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData
 	// map of imp id to stored response
 	StoredAuctionResponses stored_responses.ImpsWithBidResponses
-	TCF2ConfigBuilder      gdpr.TCF2ConfigBuilder
-	GDPRPermissionsBuilder gdpr.PermissionsBuilder
 	// map of imp id to bidder to stored response
 	StoredBidResponses stored_responses.ImpBidderStoredResp
+	PubID              string
 }
 
 // BidderRequest holds the bidder specific request and all other
@@ -209,6 +208,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 		}
 		r.ResolvedBidRequest = resolvedBidReq
 	}
+	e.me.RecordDebugRequest(responseDebugAllow || accountDebugAllow, r.PubID)
 
 	if r.RequestType == metrics.ReqTypeORTB2Web || r.RequestType == metrics.ReqTypeORTB2App {
 		//Extract First party data for auction endpoint only
@@ -244,9 +244,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 	gdprDefaultValue := e.parseGDPRDefaultValue(r.BidRequestWrapper.BidRequest)
 
 	// Slice of BidRequests, each a copy of the original cleaned to only contain bidder data for the named bidder
-	tcf2Cfg := r.TCF2ConfigBuilder(e.privacyConfig.GDPR.TCF2, r.Account.GDPR)
-	gdprPerms := r.GDPRPermissionsBuilder(e.privacyConfig.GDPR, tcf2Cfg, e.gvlVendorIDs, e.vendorListFetcher)
-	bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(ctx, r, requestExt, e.bidderToSyncerKey, e.me, gdprDefaultValue, gdprPerms, e.privacyConfig, tcf2Cfg)
+	bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(ctx, r, requestExt, e.bidderToSyncerKey, e.me, gdprDefaultValue, e.privacyConfig, e.gdprPermsBuilder, e.tcf2ConfigBuilder)
 
 	e.me.RecordRequestPrivacy(privacyLabels)
 
diff --git a/exchange/utils.go b/exchange/utils.go
index 9f68a6b6..103a7f7e 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -4,9 +4,10 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"github.com/prebid/prebid-server/stored_responses"
 	"math/rand"
 
+	"github.com/prebid/prebid-server/stored_responses"
+
 	"github.com/buger/jsonparser"
 	"github.com/mxmCherry/openrtb/v15/openrtb2"
 	"github.com/prebid/go-gdpr/vendorconsent"
@@ -42,9 +43,9 @@ func cleanOpenRTBRequests(ctx context.Context,
 	bidderToSyncerKey map[string]string,
 	metricsEngine metrics.MetricsEngine,
 	gdprDefaultValue gdpr.Signal,
-	gdprPerms gdpr.Permissions,
 	privacyConfig config.Privacy,
-	tcf2Cfg gdpr.TCF2ConfigReader,
+	gdprPermsBuilder gdpr.PermissionsBuilder,
+	tcf2ConfigBuilder gdpr.TCF2ConfigBuilder,
 ) (allowedBidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) {
 
 	req := auctionReq.BidRequestWrapper
@@ -103,7 +104,11 @@ func cleanOpenRTBRequests(ctx context.Context,
 	privacyLabels.COPPAEnforced = privacyEnforcement.COPPA
 	privacyLabels.LMTEnforced = lmtEnforcer.ShouldEnforce(unknownBidder)
 
+	tcf2Cfg := tcf2ConfigBuilder(privacyConfig.GDPR.TCF2, auctionReq.Account.GDPR)
+
 	var gdprEnforced bool
+	var gdprPerms gdpr.Permissions = &gdpr.AlwaysAllow{}
+
 	if gdprApplies {
 		gdprEnforced = tcf2Cfg.IntegrationEnabled(integrationTypeMap[auctionReq.LegacyLabels.RType])
 	}
@@ -115,6 +120,7 @@ func cleanOpenRTBRequests(ctx context.Context,
 			version := int(parsedConsent.Version())
 			privacyLabels.GDPRTCFVersion = metrics.TCFVersionToValue(version)
 		}
+		gdprPerms = gdprPermsBuilder(tcf2Cfg)
 	}
 
 	// bidder level privacy policies
diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go
index 94ef864f..a1ad10b8 100644
--- a/gdpr/gdpr.go
+++ b/gdpr/gdpr.go
@@ -24,9 +24,17 @@ type Permissions interface {
 	AuctionActivitiesAllowed(ctx context.Context, bidderCoreName openrtb_ext.BidderName, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, aliasGVLIDs map[string]uint16) (permissions AuctionPermissions, err error)
 }
 
-type PermissionsBuilder func(config.GDPR, TCF2ConfigReader, map[openrtb_ext.BidderName]uint16, VendorListFetcher) Permissions
+type PermissionsBuilder func(TCF2ConfigReader) Permissions
 
-// NewPermissions gets an instance of the Permissions for use elsewhere in the project.
+// NewPermissionsBuilder is of type PermissionsBuilderMaker
+// It takes host config data used to configure the builder function it returns
+func NewPermissionsBuilder(cfg config.GDPR, gvlVendorIDs map[openrtb_ext.BidderName]uint16, vendorListFetcher VendorListFetcher) PermissionsBuilder {
+	return func(tcf2Cfg TCF2ConfigReader) Permissions {
+		return NewPermissions(cfg, tcf2Cfg, gvlVendorIDs, vendorListFetcher)
+	}
+}
+
+// NewPermissions gets a per-request Permissions object that can then be used to check GDPR permissions for a given bidder.
 func NewPermissions(cfg config.GDPR, tcf2Config TCF2ConfigReader, vendorIDs map[openrtb_ext.BidderName]uint16, fetcher VendorListFetcher) Permissions {
 	if !cfg.Enabled {
 		return &AlwaysAllow{}
diff --git a/go.mod b/go.mod
index fea217fd..fc1555f1 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
 	github.com/buger/jsonparser v1.1.1
 	github.com/chasex/glog v0.0.0-20160217080310-c62392af379c
-	github.com/coocood/freecache v1.2.0
+	github.com/coocood/freecache v1.2.1
 	github.com/docker/go-units v0.4.0
 	github.com/gofrs/uuid v4.2.0+incompatible
 	github.com/golang/glog v1.0.0
@@ -22,15 +22,15 @@ require (
 	github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
 	github.com/rs/cors v1.8.2
 	github.com/sergi/go-diff v1.2.0 // indirect
-	github.com/spf13/viper v1.11.0
+	github.com/spf13/viper v1.12.0
 	github.com/stretchr/testify v1.7.1
 	github.com/vrischmann/go-metrics-influxdb v0.1.1
 	github.com/xeipuuv/gojsonschema v1.2.0
 	github.com/yudai/gojsondiff v1.0.0
 	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
 	github.com/yudai/pp v2.0.1+incompatible // indirect
-	golang.org/x/net v0.0.0-20220420153159-1850ba15e1be
+	golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2
 	golang.org/x/text v0.3.7
 	gopkg.in/evanphx/json-patch.v4 v4.12.0
-	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
+	gopkg.in/yaml.v3 v3.0.1
 )
diff --git a/go.sum b/go.sum
index 368f9f50..d3c37578 100644
--- a/go.sum
+++ b/go.sum
@@ -38,6 +38,8 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7
 cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
 cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
 cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
+cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
+cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
 cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
@@ -59,7 +61,6 @@ github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
 github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
 github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
-github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -102,16 +103,19 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP
 github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/coocood/freecache v1.2.0 h1:p8RhjN6Y4DRBIMzdRlm1y+M7h7YJxye3lGW8/VvzCz0=
-github.com/coocood/freecache v1.2.0/go.mod h1:OKrEjkGVoxZhyWAJoeFi5BMLUJm2Tit0kpGkIr7NGYY=
+github.com/coocood/freecache v1.2.1 h1:/v1CqMq45NFH9mp/Pt142reundeBM0dVUD3osQBeu/U=
+github.com/coocood/freecache v1.2.1/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
 github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -120,14 +124,17 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
 github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
+github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
 github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
+github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
-github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
+github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
+github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -194,8 +201,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -224,7 +232,9 @@ github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pf
 github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
 github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
 github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
+github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
 github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
@@ -278,11 +288,13 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
 github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
 github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
@@ -311,8 +323,9 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
 github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
 github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
 github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -337,10 +350,10 @@ github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug=
 github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
-github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-github.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDSb3jwDggjz3Z0=
-github.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
+github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
+github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
+github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -357,6 +370,7 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
 github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
 github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
 github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
 github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
 github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -382,28 +396,29 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X
 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
 github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA=
+github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
 github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
 github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
-github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
-github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
+github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
 github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44=
-github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk=
+github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
+github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -415,8 +430,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
-github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
+github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
 github.com/vrischmann/go-metrics-influxdb v0.1.1 h1:xneKFRjsS4BiVYvAKaM/rOlXYd1pGHksnES0ECCJLgo=
 github.com/vrischmann/go-metrics-influxdb v0.1.1/go.mod h1:q7YC8bFETCYopXRMtUvQQdLaoVhpsEwvQS2zZEYCqg8=
@@ -437,9 +452,10 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-go.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
-go.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
-go.etcd.io/etcd/client/v2 v2.305.2/go.mod h1:2D7ZejHVMIfog1221iLSYlQRzrtECw3kz4I4VAQm3qI=
+go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
+go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU=
+go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -545,8 +561,9 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220420153159-1850ba15e1be h1:yx80W7nvY5ySWpaU8UWaj5o9e23YgO9BRhQol7Lc+JI=
-golang.org/x/net v0.0.0-20220420153159-1850ba15e1be/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y=
+golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -578,6 +595,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -659,8 +677,10 @@ golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -736,8 +756,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
 golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
 google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -774,6 +794,9 @@ google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQ
 google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
 google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
 google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
+google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
+google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
+google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -853,6 +876,12 @@ google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2
 google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
 google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
 google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
+google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -881,6 +910,8 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
 google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
+google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -920,8 +951,10 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -932,3 +965,4 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/metrics/config/metrics.go b/metrics/config/metrics.go
index d8df48fa..c94557a1 100644
--- a/metrics/config/metrics.go
+++ b/metrics/config/metrics.go
@@ -254,6 +254,13 @@ func (me *MultiMetricsEngine) RecordAdapterGDPRRequestBlocked(adapter openrtb_ex
 	}
 }
 
+// RecordDebugRequest across all engines
+func (me *MultiMetricsEngine) RecordDebugRequest(debugEnabled bool, pubId string) {
+	for _, thisME := range *me {
+		thisME.RecordDebugRequest(debugEnabled, pubId)
+	}
+}
+
 // NilMetricsEngine implements the MetricsEngine interface where no metrics are actually captured. This is
 // used if no metric backend is configured and also for tests.
 type NilMetricsEngine struct{}
@@ -365,3 +372,7 @@ func (me *NilMetricsEngine) RecordRequestPrivacy(privacy metrics.PrivacyLabels)
 // RecordAdapterGDPRRequestBlocked as a noop
 func (me *NilMetricsEngine) RecordAdapterGDPRRequestBlocked(adapter openrtb_ext.BidderName) {
 }
+
+// RecordDebugRequest as a noop
+func (me *NilMetricsEngine) RecordDebugRequest(debugEnabled bool, pubId string) {
+}
diff --git a/metrics/go_metrics.go b/metrics/go_metrics.go
index c93f1060..30752321 100644
--- a/metrics/go_metrics.go
+++ b/metrics/go_metrics.go
@@ -20,6 +20,7 @@ type Metrics struct {
 	ImpMeter                       metrics.Meter
 	AppRequestMeter                metrics.Meter
 	NoCookieMeter                  metrics.Meter
+	DebugRequestMeter              metrics.Meter
 	RequestTimer                   metrics.Timer
 	RequestsQueueTimer             map[RequestType]map[bool]metrics.Timer
 	PrebidCacheRequestTimerSuccess metrics.Timer
@@ -94,6 +95,7 @@ type MarkupDeliveryMetrics struct {
 
 type accountMetrics struct {
 	requestMeter      metrics.Meter
+	debugRequestMeter metrics.Meter
 	bidsReceivedMeter metrics.Meter
 	priceHistogram    metrics.Histogram
 	// store account by adapter metrics. Type is map[PBSBidder.BidderCode]
@@ -119,6 +121,7 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa
 		ConnectionCloseErrorMeter:      blankMeter,
 		ImpMeter:                       blankMeter,
 		AppRequestMeter:                blankMeter,
+		DebugRequestMeter:              blankMeter,
 		NoCookieMeter:                  blankMeter,
 		RequestTimer:                   blankTimer,
 		DNSLookupTimer:                 blankTimer,
@@ -219,6 +222,7 @@ func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, d
 
 	newMetrics.NoCookieMeter = metrics.GetOrRegisterMeter("no_cookie_requests", registry)
 	newMetrics.AppRequestMeter = metrics.GetOrRegisterMeter("app_requests", registry)
+	newMetrics.DebugRequestMeter = metrics.GetOrRegisterMeter("debug_requests", registry)
 	newMetrics.RequestTimer = metrics.GetOrRegisterTimer("request_time", registry)
 	newMetrics.DNSLookupTimer = metrics.GetOrRegisterTimer("dns_lookup_time", registry)
 	newMetrics.TLSHandshakeTimer = metrics.GetOrRegisterTimer("tls_handshake_time", registry)
@@ -394,6 +398,7 @@ func (me *Metrics) getAccountMetrics(id string) *accountMetrics {
 	}
 	am = &accountMetrics{}
 	am.requestMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.requests", id), me.MetricsRegistry)
+	am.debugRequestMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.debug_requests", id), me.MetricsRegistry)
 	am.bidsReceivedMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.bids_received", id), me.MetricsRegistry)
 	am.priceHistogram = metrics.GetOrRegisterHistogram(fmt.Sprintf("account.%s.prices", id), me.MetricsRegistry, metrics.NewExpDecaySample(1028, 0.015))
 	am.adapterMetrics = make(map[openrtb_ext.BidderName]*AdapterMetrics, len(me.exchanges))
@@ -430,6 +435,18 @@ func (me *Metrics) RecordRequest(labels Labels) {
 	am.requestMeter.Mark(1)
 }
 
+func (me *Metrics) RecordDebugRequest(debugEnabled bool, pubID string) {
+	if debugEnabled {
+		me.DebugRequestMeter.Mark(1)
+		if pubID != PublisherUnknown {
+			am := me.getAccountMetrics(pubID)
+			if !me.MetricsDisabled.AccountDebug {
+				am.debugRequestMeter.Mark(1)
+			}
+		}
+	}
+}
+
 func (me *Metrics) RecordImps(labels ImpLabels) {
 	me.ImpMeter.Mark(int64(1))
 	if labels.BannerImps {
diff --git a/metrics/metrics.go b/metrics/metrics.go
index b70907ea..8394f419 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -409,4 +409,5 @@ type MetricsEngine interface {
 	RecordTimeoutNotice(success bool)
 	RecordRequestPrivacy(privacy PrivacyLabels)
 	RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.BidderName)
+	RecordDebugRequest(debugEnabled bool, pubId string)
 }
diff --git a/metrics/metrics_mock.go b/metrics/metrics_mock.go
index c8d5311c..84f75491 100644
--- a/metrics/metrics_mock.go
+++ b/metrics/metrics_mock.go
@@ -145,3 +145,8 @@ func (me *MetricsEngineMock) RecordRequestPrivacy(privacy PrivacyLabels) {
 func (me *MetricsEngineMock) RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.BidderName) {
 	me.Called(adapterName)
 }
+
+// RecordDebugRequest mock
+func (me *MetricsEngineMock) RecordDebugRequest(debugEnabled bool, pubId string) {
+	me.Called(debugEnabled, pubId)
+}
diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go
index 368424c5..87c4d1c2 100644
--- a/metrics/prometheus/prometheus.go
+++ b/metrics/prometheus/prometheus.go
@@ -27,6 +27,7 @@ type Metrics struct {
 	impressionsLegacy            prometheus.Counter
 	prebidCacheWriteTimer        *prometheus.HistogramVec
 	requests                     *prometheus.CounterVec
+	debugRequests                prometheus.Counter
 	requestsTimer                *prometheus.HistogramVec
 	requestsQueueTimer           *prometheus.HistogramVec
 	requestsWithoutCookie        *prometheus.CounterVec
@@ -68,7 +69,8 @@ type Metrics struct {
 	syncerSets     *prometheus.CounterVec
 
 	// Account Metrics
-	accountRequests *prometheus.CounterVec
+	accountRequests      *prometheus.CounterVec
+	accountDebugRequests *prometheus.CounterVec
 
 	metricsDisabled config.DisabledMetrics
 }
@@ -182,6 +184,10 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
 		"Count of total requests to Prebid Server labeled by type and status.",
 		[]string{requestTypeLabel, requestStatusLabel})
 
+	metrics.debugRequests = newCounterWithoutLabels(cfg, reg,
+		"debug_requests",
+		"Count of total requests to Prebid Server that have debug enabled")
+
 	metrics.requestsTimer = newHistogramVec(cfg, reg,
 		"request_time_seconds",
 		"Seconds to resolve successful Prebid Server requests labeled by type.",
@@ -370,6 +376,11 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
 		"Count of total requests to Prebid Server labeled by account.",
 		[]string{accountLabel})
 
+	metrics.accountDebugRequests = newCounter(cfg, reg,
+		"account_debug_requests",
+		"Count of total requests to Prebid Server that have debug enabled labled by account",
+		[]string{accountLabel})
+
 	metrics.requestsQueueTimer = newHistogramVec(cfg, reg,
 		"request_queue_time",
 		"Seconds request was waiting in queue",
@@ -477,6 +488,17 @@ func (m *Metrics) RecordRequest(labels metrics.Labels) {
 	}
 }
 
+func (m *Metrics) RecordDebugRequest(debugEnabled bool, pubID string) {
+	if debugEnabled {
+		m.debugRequests.Inc()
+		if !m.metricsDisabled.AccountDebug && pubID != metrics.PublisherUnknown {
+			m.accountDebugRequests.With(prometheus.Labels{
+				accountLabel: pubID,
+			}).Inc()
+		}
+	}
+}
+
 func (m *Metrics) RecordImps(labels metrics.ImpLabels) {
 	m.impressions.With(prometheus.Labels{
 		isBannerLabel: strconv.FormatBool(labels.BannerImps),
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index de33aa57..0b044da9 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -107,6 +107,7 @@ const (
 	BidderApplogy           BidderName = "applogy"
 	BidderAppnexus          BidderName = "appnexus"
 	BidderAudienceNetwork   BidderName = "audienceNetwork"
+	BidderAutomatad         BidderName = "automatad"
 	BidderAvocet            BidderName = "avocet"
 	BidderAxonix            BidderName = "axonix"
 	BidderBeachfront        BidderName = "beachfront"
@@ -116,6 +117,7 @@ const (
 	BidderBidmyadz          BidderName = "bidmyadz"
 	BidderBidsCube          BidderName = "bidscube"
 	BidderBizzclick         BidderName = "bizzclick"
+	BidderBliink            BidderName = "bliink"
 	BidderBmtm              BidderName = "bmtm"
 	BidderBrightroll        BidderName = "brightroll"
 	BidderCoinzilla         BidderName = "coinzilla"
@@ -197,9 +199,11 @@ const (
 	BidderSonobi            BidderName = "sonobi"
 	BidderSovrn             BidderName = "sovrn"
 	BidderStreamkey         BidderName = "streamkey"
+	BidderStroeerCore       BidderName = "stroeerCore"
 	BidderSynacormedia      BidderName = "synacormedia"
 	BidderTappx             BidderName = "tappx"
 	BidderTelaria           BidderName = "telaria"
+	BidderTrafficGate       BidderName = "trafficgate"
 	BidderTriplelift        BidderName = "triplelift"
 	BidderTripleliftNative  BidderName = "triplelift_native"
 	BidderTrustX            BidderName = "trustx"
@@ -256,6 +260,7 @@ func CoreBidderNames() []BidderName {
 		BidderApplogy,
 		BidderAppnexus,
 		BidderAudienceNetwork,
+		BidderAutomatad,
 		BidderAvocet,
 		BidderAxonix,
 		BidderBeachfront,
@@ -265,6 +270,7 @@ func CoreBidderNames() []BidderName {
 		BidderBidmyadz,
 		BidderBidsCube,
 		BidderBizzclick,
+		BidderBliink,
 		BidderBmtm,
 		BidderBrightroll,
 		BidderCoinzilla,
@@ -346,9 +352,11 @@ func CoreBidderNames() []BidderName {
 		BidderSonobi,
 		BidderSovrn,
 		BidderStreamkey,
+		BidderStroeerCore,
 		BidderSynacormedia,
 		BidderTappx,
 		BidderTelaria,
+		BidderTrafficGate,
 		BidderTriplelift,
 		BidderTripleliftNative,
 		BidderTrustX,
diff --git a/openrtb_ext/imp_nextmillennium.go b/openrtb_ext/imp_nextmillennium.go
index dbc970d0..32b9579c 100644
--- a/openrtb_ext/imp_nextmillennium.go
+++ b/openrtb_ext/imp_nextmillennium.go
@@ -1,5 +1,6 @@
 package openrtb_ext
 
 type ImpExtNextMillennium struct {
+	GroupID     string `json:"group_id,omitempty"`
 	PlacementID string `json:"placement_id"`
 }
diff --git a/openrtb_ext/imp_stroeercore.go b/openrtb_ext/imp_stroeercore.go
new file mode 100644
index 00000000..a4a232dd
--- /dev/null
+++ b/openrtb_ext/imp_stroeercore.go
@@ -0,0 +1,5 @@
+package openrtb_ext
+
+type ExtImpStroeerCore struct {
+	Sid string `json:"sid,omitempty"`
+}
diff --git a/openrtb_ext/imp_trafficgate.go b/openrtb_ext/imp_trafficgate.go
new file mode 100644
index 00000000..3ec0f290
--- /dev/null
+++ b/openrtb_ext/imp_trafficgate.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ExtImpTrafficGate struct {
+	PlacementID string `json:"placementId"`
+	Host        string `json:"host"`
+}
diff --git a/pbs/usersync.go b/pbs/usersync.go
index d5043b8c..748581af 100644
--- a/pbs/usersync.go
+++ b/pbs/usersync.go
@@ -32,6 +32,7 @@ type googleResponse struct {
 
 func (deps *UserSyncDeps) VerifyRecaptcha(response string) error {
 	ts := &http.Transport{
+		Proxy:           http.ProxyFromEnvironment,
 		TLSClientConfig: &tls.Config{RootCAs: ssl.GetRootCAPool()},
 	}
 
diff --git a/router/router.go b/router/router.go
index 10b231a0..509f81ba 100644
--- a/router/router.go
+++ b/router/router.go
@@ -130,6 +130,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 
 	generalHttpClient := &http.Client{
 		Transport: &http.Transport{
+			Proxy:               http.ProxyFromEnvironment,
 			MaxConnsPerHost:     cfg.Client.MaxConnsPerHost,
 			MaxIdleConns:        cfg.Client.MaxIdleConns,
 			MaxIdleConnsPerHost: cfg.Client.MaxIdleConnsPerHost,
@@ -140,6 +141,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 
 	cacheHttpClient := &http.Client{
 		Transport: &http.Transport{
+			Proxy:               http.ProxyFromEnvironment,
 			MaxConnsPerHost:     cfg.CacheClient.MaxConnsPerHost,
 			MaxIdleConns:        cfg.CacheClient.MaxIdleConns,
 			MaxIdleConnsPerHost: cfg.CacheClient.MaxIdleConnsPerHost,
@@ -199,6 +201,8 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	gvlVendorIDs := bidderInfos.ToGVLVendorIDMap()
 	vendorListFetcher := gdpr.NewVendorListFetcher(context.Background(), cfg.GDPR, generalHttpClient, gdpr.VendorListURLMaker)
 	gdprPerms := gdpr.NewPermissions(cfg.GDPR, &cfg.GDPR.TCF2, gvlVendorIDs, vendorListFetcher)
+	gdprPermsBuilder := gdpr.NewPermissionsBuilder(cfg.GDPR, gvlVendorIDs, vendorListFetcher)
+	tcf2CfgBuilder := gdpr.NewTCF2Config
 
 	cacheClient := pbc.NewClient(cacheHttpClient, &cfg.CacheURL, &cfg.ExtCacheURL, r.MetricsEngine)
 
@@ -208,7 +212,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 		return nil, errs
 	}
 
-	theExchange := exchange.NewExchange(adapters, cacheClient, cfg, syncersByBidder, r.MetricsEngine, bidderInfos, vendorListFetcher, rateConvertor, categoriesFetcher)
+	theExchange := exchange.NewExchange(adapters, cacheClient, cfg, syncersByBidder, r.MetricsEngine, bidderInfos, gdprPermsBuilder, tcf2CfgBuilder, rateConvertor, categoriesFetcher)
 	var uuidGenerator uuidutil.UUIDRandomGenerator
 	openrtbEndpoint, err := openrtb2.NewEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, storedRespFetcher)
 	if err != nil {
diff --git a/static/bidder-info/automatad.yaml b/static/bidder-info/automatad.yaml
new file mode 100644
index 00000000..0cdbb887
--- /dev/null
+++ b/static/bidder-info/automatad.yaml
@@ -0,0 +1,9 @@
+maintainer:
+  email: "tech@automatad.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+  site:
+    mediaTypes:
+      - banner
diff --git a/static/bidder-info/bliink.yaml b/static/bidder-info/bliink.yaml
new file mode 100644
index 00000000..648f14db
--- /dev/null
+++ b/static/bidder-info/bliink.yaml
@@ -0,0 +1,19 @@
+maintainer:
+  email: "support@bliink.io"
+gvlVendorID: 658
+modifyingVastXmlAllowed: true
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
+userSync:
+  redirect:
+    url: "https://cookiesync.api.bliink.io/getuid?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url={{.RedirectURL}}"
+    userMacro: "$UID"
\ No newline at end of file
diff --git a/static/bidder-info/nextmillennium.yaml b/static/bidder-info/nextmillennium.yaml
index 8caf4ac3..2bb723bf 100644
--- a/static/bidder-info/nextmillennium.yaml
+++ b/static/bidder-info/nextmillennium.yaml
@@ -7,3 +7,7 @@ capabilities:
   site:
     mediaTypes:
       - banner
+userSync:
+  iframe:
+    url: "https://cookies.nextmillmedia.com/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}"
+    userMacro: "[NMUID]"
diff --git a/static/bidder-info/stroeerCore.yaml b/static/bidder-info/stroeerCore.yaml
new file mode 100644
index 00000000..4db50fac
--- /dev/null
+++ b/static/bidder-info/stroeerCore.yaml
@@ -0,0 +1,18 @@
+maintainer:
+  email: "help@cz.stroeer-labs.com"
+gvlVendorID: 136
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+  site:
+    mediaTypes:
+      - banner
+userSync:
+  # for both user syncs, stroeerCore appends the user id to end of the redirect url and does not utilize a macro
+  iframe:
+    url: "https://js.adscale.de/pbsync.html?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect={{.RedirectURL}}"
+    userMacro: ""
+  redirect:
+    url: "https://ih.adscale.de/uu?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&cburl={{.RedirectURL}}"
+    userMacro: ""
diff --git a/static/bidder-info/trafficgate.yaml b/static/bidder-info/trafficgate.yaml
new file mode 100644
index 00000000..1d9ff63c
--- /dev/null
+++ b/static/bidder-info/trafficgate.yaml
@@ -0,0 +1,16 @@
+maintainer:
+  email: "support@bidscube.com"
+modifyingVastXmlAllowed: true
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+      - audio
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
+      - audio
diff --git a/static/bidder-params/automatad.json b/static/bidder-params/automatad.json
new file mode 100644
index 00000000..bcf0f329
--- /dev/null
+++ b/static/bidder-params/automatad.json
@@ -0,0 +1,18 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "Automatad Adapter Params",
+    "description": "A schema which validates params accepted by the Automatad adapter",
+    "type": "object",
+  
+    "properties": {
+      "position": {
+        "type": "string",
+        "description": "Position field"
+      },
+      "placementId": {
+        "type": "string",
+        "description": "Placement field"
+      }
+    },
+    "required": []
+}
diff --git a/static/bidder-params/bliink.json b/static/bidder-params/bliink.json
new file mode 100644
index 00000000..00621dee
--- /dev/null
+++ b/static/bidder-params/bliink.json
@@ -0,0 +1,8 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Bliink Adapter Params",
+  "description": "A schema which validates params accepted by the Bliink adapter",
+  "type": "object",
+
+  "properties": {}
+}
\ No newline at end of file
diff --git a/static/bidder-params/nextmillennium.json b/static/bidder-params/nextmillennium.json
index 07b869c2..743d72f8 100644
--- a/static/bidder-params/nextmillennium.json
+++ b/static/bidder-params/nextmillennium.json
@@ -8,7 +8,23 @@
       "type": "string",
       "minLength": 1,
       "description": "An id used to identify NextMillennium placement"
+    },
+    "group_id": {
+      "type": "string",
+      "minLength": 1,
+      "description": "An id used to identify NextMillennium placement group"
     }
   },
-  "required": ["placement_id"]
+  "anyOf": [
+    {
+      "required": [
+        "placement_id"
+      ]
+    },
+    {
+      "required": [
+        "group_id"
+      ]
+    }
+  ]
 }
diff --git a/static/bidder-params/stroeerCore.json b/static/bidder-params/stroeerCore.json
new file mode 100644
index 00000000..d3476be4
--- /dev/null
+++ b/static/bidder-params/stroeerCore.json
@@ -0,0 +1,14 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "StroeerCore Adapter Params",
+  "description": "A schema which validates params accepted by the StroeerCore adapter",
+
+  "type": "object",
+  "properties": {
+    "sid": {
+      "type": "string",
+      "description": "Slot Id"
+    }
+  },
+  "required": ["sid"]
+}
diff --git a/static/bidder-params/trafficgate.json b/static/bidder-params/trafficgate.json
new file mode 100644
index 00000000..8b7ff3be
--- /dev/null
+++ b/static/bidder-params/trafficgate.json
@@ -0,0 +1,23 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "TrafficGate Adapter Params",
+  "description": "A schema which validates params accepted by the TrafficGate adapter",
+  "type": "object",
+  "properties": {
+    "placementId": {
+      "type": "string",
+      "minLength": 1,
+      "description": "An ID which identifies the TrafficGate placement"
+    },
+    "host": {
+      "type": "string",
+      "minLength": 1,
+      "description": "An ID which identifies the TrafficGate host"
+    },
+    "customParams": {
+      "type": "object",
+      "description": "User-defined targeting key-value pairs."
+    }
+  },
+  "required" : [ "placementId", "host" ]
+}


Test patch:
diff --git a/account/account_test.go b/account/account_test.go
index 2b7f6a4e..68522462 100644
--- a/account/account_test.go
+++ b/account/account_test.go
@@ -66,6 +66,12 @@ func TestGetAccount(t *testing.T) {
 		{accountID: "disabled_acct", required: true, disabled: false, err: &errortypes.BlacklistedAcct{}},
 		{accountID: "disabled_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}},
 		{accountID: "disabled_acct", required: true, disabled: true, err: &errortypes.BlacklistedAcct{}},
+
+		// account not provided (does not exist)
+		{accountID: "", required: false, disabled: false, err: nil},
+		{accountID: "", required: true, disabled: false, err: nil},
+		{accountID: "", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}},
+		{accountID: "", required: true, disabled: true, err: &errortypes.AcctRequired{}},
 	}
 
 	for _, test := range testCases {
diff --git a/adapters/automatad/automatad_test.go b/adapters/automatad/automatad_test.go
new file mode 100644
index 00000000..0428f51f
--- /dev/null
+++ b/adapters/automatad/automatad_test.go
@@ -0,0 +1,20 @@
+package automatad
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderAutomatad, config.Adapter{
+		Endpoint: "http://www.biddertest.com"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "automatadtest", bidder)
+}
diff --git a/adapters/automatad/automatadtest/exemplary/simple-banner.json b/adapters/automatad/automatadtest/exemplary/simple-banner.json
new file mode 100644
index 00000000..98d4a0a6
--- /dev/null
+++ b/adapters/automatad/automatadtest/exemplary/simple-banner.json
@@ -0,0 +1,109 @@
+{
+	"mockBidRequest": {
+		"id": "test-request-id",
+		"imp": [
+			{
+				"id": "test-banner-imp-id",
+				"banner": {
+					"format": [
+						{
+							"w": 300,
+							"h": 250
+						}
+					]
+				},
+				"ext": {
+					"bidder": {
+						"position": "123",
+						"placementId": "a34gh6d"
+					}
+				}
+			}
+		],
+		"site": {
+			"id": "123"
+		}
+	},
+	"httpCalls": [
+		{
+			"expectedRequest": {
+				"uri": "http://www.biddertest.com",
+				"body": {
+					"id": "test-request-id",
+					"imp": [
+						{
+							"id": "test-banner-imp-id",
+							"banner": {
+								"format": [
+									{
+										"h": 250,
+										"w": 300
+									}
+								]
+							},
+							"ext": {
+								"bidder": {
+									"position": "123",
+									"placementId": "a34gh6d"
+								}
+							}
+						}
+					],
+					"site": {
+						"id": "123"
+					}
+				}
+			},
+			"mockResponse": {
+				"status": 200,
+				"body": {
+					"id": "test-request-id",
+					"seatbid": [
+						{
+							"seat": "apacdex",
+							"bid": [
+								{
+									"id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+									"impid": "test-banner-imp-id",
+									"price": 0.5,
+									"adm": "some-test-ad",
+									"crid": "crid_10",
+									"h": 250,
+									"w": 300,
+									"ext": {
+										"prebid": {
+											"type": "banner"
+										}
+									}
+								}
+							]
+						}
+					]
+				}
+			}
+		}
+	],
+	"expectedBidResponses": [
+		{
+			"bids": [
+				{
+					"bid": {
+						"id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+						"impid": "test-banner-imp-id",
+						"price": 0.5,
+						"adm": "some-test-ad",
+						"crid": "crid_10",
+						"w": 300,
+						"h": 250,
+						"ext": {
+							"prebid": {
+								"type": "banner"
+							}
+						}
+					},
+					"type": "banner"
+				}
+			]
+		}
+	]
+}
\ No newline at end of file
diff --git a/adapters/automatad/automatadtest/supplemental/bad-request.json b/adapters/automatad/automatadtest/supplemental/bad-request.json
new file mode 100644
index 00000000..df569ea1
--- /dev/null
+++ b/adapters/automatad/automatadtest/supplemental/bad-request.json
@@ -0,0 +1,69 @@
+{
+	"mockBidRequest": {
+		"id": "test-request-id",
+		"imp": [
+			{
+				"id": "test-banner-imp-id",
+				"banner": {
+					"format": [
+						{
+							"w": 200,
+							"h": 250
+						}
+					]
+				},
+				"ext": {
+					"bidder": {
+						"position": "123",
+						"placementId": "a34gh6d"
+					}
+				}
+			}
+		],
+		"site": {
+			"id": "123"
+		}
+	},
+	"httpCalls": [
+		{
+			"expectedRequest": {
+				"uri": "http://www.biddertest.com",
+				"body": {
+					"id": "test-request-id",
+					"imp": [
+						{
+							"id": "test-banner-imp-id",
+							"banner": {
+								"format": [
+									{
+										"h": 250,
+										"w": 200
+									}
+								]
+							},
+							"ext": {
+								"bidder": {
+									"position": "123",
+									"placementId": "a34gh6d"
+								}
+							}
+						}
+					],
+					"site": {
+						"id": "123"
+					}
+				}
+			},
+			"mockResponse": {
+				"status": 400,
+				"body": "bad request"
+			}
+		}
+	],
+	"expectedMakeBidsErrors": [
+		{
+			"value": "Unexpected status code: 400. Malformed request syntax received.",
+			"comparison": "literal"
+		}
+	]
+}
\ No newline at end of file
diff --git a/adapters/automatad/automatadtest/supplemental/error-500-request.json b/adapters/automatad/automatadtest/supplemental/error-500-request.json
new file mode 100644
index 00000000..52f87543
--- /dev/null
+++ b/adapters/automatad/automatadtest/supplemental/error-500-request.json
@@ -0,0 +1,69 @@
+{
+	"mockBidRequest": {
+		"id": "test-request-id",
+		"imp": [
+			{
+				"id": "test-banner-imp-id",
+				"banner": {
+					"format": [
+						{
+							"w": 200,
+							"h": 250
+						}
+					]
+				},
+				"ext": {
+					"bidder": {
+						"position": "123",
+						"placementId": "a34gh6d"
+					}
+				}
+			}
+		],
+		"site": {
+			"id": "123"
+		}
+	},
+	"httpCalls": [
+		{
+			"expectedRequest": {
+				"uri": "http://www.biddertest.com",
+				"body": {
+					"id": "test-request-id",
+					"imp": [
+						{
+							"id": "test-banner-imp-id",
+							"banner": {
+								"format": [
+									{
+										"h": 250,
+										"w": 200
+									}
+								]
+							},
+							"ext": {
+								"bidder": {
+									"position": "123",
+									"placementId": "a34gh6d"
+								}
+							}
+						}
+					],
+					"site": {
+						"id": "123"
+					}
+				}
+			},
+			"mockResponse": {
+				"status": 500,
+				"body": "Something went wrong on the bidder's side."
+			}
+		}
+	],
+	"expectedMakeBidsErrors": [
+		{
+			"value": "Unexpected status code: 500. Something went wrong on the bidder's side.",
+			"comparison": "literal"
+		}
+	]
+}
\ No newline at end of file
diff --git a/adapters/automatad/automatadtest/supplemental/no-content.json b/adapters/automatad/automatadtest/supplemental/no-content.json
new file mode 100644
index 00000000..7e3dec9e
--- /dev/null
+++ b/adapters/automatad/automatadtest/supplemental/no-content.json
@@ -0,0 +1,64 @@
+{
+	"mockBidRequest": {
+		"id": "test-request-id",
+		"imp": [
+			{
+				"id": "test-banner-imp-id",
+				"banner": {
+					"format": [
+						{
+							"w": 200,
+							"h": 250
+						}
+					]
+				},
+				"ext": {
+					"bidder": {
+						"position": "123",
+						"placementId": "a34gh6d"
+					}
+				}
+			}
+		],
+		"site": {
+			"id": "123"
+		}
+	},
+	"httpCalls": [
+		{
+			"expectedRequest": {
+				"uri": "http://www.biddertest.com",
+				"body": {
+					"id": "test-request-id",
+					"imp": [
+						{
+							"id": "test-banner-imp-id",
+							"banner": {
+								"format": [
+									{
+										"h": 250,
+										"w": 200
+									}
+								]
+							},
+							"ext": {
+								"bidder": {
+									"position": "123",
+									"placementId": "a34gh6d"
+								}
+							}
+						}
+					],
+					"site": {
+						"id": "123"
+					}
+				}
+			},
+			"mockResponse": {
+				"status": 204,
+				"body": ""
+			}
+		}
+	],
+	"expectedMakeBidsErrors": []
+}
\ No newline at end of file
diff --git a/adapters/automatad/params_test.go b/adapters/automatad/params_test.go
new file mode 100644
index 00000000..df7f676b
--- /dev/null
+++ b/adapters/automatad/params_test.go
@@ -0,0 +1,48 @@
+package automatad
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json schema. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderAutomatad, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected valid params: %s", validParam)
+		}
+	}
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json schema. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderAutomatad, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed invalid params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"position": "123abc"}`,
+	`{"placementId": "a34gh6d"}`,
+	`{"position": "123abc", "placementId" : "a34gh6d"}`,
+}
+
+var invalidParams = []string{
+	`{"position": 123abc}`,
+	`"placementId" : 46}`,
+	`{"position": "123abc", "placementId" : 46}`,
+	`{"position": 100, "placementId" : "a34gh6d"}`,
+	`{"position": 100, "placementId" : 200}`,
+}
diff --git a/adapters/bliink/bliink_test.go b/adapters/bliink/bliink_test.go
new file mode 100644
index 00000000..faa062f8
--- /dev/null
+++ b/adapters/bliink/bliink_test.go
@@ -0,0 +1,19 @@
+package bliink
+
+import (
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"testing"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderBliink, config.Adapter{
+		Endpoint: "http://biddertest.url/bid"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "bliinktest", bidder)
+}
diff --git a/adapters/bliink/bliinktest/exemplary/banner.json b/adapters/bliink/bliinktest/exemplary/banner.json
new file mode 100644
index 00000000..dbff2dfc
--- /dev/null
+++ b/adapters/bliink/bliinktest/exemplary/banner.json
@@ -0,0 +1,126 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123"
+    },
+    "user": {
+      "buyeruid": "awesome-user",
+      "ext": {
+        "consent": "gdprConsentString"
+      }
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "TAGID",
+        "banner": {
+          "w": 320,
+          "h": 50
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ]
+        },
+        "uri": "http://biddertest.url/bid",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123"
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "banner": {
+                "w": 320,
+                "h": 50
+              },
+              "tagid": "TAGID"
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user",
+            "ext": {
+              "consent": "gdprConsentString"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 0.9,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 320,
+                  "h": 50
+                }
+              ],
+              "type": "banner",
+              "seat": "bliink"
+            }
+          ],
+          "cur": "EUR"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 0.9,
+            "adm": "awesome-markup",
+            "adomain": [
+              "awesome.com"
+            ],
+            "crid": "20",
+            "w": 320,
+            "h": 50
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bliink/bliinktest/exemplary/native.json b/adapters/bliink/bliinktest/exemplary/native.json
new file mode 100644
index 00000000..960d91aa
--- /dev/null
+++ b/adapters/bliink/bliinktest/exemplary/native.json
@@ -0,0 +1,122 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123"
+    },
+    "user": {
+      "buyeruid": "awesome-user",
+      "ext": {
+        "consent": "gdprConsentString"
+      }
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "TAGID",
+        "native": {
+          "request": "{test json string}",
+          "ver": "1.2"
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ]
+        },
+        "uri": "http://biddertest.url/bid",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123"
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "native": {
+                "request": "{test json string}",
+                "ver": "1.2"
+              },
+              "tagid": "TAGID"
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user",
+            "ext": {
+              "consent": "gdprConsentString"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 0.9,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20"
+                }
+              ],
+              "type": "native",
+              "seat": "bliink"
+            }
+          ],
+          "cur": "EUR"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 0.9,
+            "adm": "awesome-markup",
+            "adomain": [
+              "awesome.com"
+            ],
+            "crid": "20"
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bliink/bliinktest/exemplary/video.json b/adapters/bliink/bliinktest/exemplary/video.json
new file mode 100644
index 00000000..8b3bc5e1
--- /dev/null
+++ b/adapters/bliink/bliinktest/exemplary/video.json
@@ -0,0 +1,136 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123"
+    },
+    "user": {
+      "buyeruid": "awesome-user",
+      "ext": {
+        "consent": "gdprConsentString"
+      }
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "TAGID",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 500,
+          "h": 300,
+          "minduration": 120,
+          "maxduration": 150
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ]
+        },
+        "uri": "http://biddertest.url/bid",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123"
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "w": 500,
+                "h": 300,
+                "minduration": 120,
+                "maxduration": 150
+              },
+              "tagid": "TAGID"
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user",
+            "ext": {
+              "consent": "gdprConsentString"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 0.9,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 320,
+                  "h": 50
+                }
+              ],
+              "type": "video",
+              "seat": "bliink"
+            }
+          ],
+          "cur": "EUR"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 0.9,
+            "adm": "awesome-markup",
+            "adomain": [
+              "awesome.com"
+            ],
+            "crid": "20",
+            "w": 320,
+            "h": 50
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bliink/bliinktest/supplemental/multiple_format_request.json b/adapters/bliink/bliinktest/supplemental/multiple_format_request.json
new file mode 100644
index 00000000..43ee1e9d
--- /dev/null
+++ b/adapters/bliink/bliinktest/supplemental/multiple_format_request.json
@@ -0,0 +1,125 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123"
+    },
+    "user": {
+      "buyeruid": "awesome-user",
+      "ext": {
+        "consent": "gdprConsentString"
+      }
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "TAGID",
+        "banner": {
+          "w": 320,
+          "h": 50
+        }
+      },
+      {
+        "id": "some-impression-id",
+        "tagid": "TAGID",
+        "native": {
+          "request": "{test json string}",
+          "ver": "1.2"
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ]
+        },
+        "uri": "http://biddertest.url/bid",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123"
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "banner": {
+                "w": 320,
+                "h": 50
+              },
+              "tagid": "TAGID"
+            },
+            {
+              "id": "some-impression-id",
+              "tagid": "TAGID",
+              "native": {
+                "request": "{test json string}",
+                "ver": "1.2"
+              }
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user",
+            "ext": {
+              "consent": "gdprConsentString"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 0.9,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 320,
+                  "h": 50
+                }
+              ]
+            }
+          ],
+          "cur": "EUR"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "unable to fetch mediaType in multi-format: some-impression-id",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bliink/bliinktest/supplemental/no_seat_bid_in_response.json b/adapters/bliink/bliinktest/supplemental/no_seat_bid_in_response.json
new file mode 100644
index 00000000..7482a6ff
--- /dev/null
+++ b/adapters/bliink/bliinktest/supplemental/no_seat_bid_in_response.json
@@ -0,0 +1,92 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123"
+    },
+    "user": {
+      "buyeruid": "awesome-user",
+      "ext": {
+        "consent": "gdprConsentString"
+      }
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "TAGID",
+        "banner": {
+          "w": 320,
+          "h": 50
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ]
+        },
+        "uri": "http://biddertest.url/bid",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123"
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "banner": {
+                "w": 320,
+                "h": 50
+              },
+              "tagid": "TAGID"
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user",
+            "ext": {
+              "consent": "gdprConsentString"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [],
+          "cur": "EUR"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Empty SeatBid array",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bliink/bliinktest/supplemental/status_bad_request.json b/adapters/bliink/bliinktest/supplemental/status_bad_request.json
new file mode 100644
index 00000000..2b0bf4e7
--- /dev/null
+++ b/adapters/bliink/bliinktest/supplemental/status_bad_request.json
@@ -0,0 +1,97 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123"
+    },
+    "user": {
+      "buyeruid": "awesome-user",
+      "ext": {
+        "consent": "gdprConsentString"
+      }
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "TAGID",
+        "banner": {
+          "w": 320,
+          "h": 50
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ]
+        },
+        "uri": "http://biddertest.url/bid",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123"
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "banner": {
+                "w": 320,
+                "h": 50
+              },
+              "tagid": "TAGID"
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user",
+            "ext": {
+              "consent": "gdprConsentString"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 400,
+        "body": {
+          "error": {
+            "message": "Validation failed",
+            "details": [
+              {
+                "message": "invalid consent"
+              }
+            ]
+          }
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bliink/bliinktest/supplemental/status_no_content.json b/adapters/bliink/bliinktest/supplemental/status_no_content.json
new file mode 100644
index 00000000..b696dc27
--- /dev/null
+++ b/adapters/bliink/bliinktest/supplemental/status_no_content.json
@@ -0,0 +1,92 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123"
+    },
+    "user": {
+      "buyeruid": "awesome-user",
+      "ext": {
+        "consent": "gdprConsentString"
+      }
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "TAGID",
+        "banner": {
+          "w": 320,
+          "h": 50
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ]
+        },
+        "uri": "http://biddertest.url/bid",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123"
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "banner": {
+                "w": 320,
+                "h": 50
+              },
+              "tagid": "TAGID"
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user",
+            "ext": {
+              "consent": "gdprConsentString"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 204,
+        "body": {
+          "error": {
+            "message": "No content",
+            "details": [
+              {
+                "message": "no content"
+              }
+            ]
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/bliink/bliinktest/supplemental/status_not_ok.json b/adapters/bliink/bliinktest/supplemental/status_not_ok.json
new file mode 100644
index 00000000..5560f830
--- /dev/null
+++ b/adapters/bliink/bliinktest/supplemental/status_not_ok.json
@@ -0,0 +1,92 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123"
+    },
+    "user": {
+      "buyeruid": "awesome-user",
+      "ext": {
+        "consent": "gdprConsentString"
+      }
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "TAGID",
+        "banner": {
+          "w": 320,
+          "h": 50
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ]
+        },
+        "uri": "http://biddertest.url/bid",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123"
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "banner": {
+                "w": 320,
+                "h": 50
+              },
+              "tagid": "TAGID"
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user",
+            "ext": {
+              "consent": "gdprConsentString"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 500,
+        "body": {
+          "error": {
+            "message": "Internal server error"
+          }
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 500. Run with request.debug = 1 for more info.",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-empty-group-id.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-empty-group-id.json
new file mode 100644
index 00000000..dd9ee32d
--- /dev/null
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-empty-group-id.json
@@ -0,0 +1,90 @@
+{
+  "mockBidRequest": {
+    "id": "testid",
+    "imp": [
+      {
+        "id": "testimpid",
+        "banner": {
+          "format": [
+            {
+              "w": 320,
+              "h": 250
+            },
+            {
+              "w": 320,
+              "h": 300
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "group_id": "",
+            "placement_id": "7819"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.nextmillmedia.com/openrtb2/auction",
+        "body":{
+          "id": "testid",
+          "ext": {
+            "prebid": {
+              "storedrequest": {
+                "id": "7819"
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "f7b3d2da-e762-410c-b069-424f92c4c4b2",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "impid": "123654",
+                  "id": "7457329903666272789",
+                  "price": 0.5,
+                  "adm": "Hello! It\"s a test ad!",
+                  "adid": "96846035",
+                  "adomain": ["test.addomain.com"],
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7457329903666272789",
+            "impid": "123654",
+            "price": 0.5,
+            "adm": "Hello! It\"s a test ad!",
+            "adid": "96846035",
+            "adomain": ["test.addomain.com"],
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id.json
new file mode 100644
index 00000000..72d22b28
--- /dev/null
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id.json
@@ -0,0 +1,101 @@
+{
+  "mockBidRequest": {
+    "id": "testid",
+    "imp": [
+      {
+        "id": "testimpid",
+        "banner": {
+          "format": [
+            {
+              "w": 320,
+              "h": 250
+            },
+            {
+              "w": 320,
+              "h": 300
+            }
+          ],
+          "w": 320,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "group_id": "7819"
+          }
+        }
+      }
+    ],
+    "site": {
+      "domain": "www.example.com",
+      "page": "http://www.example.com",
+      "publisher": {
+          "domain": "example.com"
+      },
+      "ext": {
+          "amp": 0
+      }
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.nextmillmedia.com/openrtb2/auction",
+        "body":{
+          "id": "testid",
+          "ext": {
+            "prebid": {
+              "storedrequest": {
+                "id": "g7819;320x250;www.example.com"
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "f7b3d2da-e762-410c-b069-424f92c4c4b2",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "impid": "123654",
+                  "id": "7457329903666272789",
+                  "price": 0.5,
+                  "adm": "Hello! It\"s a test ad!",
+                  "adid": "96846035",
+                  "adomain": ["test.addomain.com"],
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7457329903666272789",
+            "impid": "123654",
+            "price": 0.5,
+            "adm": "Hello! It\"s a test ad!",
+            "adid": "96846035",
+            "adomain": ["test.addomain.com"],
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id_app.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id_app.json
new file mode 100644
index 00000000..6633a063
--- /dev/null
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id_app.json
@@ -0,0 +1,94 @@
+{
+  "mockBidRequest": {
+    "id": "testid",
+    "imp": [
+      {
+        "id": "testimpid",
+        "banner": {
+          "format": [
+            {
+              "w": 320,
+              "h": 250
+            },
+            {
+              "w": 320,
+              "h": 300
+            }
+          ],
+          "w": 320,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "group_id": "7819"
+          }
+        }
+      }
+    ],
+    "app": {
+      "domain": "www.example.com"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.nextmillmedia.com/openrtb2/auction",
+        "body":{
+          "id": "testid",
+          "ext": {
+            "prebid": {
+              "storedrequest": {
+                "id": "g7819;320x250;www.example.com"
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "f7b3d2da-e762-410c-b069-424f92c4c4b2",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "impid": "123654",
+                  "id": "7457329903666272789",
+                  "price": 0.5,
+                  "adm": "Hello! It\"s a test ad!",
+                  "adid": "96846035",
+                  "adomain": ["test.addomain.com"],
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7457329903666272789",
+            "impid": "123654",
+            "price": 0.5,
+            "adm": "Hello! It\"s a test ad!",
+            "adid": "96846035",
+            "adomain": ["test.addomain.com"],
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-only-width.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-only-width.json
new file mode 100644
index 00000000..2dc1f3b6
--- /dev/null
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-only-width.json
@@ -0,0 +1,83 @@
+{
+  "mockBidRequest": {
+    "id": "testid",
+    "imp": [
+      {
+        "id": "testimpid",
+        "banner": {
+          "w": 320
+        },
+        "ext": {
+          "bidder": {
+            "group_id": "7819"
+          }
+        }
+      }
+    ],
+    "app": {
+      "domain": "www.example.com"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.nextmillmedia.com/openrtb2/auction",
+        "body":{
+          "id": "testid",
+          "ext": {
+            "prebid": {
+              "storedrequest": {
+                "id": "g7819;;www.example.com"
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "f7b3d2da-e762-410c-b069-424f92c4c4b2",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "impid": "123654",
+                  "id": "7457329903666272789",
+                  "price": 0.5,
+                  "adm": "Hello! It\"s a test ad!",
+                  "adid": "96846035",
+                  "adomain": ["test.addomain.com"],
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7457329903666272789",
+            "impid": "123654",
+            "price": 0.5,
+            "adm": "Hello! It\"s a test ad!",
+            "adid": "96846035",
+            "adomain": ["test.addomain.com"],
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-wh.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-wh.json
new file mode 100644
index 00000000..9e3385fd
--- /dev/null
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-wh.json
@@ -0,0 +1,84 @@
+{
+  "mockBidRequest": {
+    "id": "testid",
+    "imp": [
+      {
+        "id": "testimpid",
+        "banner": {
+          "w": 320,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "group_id": "7819"
+          }
+        }
+      }
+    ],
+    "app": {
+      "domain": "www.example.com"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.nextmillmedia.com/openrtb2/auction",
+        "body":{
+          "id": "testid",
+          "ext": {
+            "prebid": {
+              "storedrequest": {
+                "id": "g7819;320x250;www.example.com"
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "f7b3d2da-e762-410c-b069-424f92c4c4b2",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "impid": "123654",
+                  "id": "7457329903666272789",
+                  "price": 0.5,
+                  "adm": "Hello! It\"s a test ad!",
+                  "adid": "96846035",
+                  "adomain": ["test.addomain.com"],
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7457329903666272789",
+            "impid": "123654",
+            "price": 0.5,
+            "adm": "Hello! It\"s a test ad!",
+            "adid": "96846035",
+            "adomain": ["test.addomain.com"],
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-domain.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-domain.json
new file mode 100644
index 00000000..6a1694ba
--- /dev/null
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-domain.json
@@ -0,0 +1,91 @@
+{
+  "mockBidRequest": {
+    "id": "testid",
+    "imp": [
+      {
+        "id": "testimpid",
+        "banner": {
+          "format": [
+            {
+              "w": 320,
+              "h": 250
+            },
+            {
+              "w": 320,
+              "h": 300
+            }
+          ],
+          "w": 320,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "group_id": "7819"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.nextmillmedia.com/openrtb2/auction",
+        "body":{
+          "id": "testid",
+          "ext": {
+            "prebid": {
+              "storedrequest": {
+                "id": "g7819;320x250;"
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "f7b3d2da-e762-410c-b069-424f92c4c4b2",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "impid": "123654",
+                  "id": "7457329903666272789",
+                  "price": 0.5,
+                  "adm": "Hello! It\"s a test ad!",
+                  "adid": "96846035",
+                  "adomain": ["test.addomain.com"],
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7457329903666272789",
+            "impid": "123654",
+            "price": 0.5,
+            "adm": "Hello! It\"s a test ad!",
+            "adid": "96846035",
+            "adomain": ["test.addomain.com"],
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-size.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-size.json
new file mode 100644
index 00000000..9da3ccf9
--- /dev/null
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-size.json
@@ -0,0 +1,89 @@
+{
+  "mockBidRequest": {
+    "id": "testid",
+    "imp": [
+      {
+        "id": "testimpid",
+        "banner": {
+          "format": [
+            {
+              "w": 320,
+              "h": 250
+            },
+            {
+              "w": 320,
+              "h": 300
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "group_id": "7819"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.nextmillmedia.com/openrtb2/auction",
+        "body":{
+          "id": "testid",
+          "ext": {
+            "prebid": {
+              "storedrequest": {
+                "id": "g7819;320x250;"
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "f7b3d2da-e762-410c-b069-424f92c4c4b2",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "impid": "123654",
+                  "id": "7457329903666272789",
+                  "price": 0.5,
+                  "adm": "Hello! It\"s a test ad!",
+                  "adid": "96846035",
+                  "adomain": ["test.addomain.com"],
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7457329903666272789",
+            "impid": "123654",
+            "price": 0.5,
+            "adm": "Hello! It\"s a test ad!",
+            "adid": "96846035",
+            "adomain": ["test.addomain.com"],
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/empty-banner-obj.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/empty-banner-obj.json
new file mode 100644
index 00000000..52f53bdb
--- /dev/null
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/empty-banner-obj.json
@@ -0,0 +1,78 @@
+{
+  "mockBidRequest": {
+    "id": "testid",
+    "imp": [
+      {
+        "id": "testimpid",
+        "banner": {},
+        "ext": {
+          "bidder": {
+            "group_id": "7819"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://pbs.nextmillmedia.com/openrtb2/auction",
+        "body":{
+          "id": "testid",
+          "ext": {
+            "prebid": {
+              "storedrequest": {
+                "id": "g7819;;"
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "f7b3d2da-e762-410c-b069-424f92c4c4b2",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "impid": "123654",
+                  "id": "7457329903666272789",
+                  "price": 0.5,
+                  "adm": "Hello! It\"s a test ad!",
+                  "adid": "96846035",
+                  "adomain": ["test.addomain.com"],
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7457329903666272789",
+            "impid": "123654",
+            "price": 0.5,
+            "adm": "Hello! It\"s a test ad!",
+            "adid": "96846035",
+            "adomain": ["test.addomain.com"],
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/nextmillennium/params_test.go b/adapters/nextmillennium/params_test.go
index 286d80c6..50a9c377 100644
--- a/adapters/nextmillennium/params_test.go
+++ b/adapters/nextmillennium/params_test.go
@@ -35,6 +35,7 @@ func TestInvalidParams(t *testing.T) {
 
 var validParams = []string{
 	`{"placement_id": "123654"}`,
+	`{"group_id": "123"}`,
 }
 
 var invalidParams = []string{
diff --git a/adapters/stroeerCore/params_test.go b/adapters/stroeerCore/params_test.go
new file mode 100644
index 00000000..92586189
--- /dev/null
+++ b/adapters/stroeerCore/params_test.go
@@ -0,0 +1,49 @@
+package stroeerCore
+
+import (
+	"encoding/json"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"testing"
+)
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderStroeerCore, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected stroeerCore params: %s", validParam)
+		}
+	}
+}
+
+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.BidderStroeerCore, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"sid":"slot id"}`,
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{"something":"else"}`,
+	`{"sid":false}`,
+}
diff --git a/adapters/stroeerCore/stroeercore_test.go b/adapters/stroeerCore/stroeercore_test.go
new file mode 100644
index 00000000..be335003
--- /dev/null
+++ b/adapters/stroeerCore/stroeercore_test.go
@@ -0,0 +1,19 @@
+package stroeerCore
+
+import (
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"testing"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderStroeerCore, config.Adapter{
+		Endpoint: "http://localhost/s2sdsh"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "stroeercoretest", bidder)
+}
diff --git a/adapters/stroeerCore/stroeercoretest/exemplary/mobile-banner-single.json b/adapters/stroeerCore/stroeercoretest/exemplary/mobile-banner-single.json
new file mode 100644
index 00000000..0abdbc8b
--- /dev/null
+++ b/adapters/stroeerCore/stroeercoretest/exemplary/mobile-banner-single.json
@@ -0,0 +1,188 @@
+{
+  "mockBidRequest": {
+    "id": "auction-id",
+    "cur": ["EUR"],
+    "imp": [
+      {
+        "id": "3",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 728,
+              "h": 90
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "sid": "123456"
+          }
+        }
+      }
+    ],
+    "device": {
+      "geo":{
+        "city":"Paris",
+        "country":"FRA",
+        "lat": 48.860757,
+        "lon": 2.337562,
+        "region":"IDF",
+        "zip":"75001"
+      },
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0,
+      "devicetype": 4,
+      "ifa": "e785543f-0be8-48f1-2c53-b4ae46a942d3",
+      "hwv": "6S",
+      "make": "Apple",
+      "model": "iPhone",
+      "os": "iOS",
+      "osv": "8.1"
+    },
+    "app": {
+      "id": "test-app-id",
+      "name": "test-app-name",
+      "bundle": "test-app-bundle",
+      "domain": "test-app-domain",
+      "storeurl": "test-app-storeurl",
+      "cat": [
+        "IAB9-30",
+        "IAB9-5"
+      ],
+      "ver": "1.0",
+      "privacypolicy": 1,
+      "paid": 2,
+      "publisher": {
+        "domain": "topleveldomain.com",
+        "id": "04241e0b1cc98976858ce16377c7eef4",
+        "name": "App Publisher"
+      },
+      "keywords": "game,puzzle"
+    },
+    "user": {
+      "buyeruid": "test-buyer-user-id"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": ["application/json"],
+          "Content-Type": ["application/json;charset=utf-8"]
+        },
+        "uri": "http://localhost/s2sdsh",
+        "body": {
+          "id": "auction-id",
+          "cur": ["EUR"],
+          "imp": [
+            {
+              "id": "3",
+              "tagid": "123456",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 728,
+                    "h": 90
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "sid": "123456"
+                }
+              }
+            }
+          ],
+          "device": {
+            "geo":{
+              "city":"Paris",
+              "country":"FRA",
+              "lat": 48.860757,
+              "lon": 2.337562,
+              "region":"IDF",
+              "zip":"75001"
+            },
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0,
+            "devicetype": 4,
+            "ifa": "e785543f-0be8-48f1-2c53-b4ae46a942d3",
+            "hwv": "6S",
+            "make": "Apple",
+            "model": "iPhone",
+            "os": "iOS",
+            "osv": "8.1"
+          },
+          "app": {
+            "id": "test-app-id",
+            "name": "test-app-name",
+            "bundle": "test-app-bundle",
+            "domain": "test-app-domain",
+            "storeurl": "test-app-storeurl",
+            "cat": [
+              "IAB9-30",
+              "IAB9-5"
+            ],
+            "ver": "1.0",
+            "privacypolicy": 1,
+            "paid": 2,
+            "publisher": {
+              "domain": "topleveldomain.com",
+              "id": "04241e0b1cc98976858ce16377c7eef4",
+              "name": "App Publisher"
+            },
+            "keywords": "game,puzzle"
+          },
+          "user": {
+            "buyeruid": "test-buyer-user-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "bids": [
+            {
+              "id": "4985939039393-04",
+              "bidId": "3",
+              "cpm": 0.24,
+              "width": 468,
+              "height": 60,
+              "ad": "advert",
+              "crid": "XYZijk"
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids" : [{
+        "bid": {
+          "id": "4985939039393-04",
+          "impid": "3",
+          "price": 0.24,
+          "adm": "advert",
+          "w": 468,
+          "h": 60,
+          "crid": "XYZijk"
+        },
+        "type": "banner"
+      }]
+    }
+  ]
+}
diff --git a/adapters/stroeerCore/stroeercoretest/exemplary/site-banner-multi.json b/adapters/stroeerCore/stroeercoretest/exemplary/site-banner-multi.json
new file mode 100644
index 00000000..091f244c
--- /dev/null
+++ b/adapters/stroeerCore/stroeercoretest/exemplary/site-banner-multi.json
@@ -0,0 +1,188 @@
+{
+  "mockBidRequest": {
+    "id": "auction-id",
+    "cur": ["EUR"],
+    "imp": [
+      {
+        "id": "3",
+        "banner": {
+          "format": [
+            {
+              "w": 468,
+              "h": 60
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "sid": "123456"
+          }
+        }
+      },
+      {
+        "id": "9",
+        "banner": {
+          "format": [
+            {
+              "w": 770,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "sid": "85310"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "site": {
+      "domain": "www.publisher.com",
+      "page": "http://www.publisher.com/some/path",
+      "ext": {
+        "amp": 1
+      }
+    },
+    "user": {
+      "buyeruid": "test-buyer-user-id"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": ["application/json"],
+          "Content-Type": ["application/json;charset=utf-8"]
+        },
+        "uri": "http://localhost/s2sdsh",
+        "body": {
+          "id": "auction-id",
+          "cur": ["EUR"],
+          "imp": [
+            {
+              "id": "3",
+              "tagid": "123456",
+              "banner": {
+                "format": [
+                  {
+                    "w": 468,
+                    "h": 60
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "sid": "123456"
+                }
+              }
+            },
+            {
+              "id": "9",
+              "tagid": "85310",
+              "banner": {
+                "format": [
+                  {
+                    "w": 770,
+                    "h": 250
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "sid": "85310"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "site": {
+            "domain": "www.publisher.com",
+            "page": "http://www.publisher.com/some/path",
+            "ext": {
+              "amp": 1
+            }
+          },
+          "user": {
+            "buyeruid": "test-buyer-user-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "bids": [
+            {
+              "id": "3929239282-01",
+              "bidId": "3",
+              "cpm": 2,
+              "width": 468,
+              "height": 60,
+              "ad": "advert",
+              "crid": "qwert"
+            },
+            {
+              "id": "3929239282-02",
+              "bidId": "9",
+              "cpm": 7.21,
+              "width": 770,
+              "height": 250,
+              "ad": "another advert",
+              "crid": "wasd"
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "id": "3929239282-01",
+            "impid": "3",
+            "price": 2,
+            "adm": "advert",
+            "w": 468,
+            "h": 60,
+            "crid": "qwert"
+          },
+          "type": "banner"
+        },
+        {
+          "bid": {
+            "id": "3929239282-02",
+            "impid": "9",
+            "price": 7.21,
+            "adm": "another advert",
+            "w": 770,
+            "h": 250,
+            "crid": "wasd"
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/stroeerCore/stroeercoretest/exemplary/site-banner-single.json b/adapters/stroeerCore/stroeercoretest/exemplary/site-banner-single.json
new file mode 100644
index 00000000..ca6ea0c2
--- /dev/null
+++ b/adapters/stroeerCore/stroeercoretest/exemplary/site-banner-single.json
@@ -0,0 +1,132 @@
+{
+  "mockBidRequest": {
+    "id": "auction-id",
+    "cur": ["EUR"],
+    "imp": [
+      {
+        "id": "3",
+        "banner": {
+          "format": [
+            {
+              "w": 468,
+              "h": 60
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "sid": "123456"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "site": {
+      "domain": "www.publisher.com",
+      "page": "http://www.publisher.com/some/path",
+      "ext": {
+        "amp": 0
+      }
+    },
+    "user": {
+      "buyeruid": "test-buyer-user-id"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": ["application/json"],
+          "Content-Type": ["application/json;charset=utf-8"]
+        },
+        "uri": "http://localhost/s2sdsh",
+        "body": {
+          "id": "auction-id",
+          "cur": ["EUR"],
+          "imp": [
+            {
+              "id": "3",
+              "tagid": "123456",
+              "banner": {
+                "format": [
+                  {
+                    "w": 468,
+                    "h": 60
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "sid": "123456"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "site": {
+            "domain": "www.publisher.com",
+            "page": "http://www.publisher.com/some/path",
+            "ext": {
+              "amp": 0
+            }
+          },
+          "user": {
+            "buyeruid": "test-buyer-user-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "bids": [
+            {
+              "id": "8923356982838-09",
+              "bidId": "3",
+              "cpm": 2,
+              "width": 468,
+              "height": 60,
+              "ad": "advert",
+              "crid": "wasd"
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids" : [{
+        "bid": {
+          "id": "8923356982838-09",
+          "impid": "3",
+          "price": 2,
+          "adm": "advert",
+          "w": 468,
+          "h": 60,
+          "crid": "wasd"
+        },
+        "type": "banner"
+      }]
+    }
+  ]
+}
diff --git a/adapters/stroeerCore/stroeercoretest/supplemental/bad-server-response.json b/adapters/stroeerCore/stroeercoretest/supplemental/bad-server-response.json
new file mode 100644
index 00000000..48275916
--- /dev/null
+++ b/adapters/stroeerCore/stroeercoretest/supplemental/bad-server-response.json
@@ -0,0 +1,59 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "banner": {
+          "w": 300,
+          "h": 200
+        },
+        "ext": {
+          "bidder": {
+            "sid": "tagid"
+          }
+        }
+      }
+    ],
+    "user": {
+      "buyeruid": "test-buyer-user-id"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/s2sdsh",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "tagid": "tagid",
+              "banner": {
+                "w": 300,
+                "h": 200
+              },
+              "ext": {
+                "bidder": {
+                  "sid": "tagid"
+                }
+              }
+            }
+          ],
+          "user": {
+            "buyeruid": "test-buyer-user-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 500
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected http status code: 500.",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/params_test.go b/adapters/trafficgate/params_test.go
new file mode 100644
index 00000000..4dc2c792
--- /dev/null
+++ b/adapters/trafficgate/params_test.go
@@ -0,0 +1,48 @@
+package trafficgate
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// TestValidParams makes sure that the trafficgate schema accepts all imp.ext fields which we intend to support.
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderTrafficGate, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected trafficgate params: %s", validParam)
+		}
+	}
+}
+
+// TestInvalidParams makes sure that the trafficgate schema rejects all the imp.ext fields we don't support.
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderTrafficGate, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"placementId": "11", "host": "example"}`,
+}
+
+var invalidParams = []string{
+	`{"id": "456"}`,
+	`{"placementid": "3456", "host": ""}`,
+	`{"placement_id": 346, "host": ""}`,
+	`{"placementID": "", "HOST": "example"}`,
+	`{"placementId": 234}`,
+}
diff --git a/adapters/trafficgate/trafficgate_test.go b/adapters/trafficgate/trafficgate_test.go
new file mode 100644
index 00000000..37cea770
--- /dev/null
+++ b/adapters/trafficgate/trafficgate_test.go
@@ -0,0 +1,28 @@
+package trafficgate
+
+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.BidderTrafficGate, config.Adapter{
+		Endpoint: "http://{{.Host}}.bc-plugin.com/?c=o&m=rtb"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "trafficgatetest", bidder)
+}
+
+func TestEndpointTemplateMalformed(t *testing.T) {
+	_, buildErr := Builder(openrtb_ext.BidderTrafficGate, config.Adapter{
+		Endpoint: "{{Malformed}}"})
+
+	assert.Error(t, buildErr)
+}
diff --git a/adapters/trafficgate/trafficgatetest/exemplary/multiple-imps.json b/adapters/trafficgate/trafficgatetest/exemplary/multiple-imps.json
new file mode 100644
index 00000000..14f77f62
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/exemplary/multiple-imps.json
@@ -0,0 +1,149 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    },
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": ["video/mp4"],
+          "protocols": [2, 5],
+          "w": 1024,
+          "h": 576
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "3",
+            "host": "example"
+          }
+        }
+      },
+      {
+        "id": "test-imp-id2",
+        "video": {
+          "mimes": ["video/mp4"],
+          "protocols": [2, 5],
+          "w": 1920,
+          "h": 1080
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "3",
+            "host": "example"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+          },
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "video": {
+                "mimes": ["video/mp4"],
+                "protocols": [2, 5],
+                "w": 1024,
+                "h": 576
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": "3",
+                  "host": "example"
+                }
+              }
+            },
+            {
+              "id": "test-imp-id2",
+              "video": {
+                "mimes": ["video/mp4"],
+                "protocols": [2, 5],
+                "w": 1920,
+                "h": 1080
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": "3",
+                  "host": "example"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test_bid_id",
+                  "impid": "test-imp-id",
+                  "price": 0.27543,
+                  "adm": "<VAST version=\"3.0\"></VAST>",
+                  "cid": "test_cid",
+                  "crid": "test_crid",
+                  "dealid": "test_dealid",
+                  "ext": {
+                    "prebid": {
+                      "type": "video"
+                    }
+                  }
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "<VAST version=\"3.0\"></VAST>",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type": "video"
+        }
+      ],
+      "currency": "USD"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/exemplary/simple-audio.json b/adapters/trafficgate/trafficgatetest/exemplary/simple-audio.json
new file mode 100644
index 00000000..4cbe9f63
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/exemplary/simple-audio.json
@@ -0,0 +1,111 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    },
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "audio": {
+          "mimes": ["audio/mp4"]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "3",
+            "host": "example"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+          },
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "audio": {
+                "mimes": ["audio/mp4"]
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": "3",
+                  "host": "example"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+          {
+            "bid": [
+              {
+                "id": "test_bid_id",
+                "impid": "test-imp-id",
+                "price": 0.27543,
+                "adm": "<VAST version=\"4.1\"></VAST>",
+                "cid": "test_cid",
+                "crid": "test_crid",
+                "dealid": "test_dealid",
+                "ext": {
+                  "prebid": {
+                    "type": "audio"
+                  }
+                }
+              }
+            ]
+          }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "<VAST version=\"4.1\"></VAST>",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "ext": {
+              "prebid": {
+                "type": "audio"
+              }
+            }
+          },
+          "type": "audio"
+        }
+      ],
+      "currency": "USD"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/exemplary/simple-banner.json b/adapters/trafficgate/trafficgatetest/exemplary/simple-banner.json
new file mode 100644
index 00000000..7f3c7abf
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/exemplary/simple-banner.json
@@ -0,0 +1,134 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+        {
+            "id": "test-imp-id",
+            "banner": {
+                "format": [
+                    {
+                      "w": 300,
+                      "h": 250
+                    },
+                    {
+                      "w": 300,
+                      "h": 600
+                    }
+                ]
+            },
+            "ext": {
+                "bidder": {
+                  "placementId": "3",
+                  "host": "example"
+                }
+            }
+        }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    }
+},
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+              {
+                "id": "test-imp-id",
+                "banner": {
+                  "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+                },
+              "ext": {
+                "bidder": {
+                  "placementId": "3",
+                  "host": "example"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+              {
+                "bid": [
+                  {
+                    "id": "test_bid_id",
+                    "impid": "test-imp-id",
+                    "price": 0.27543,
+                    "adm": "<iframe width=\"300\" height=\"250\"></iframe>",
+                    "cid": "test_cid",
+                    "crid": "test_crid",
+                    "dealid": "test_dealid",
+                    "w": 300,
+                    "h": 250,
+                    "ext": {
+                      "prebid": {
+                        "type": "banner"
+                      }
+                    }
+                  }
+                ]
+              }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "<iframe width=\"300\" height=\"250\"></iframe>",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ],
+      "currency": "USD"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/exemplary/simple-native.json b/adapters/trafficgate/trafficgatetest/exemplary/simple-native.json
new file mode 100644
index 00000000..c2482c86
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/exemplary/simple-native.json
@@ -0,0 +1,118 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+        {
+            "id": "test-imp-id",
+            "native": {
+              "ver":"1.1",
+              "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+            },
+            "ext": {
+                "bidder": {
+                  "placementId": "5",
+                  "host": "example"
+                }
+            }
+        }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    }
+},
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+              {
+                "id": "test-imp-id",
+                "native": {
+                  "ver":"1.1",
+                  "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+                },
+              "ext": {
+                "bidder": {
+                  "placementId": "5",
+                  "host": "example"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+              {
+                "bid": [
+                  {
+                    "id": "test_bid_id",
+                    "impid": "test-imp-id",
+                    "price": 0.97642,
+                    "adm": "awesome",
+                    "cid": "test_cid",
+                    "crid": "test_crid",
+                    "dealid": "test_dealid",
+                    "w": 300,
+                    "h": 250,
+                    "ext": {
+                      "prebid": {
+                        "type": "native"
+                      }
+                    }
+                  }
+                ]
+              }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.97642,
+            "adm": "awesome",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "native"
+              }
+            }
+          },
+          "type": "native"
+        }
+      ],
+      "currency": "USD"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/exemplary/simple-video.json b/adapters/trafficgate/trafficgatetest/exemplary/simple-video.json
new file mode 100644
index 00000000..61a6d6ef
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/exemplary/simple-video.json
@@ -0,0 +1,119 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    },
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": ["video/mp4"],
+          "protocols": [2, 5],
+          "w": 1024,
+          "h": 576
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "3",
+            "host": "example"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+          },
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "video": {
+                "mimes": ["video/mp4"],
+                "protocols": [2, 5],
+                "w": 1024,
+                "h": 576
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": "3",
+                  "host": "example"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+          {
+            "bid": [
+              {
+                "id": "test_bid_id",
+                "impid": "test-imp-id",
+                "price": 0.27543,
+                "adm": "<VAST version=\"3.0\"></VAST>",
+                "cid": "test_cid",
+                "crid": "test_crid",
+                "dealid": "test_dealid",
+                "ext": {
+                  "prebid": {
+                    "type": "video"
+                  }
+                }
+              }
+            ]
+          }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "<VAST version=\"3.0\"></VAST>",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type": "video"
+        }
+      ],
+      "currency": "USD"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/exemplary/simple-web-banner.json b/adapters/trafficgate/trafficgatetest/exemplary/simple-web-banner.json
new file mode 100644
index 00000000..f46fde30
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/exemplary/simple-web-banner.json
@@ -0,0 +1,130 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "3",
+            "host": "example"
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "1",
+      "domain": "test.com"
+    },
+    "device": {
+      "ip": "123.123.123.123"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": "3",
+                  "host": "example"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "1",
+            "domain": "test.com"
+          },
+          "device": {
+            "ip": "123.123.123.123"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test_bid_id",
+                  "impid": "test-imp-id",
+                  "price": 0.27543,
+                  "adm": "<iframe width=\"468\" height=\"60\"></iframe>",
+                  "cid": "test_cid",
+                  "crid": "test_crid",
+                  "dealid": "test_dealid",
+                  "w": 468,
+                  "h": 60,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "<iframe width=\"468\" height=\"60\"></iframe>",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "w": 468,
+            "h": 60,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ],
+      "currency": "USD"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/supplemental/bad-imp-ext.json b/adapters/trafficgate/trafficgatetest/supplemental/bad-imp-ext.json
new file mode 100644
index 00000000..d244f329
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/supplemental/bad-imp-ext.json
@@ -0,0 +1,42 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "invalid": {
+            "placementId": "1",
+            "host": "example"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    }
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Bidder parameters required",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/supplemental/bad_bidtype_response.json b/adapters/trafficgate/trafficgatetest/supplemental/bad_bidtype_response.json
new file mode 100644
index 00000000..177a7cf2
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/supplemental/bad_bidtype_response.json
@@ -0,0 +1,108 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "1",
+            "host": "example"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": "1",
+                  "host": "example"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test_bid_id",
+                  "impid": "test-imp-id",
+                  "price": 0.27543,
+                  "adm": "awesome",
+                  "cid": "test_cid",
+                  "crid": "test_crid",
+                  "dealid": "test_dealid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": ""
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Missing response ext",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/supplemental/bad_ext_response.json b/adapters/trafficgate/trafficgatetest/supplemental/bad_ext_response.json
new file mode 100644
index 00000000..caea968d
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/supplemental/bad_ext_response.json
@@ -0,0 +1,110 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "1",
+            "host": "example"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": "1",
+                  "host": "example"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test_bid_id",
+                  "impid": "test-imp-id",
+                  "price": 0.27543,
+                  "adm": "awesome",
+                  "cid": "test_cid",
+                  "crid": "test_crid",
+                  "dealid": "test_dealid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {}
+                  }
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unable to read bid.ext.prebid.type",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/supplemental/bad_response.json b/adapters/trafficgate/trafficgatetest/supplemental/bad_response.json
new file mode 100644
index 00000000..60c9a43e
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/supplemental/bad_response.json
@@ -0,0 +1,87 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "1",
+            "host": "example"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": "1",
+                  "host": "example"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": ""
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/supplemental/bad_status_code.json b/adapters/trafficgate/trafficgatetest/supplemental/bad_status_code.json
new file mode 100644
index 00000000..41207652
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/supplemental/bad_status_code.json
@@ -0,0 +1,81 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "1",
+            "host": "example"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {}
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": "1",
+                  "host": "example"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {}
+        }
+      },
+      "mockResponse": {
+        "status": 400,
+        "body": {}
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Error response with status 400",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/supplemental/imp_ext_empty_object.json b/adapters/trafficgate/trafficgatetest/supplemental/imp_ext_empty_object.json
new file mode 100644
index 00000000..18d84b35
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/supplemental/imp_ext_empty_object.json
@@ -0,0 +1,37 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {}
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    }
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Bidder parameters required",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/supplemental/imp_ext_string.json b/adapters/trafficgate/trafficgatetest/supplemental/imp_ext_string.json
new file mode 100644
index 00000000..3037675a
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/supplemental/imp_ext_string.json
@@ -0,0 +1,37 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": ""
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    }
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Missing bidder ext",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/supplemental/status-204.json b/adapters/trafficgate/trafficgatetest/supplemental/status-204.json
new file mode 100644
index 00000000..79235df2
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/supplemental/status-204.json
@@ -0,0 +1,82 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "1",
+            "host": "example"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": "1",
+                  "host": "example"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+          }
+        }
+      },
+      "expectedBidResponses": [],
+      "mockResponse": {
+        "status": 204,
+        "body": {}
+      }
+    }
+  ]
+}
diff --git a/adapters/trafficgate/trafficgatetest/supplemental/status-404.json b/adapters/trafficgate/trafficgatetest/supplemental/status-404.json
new file mode 100644
index 00000000..90bbd764
--- /dev/null
+++ b/adapters/trafficgate/trafficgatetest/supplemental/status-404.json
@@ -0,0 +1,87 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "1",
+            "host": "example"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.bc-plugin.com/?c=o&m=rtb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "placementId": "1",
+                  "host": "example"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 404,
+        "body": {}
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Error response with status 404",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/config/config_test.go b/config/config_test.go
index 4f9bc44c..5a7d3899 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -137,6 +137,7 @@ func TestDefaults(t *testing.T) {
 	cmpBools(t, "account_required", cfg.AccountRequired, false)
 	cmpInts(t, "metrics.influxdb.collection_rate_seconds", cfg.Metrics.Influxdb.MetricSendInterval, 20)
 	cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, false)
+	cmpBools(t, "account_debug", cfg.Metrics.Disabled.AccountDebug, true)
 	cmpBools(t, "adapter_connections_metrics", cfg.Metrics.Disabled.AdapterConnectionMetrics, true)
 	cmpBools(t, "adapter_gdpr_request_blocked", cfg.Metrics.Disabled.AdapterGDPRRequestBlocked, false)
 	cmpStrings(t, "certificates_file", cfg.PemCertsFile, "")
@@ -339,6 +340,7 @@ metrics:
     metric_send_interval: 30
   disabled_metrics:
     account_adapter_details: true
+    account_debug: false
     adapter_connections_metrics: true
     adapter_gdpr_request_blocked: true
 adapters:
@@ -611,6 +613,7 @@ func TestFullConfig(t *testing.T) {
 	cmpBools(t, "account_required", cfg.AccountRequired, true)
 	cmpBools(t, "auto_gen_source_tid", cfg.AutoGenSourceTID, false)
 	cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, true)
+	cmpBools(t, "account_debug", cfg.Metrics.Disabled.AccountDebug, false)
 	cmpBools(t, "adapter_connections_metrics", cfg.Metrics.Disabled.AdapterConnectionMetrics, true)
 	cmpBools(t, "adapter_gdpr_request_blocked", cfg.Metrics.Disabled.AdapterGDPRRequestBlocked, true)
 	cmpStrings(t, "certificates_file", cfg.PemCertsFile, "/etc/ssl/cert.pem")
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index aed9ed73..efbb57a4 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -239,6 +239,8 @@ func TestCookieSyncHandle(t *testing.T) {
 		mockAnalytics := MockAnalytics{}
 		test.setAnalyticsExpectations(&mockAnalytics)
 
+		fakeAccountFetcher := FakeAccountsFetcher{}
+
 		request := httptest.NewRequest("POST", "/cookiesync", test.givenBody)
 		if test.givenCookie != nil {
 			request.AddCookie(test.givenCookie.ToHTTPCookie(24 * time.Hour))
@@ -256,8 +258,9 @@ func TestCookieSyncHandle(t *testing.T) {
 				},
 				ccpaEnforce: true,
 			},
-			metrics:      &mockMetrics,
-			pbsAnalytics: &mockAnalytics,
+			metrics:         &mockMetrics,
+			pbsAnalytics:    &mockAnalytics,
+			accountsFetcher: &fakeAccountFetcher,
 		}
 		endpoint.Handle(writer, request, nil)
 
@@ -787,122 +790,226 @@ func TestCookieSyncParseRequest(t *testing.T) {
 }
 
 func TestSetLimit(t *testing.T) {
-	coopSync := true
+	intNegative1 := -1
+	int20 := 20
+	int30 := 30
+	int40 := 40
 
 	testCases := []struct {
 		description     string
 		givenRequest    cookieSyncRequest
-		givenConfig     config.UserSync
-		expectedError   string
+		givenAccount    *config.Account
 		expectedRequest cookieSyncRequest
 	}{
 		{
-			description: "Test that Max Limit and Default Coop Sync are Applied",
+			description: "Default Limit is Applied (request limit = 0)",
 			givenRequest: cookieSyncRequest{
-				Account: "TestAccount",
-				Limit:   42,
+				Limit: 0,
 			},
-			givenConfig: config.UserSync{
-				Cooperative: config.UserSyncCooperative{
-					PriorityGroups: [][]string{{"a", "b", "c"}},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					DefaultLimit: &int20,
 				},
 			},
 			expectedRequest: cookieSyncRequest{
-				Account:         "TestAccount",
-				Limit:           30,
-				CooperativeSync: &coopSync,
+				Limit: 20,
 			},
-			expectedError: "",
 		},
 		{
-			description: "Test that Default Limit is Applied",
+			description: "Default Limit is Not Applied (default limit not set)",
 			givenRequest: cookieSyncRequest{
-				Account: "TestAccount",
+				Limit: 0,
 			},
-			givenConfig: config.UserSync{
-				Cooperative: config.UserSyncCooperative{
-					PriorityGroups: [][]string{{"a", "b", "c"}},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					DefaultLimit: nil,
 				},
 			},
 			expectedRequest: cookieSyncRequest{
-				Account:         "TestAccount",
-				Limit:           20,
-				CooperativeSync: &coopSync,
+				Limit: 0,
 			},
-			expectedError: "",
 		},
 		{
-			description: "Test that max limit is applied when given limit is zero",
+			description: "Default Limit is Not Applied (request limit > 0)",
 			givenRequest: cookieSyncRequest{
-				Account: "ZeroLimitAccount",
+				Limit: 10,
 			},
-			givenConfig: config.UserSync{
-				Cooperative: config.UserSyncCooperative{
-					PriorityGroups: [][]string{{"a", "b", "c"}},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					DefaultLimit: &int20,
 				},
 			},
 			expectedRequest: cookieSyncRequest{
-				Account:         "ZeroLimitAccount",
-				Limit:           30,
-				CooperativeSync: &coopSync,
+				Limit: 10,
 			},
-			expectedError: "",
 		},
 		{
-			description: "Negative Value Check",
+			description: "Max Limit is Applied (request limit <= 0)",
 			givenRequest: cookieSyncRequest{
-				Account: "NegativeLimitAccount",
+				Limit: 0,
 			},
-			givenConfig: config.UserSync{
-				Cooperative: config.UserSyncCooperative{
-					PriorityGroups: [][]string{{"a", "b", "c"}},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					MaxLimit: &int30,
 				},
 			},
 			expectedRequest: cookieSyncRequest{
-				Account:         "NegativeLimitAccount",
-				Limit:           0,
-				CooperativeSync: &coopSync,
+				Limit: 30,
 			},
-			expectedError: "",
 		},
 		{
-			description: "Error Handler",
+			description: "Max Limit is Applied (0 < max < limit)",
 			givenRequest: cookieSyncRequest{
-				Account: "DisabledAccount",
+				Limit: 40,
 			},
-			givenConfig: config.UserSync{
-				Cooperative: config.UserSyncCooperative{
-					PriorityGroups: [][]string{{"a", "b", "c"}},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					MaxLimit: &int30,
 				},
 			},
 			expectedRequest: cookieSyncRequest{
-				Account: "DisabledAccount",
+				Limit: 30,
+			},
+		},
+		{
+			description: "Max Limit is Not Applied (max not set)",
+			givenRequest: cookieSyncRequest{
+				Limit: 10,
+			},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					MaxLimit: nil,
+				},
+			},
+			expectedRequest: cookieSyncRequest{
+				Limit: 10,
+			},
+		},
+		{
+			description: "Max Limit is Not Applied (0 < limit < max)",
+			givenRequest: cookieSyncRequest{
+				Limit: 10,
+			},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					MaxLimit: &int30,
+				},
+			},
+			expectedRequest: cookieSyncRequest{
+				Limit: 10,
+			},
+		},
+		{
+			description: "Max Limit is Applied After applying the default",
+			givenRequest: cookieSyncRequest{
+				Limit: 0,
+			},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					DefaultLimit: &int40,
+					MaxLimit:     &int30,
+				},
+			},
+			expectedRequest: cookieSyncRequest{
+				Limit: 30,
+			},
+		},
+		{
+			description: "Negative Value Check",
+			givenRequest: cookieSyncRequest{
+				Limit: 0,
+			},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					DefaultLimit: &intNegative1,
+					MaxLimit:     &intNegative1,
+				},
+			},
+			expectedRequest: cookieSyncRequest{
+				Limit: 0,
 			},
-			expectedError: errCookieSyncAccountBlocked.Error(),
 		},
 	}
 
 	for _, test := range testCases {
-		endpoint := cookieSyncEndpoint{
-			config: &config.Configuration{
-				UserSync: test.givenConfig,
+		endpoint := cookieSyncEndpoint{}
+		request := endpoint.setLimit(test.givenRequest, test.givenAccount.CookieSync)
+		assert.Equal(t, test.expectedRequest, request, test.description)
+	}
+}
+
+func TestSetCooperativeSync(t *testing.T) {
+	coopSyncFalse := false
+	coopSyncTrue := true
+
+	testCases := []struct {
+		description     string
+		givenRequest    cookieSyncRequest
+		givenAccount    *config.Account
+		expectedRequest cookieSyncRequest
+	}{
+		{
+			description: "Request coop sync unmodified - request sync nil & default sync nil",
+			givenRequest: cookieSyncRequest{
+				CooperativeSync: nil,
 			},
-			accountsFetcher: FakeAccountsFetcher{AccountData: map[string]json.RawMessage{
-				"TestAccount":          json.RawMessage(`{"cookie_sync": {"default_limit": 20, "max_limit": 30, "default_coop_sync": true}}`),
-				"DisabledAccount":      json.RawMessage(`{"disabled":true}`),
-				"ZeroLimitAccount":     json.RawMessage(`{"cookie_sync": {"default_limit": 0, "max_limit": 30, "default_coop_sync": true}}`),
-				"NegativeLimitAccount": json.RawMessage(`{"cookie_sync": {"default_limit": -1, "max_limit": -1, "default_coop_sync": true}}`),
-			}},
-		}
-		assert.NoError(t, endpoint.config.MarshalAccountDefaults())
-		request, err := endpoint.setLimit(test.givenRequest)
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					DefaultCoopSync: nil,
+				},
+			},
+			expectedRequest: cookieSyncRequest{
+				CooperativeSync: nil,
+			},
+		},
+		{
+			description: "Request coop sync set to default - request sync nil & default sync not nil",
+			givenRequest: cookieSyncRequest{
+				CooperativeSync: nil,
+			},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					DefaultCoopSync: &coopSyncTrue,
+				},
+			},
+			expectedRequest: cookieSyncRequest{
+				CooperativeSync: &coopSyncTrue,
+			},
+		},
+		{
+			description: "Request coop sync unmodified - request sync not nil & default sync nil",
+			givenRequest: cookieSyncRequest{
+				CooperativeSync: &coopSyncTrue,
+			},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					DefaultCoopSync: nil,
+				},
+			},
+			expectedRequest: cookieSyncRequest{
+				CooperativeSync: &coopSyncTrue,
+			},
+		},
+		{
+			description: "Request coop sync unmodified - request sync not nil & default sync not nil",
+			givenRequest: cookieSyncRequest{
+				CooperativeSync: &coopSyncFalse,
+			},
+			givenAccount: &config.Account{
+				CookieSync: config.CookieSync{
+					DefaultCoopSync: &coopSyncTrue,
+				},
+			},
+			expectedRequest: cookieSyncRequest{
+				CooperativeSync: &coopSyncFalse,
+			},
+		},
+	}
 
-		if test.expectedError == "" {
-			assert.NoError(t, err, test.description)
-			assert.Equal(t, test.expectedRequest, request, test.description)
-		} else {
-			assert.EqualError(t, err, test.expectedError, test.description)
-		}
+	for _, test := range testCases {
+		endpoint := cookieSyncEndpoint{}
+		request := endpoint.setCooperativeSync(test.givenRequest, test.givenAccount.CookieSync)
+		assert.Equal(t, test.expectedRequest, request, test.description)
 	}
 }
 
diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go
index eb50b57e..02f11bc5 100644
--- a/endpoints/openrtb2/auction_benchmark_test.go
+++ b/endpoints/openrtb2/auction_benchmark_test.go
@@ -2,7 +2,6 @@ package openrtb2
 
 import (
 	"bytes"
-	"context"
 	"fmt"
 	"io/ioutil"
 	"net/http"
@@ -11,11 +10,11 @@ import (
 	"testing"
 	"time"
 
-	"github.com/prebid/go-gdpr/vendorlist"
 	analyticsConf "github.com/prebid/prebid-server/analytics/config"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/currency"
 	"github.com/prebid/prebid-server/exchange"
+	"github.com/prebid/prebid-server/gdpr"
 	metricsConfig "github.com/prebid/prebid-server/metrics/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
@@ -78,6 +77,13 @@ func BenchmarkOpenrtbEndpoint(b *testing.B) {
 		b.Fatal("unable to build adapters")
 	}
 
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &fakePermissions{},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+
 	exchange := exchange.NewExchange(
 		adapters,
 		nil,
@@ -85,7 +91,8 @@ func BenchmarkOpenrtbEndpoint(b *testing.B) {
 		map[string]usersync.Syncer{},
 		nilMetrics,
 		infos,
-		func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { return nil, nil },
+		gdprPermsBuilder,
+		tcf2ConfigBuilder,
 		currency.NewRateConverter(&http.Client{}, "", time.Duration(0)),
 		empty_fetcher.EmptyFetcher{},
 	)
diff --git a/endpoints/openrtb2/test_utils.go b/endpoints/openrtb2/test_utils.go
index d42ba22f..5b76345f 100644
--- a/endpoints/openrtb2/test_utils.go
+++ b/endpoints/openrtb2/test_utils.go
@@ -1101,13 +1101,21 @@ func buildTestExchange(testCfg *testConfigValues, adapterMap map[openrtb_ext.Bid
 	mockCurrencyConverter := currency.NewRateConverter(mockCurrencyRatesServer.Client(), mockCurrencyRatesServer.URL, time.Second)
 	mockCurrencyConverter.Run()
 
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &fakePermissions{},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+
 	return exchange.NewExchange(adapterMap,
 		&wellBehavedCache{},
 		cfg,
 		nil,
 		met,
 		bidderInfos,
-		gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker),
+		gdprPermsBuilder,
+		tcf2ConfigBuilder,
 		mockCurrencyConverter,
 		mockFetcher,
 	), mockBidServersArray
@@ -1254,3 +1262,36 @@ func readFile(t *testing.T, filename string) []byte {
 	}
 	return data
 }
+
+type fakePermissionsBuilder struct {
+	permissions gdpr.Permissions
+}
+
+func (fpb fakePermissionsBuilder) Builder(gdpr.TCF2ConfigReader) gdpr.Permissions {
+	return fpb.permissions
+}
+
+type fakeTCF2ConfigBuilder struct {
+	cfg gdpr.TCF2ConfigReader
+}
+
+func (fcr fakeTCF2ConfigBuilder) Builder(hostConfig config.TCF2, accountConfig config.AccountGDPR) gdpr.TCF2ConfigReader {
+	return fcr.cfg
+}
+
+type fakePermissions struct {
+}
+
+func (p *fakePermissions) HostCookiesAllowed(ctx context.Context, gdpr gdpr.Signal, consent string) (bool, error) {
+	return true, nil
+}
+
+func (p *fakePermissions) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdpr gdpr.Signal, consent string) (bool, error) {
+	return true, nil
+}
+
+func (p *fakePermissions) AuctionActivitiesAllowed(ctx context.Context, bidderCoreName openrtb_ext.BidderName, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, aliasGVLIDs map[string]uint16) (permissions gdpr.AuctionPermissions, err error) {
+	return gdpr.AuctionPermissions{
+		AllowBidRequest: true,
+	}, nil
+}
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index e06ec9d2..97ad934b 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -17,7 +17,6 @@ import (
 
 	"github.com/buger/jsonparser"
 	"github.com/mxmCherry/openrtb/v15/openrtb2"
-	"github.com/prebid/go-gdpr/vendorlist"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 	jsonpatch "gopkg.in/evanphx/json-patch.v4"
@@ -66,8 +65,17 @@ func TestNewExchange(t *testing.T) {
 	}
 
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	vendorListFetcher := func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { return nil, nil }
-	e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, vendorListFetcher, currencyConverter, nilCategoryFetcher{}).(*exchange)
+
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+
+	e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, tcf2ConfigBuilder, currencyConverter, nilCategoryFetcher{}).(*exchange)
 	for _, bidderName := range knownAdapters {
 		if _, ok := e.adapterMap[bidderName]; !ok {
 			t.Errorf("NewExchange produced an Exchange without bidder %s", bidderName)
@@ -115,8 +123,17 @@ func TestCharacterEscape(t *testing.T) {
 	}
 
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	vendorListFetcher := func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { return nil, nil }
-	e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, vendorListFetcher, currencyConverter, nilCategoryFetcher{}).(*exchange)
+
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+
+	e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, tcf2ConfigBuilder, currencyConverter, nilCategoryFetcher{}).(*exchange)
 
 	// 	3) Build all the parameters e.buildBidResponse(ctx.Background(), liveA... ) needs
 	//liveAdapters []openrtb_ext.BidderName,
@@ -311,7 +328,14 @@ func TestDebugBehaviour(t *testing.T) {
 
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.NilMetricsEngine{}
-	e.vendorListFetcher = gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
+	e.gdprPermsBuilder = fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	e.tcf2ConfigBuilder = fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.categoriesFetcher = categoriesFetcher
 
@@ -333,12 +357,10 @@ func TestDebugBehaviour(t *testing.T) {
 		}
 
 		auctionRequest := AuctionRequest{
-			BidRequestWrapper:      &openrtb_ext.RequestWrapper{BidRequest: bidRequest},
-			Account:                config.Account{DebugAllow: test.debugData.accountLevelDebugAllowed},
-			UserSyncs:              &emptyUsersync{},
-			StartTime:              time.Now(),
-			GDPRPermissionsBuilder: mockGDPRPermissionsBuilder,
-			TCF2ConfigBuilder:      mockTCF2ConfigBuilder,
+			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: bidRequest},
+			Account:           config.Account{DebugAllow: test.debugData.accountLevelDebugAllowed},
+			UserSyncs:         &emptyUsersync{},
+			StartTime:         time.Now(),
 		}
 		if test.generateWarnings {
 			var errL []error
@@ -470,7 +492,14 @@ func TestTwoBiddersDebugDisabledAndEnabled(t *testing.T) {
 	e := new(exchange)
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.NilMetricsEngine{}
-	e.vendorListFetcher = gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
+	e.gdprPermsBuilder = fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	e.tcf2ConfigBuilder = fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.categoriesFetcher = categoriesFetcher
 
@@ -493,12 +522,10 @@ func TestTwoBiddersDebugDisabledAndEnabled(t *testing.T) {
 		bidRequest.Ext = json.RawMessage(`{"prebid":{"debug":true}}`)
 
 		auctionRequest := AuctionRequest{
-			BidRequestWrapper:      &openrtb_ext.RequestWrapper{BidRequest: bidRequest},
-			Account:                config.Account{DebugAllow: true},
-			UserSyncs:              &emptyUsersync{},
-			StartTime:              time.Now(),
-			GDPRPermissionsBuilder: mockGDPRPermissionsBuilder,
-			TCF2ConfigBuilder:      mockTCF2ConfigBuilder,
+			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: bidRequest},
+			Account:           config.Account{DebugAllow: true},
+			UserSyncs:         &emptyUsersync{},
+			StartTime:         time.Now(),
 		}
 
 		e.adapterMap = map[openrtb_ext.BidderName]AdaptedBidder{
@@ -628,7 +655,14 @@ func TestOverrideWithCustomCurrency(t *testing.T) {
 	e := new(exchange)
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.NilMetricsEngine{}
-	e.vendorListFetcher = gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
+	e.gdprPermsBuilder = fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	e.tcf2ConfigBuilder = fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
 	e.currencyConverter = mockCurrencyConverter
 	e.categoriesFetcher = categoriesFetcher
 	e.bidIDGenerator = &mockBidIDGenerator{false, false}
@@ -667,11 +701,9 @@ func TestOverrideWithCustomCurrency(t *testing.T) {
 		mockBidRequest.Cur = []string{test.in.bidRequestCurrency}
 
 		auctionRequest := AuctionRequest{
-			BidRequestWrapper:      &openrtb_ext.RequestWrapper{BidRequest: mockBidRequest},
-			Account:                config.Account{},
-			UserSyncs:              &emptyUsersync{},
-			GDPRPermissionsBuilder: mockGDPRPermissionsBuilder,
-			TCF2ConfigBuilder:      mockTCF2ConfigBuilder,
+			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: mockBidRequest},
+			Account:           config.Account{},
+			UserSyncs:         &emptyUsersync{},
 		}
 
 		// Run test
@@ -720,13 +752,18 @@ func TestAdapterCurrency(t *testing.T) {
 	mockBidder := &mockBidder{}
 	mockBidder.On("MakeRequests", mock.Anything, mock.Anything).Return([]*adapters.RequestData(nil), []error(nil))
 
-	vendorListFetcher := gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
-
 	// Initialize Real Exchange
 	e := exchange{
-		cache:             &wellBehavedCache{},
-		me:                &metricsConf.NilMetricsEngine{},
-		vendorListFetcher: vendorListFetcher,
+		cache: &wellBehavedCache{},
+		me:    &metricsConf.NilMetricsEngine{},
+		gdprPermsBuilder: fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+			},
+		}.Builder,
+		tcf2ConfigBuilder: fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+		}.Builder,
 		currencyConverter: currencyConverter,
 		categoriesFetcher: nilCategoryFetcher{},
 		bidIDGenerator:    &mockBidIDGenerator{false, false},
@@ -753,11 +790,9 @@ func TestAdapterCurrency(t *testing.T) {
 
 	// Run Auction
 	auctionRequest := AuctionRequest{
-		BidRequestWrapper:      &openrtb_ext.RequestWrapper{BidRequest: request},
-		Account:                config.Account{},
-		UserSyncs:              &emptyUsersync{},
-		GDPRPermissionsBuilder: mockGDPRPermissionsBuilder,
-		TCF2ConfigBuilder:      mockTCF2ConfigBuilder,
+		BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: request},
+		Account:           config.Account{},
+		UserSyncs:         &emptyUsersync{},
 	}
 	response, err := e.HoldAuction(context.Background(), auctionRequest, &DebugLog{})
 	assert.NoError(t, err)
@@ -1118,7 +1153,14 @@ func TestReturnCreativeEndToEnd(t *testing.T) {
 	}
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.NilMetricsEngine{}
-	e.vendorListFetcher = gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
+	e.gdprPermsBuilder = fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	e.tcf2ConfigBuilder = fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.categoriesFetcher = categoriesFetcher
 	e.bidIDGenerator = &mockBidIDGenerator{false, false}
@@ -1140,11 +1182,9 @@ func TestReturnCreativeEndToEnd(t *testing.T) {
 			mockBidRequest.Ext = test.inExt
 
 			auctionRequest := AuctionRequest{
-				BidRequestWrapper:      &openrtb_ext.RequestWrapper{BidRequest: mockBidRequest},
-				Account:                config.Account{},
-				UserSyncs:              &emptyUsersync{},
-				GDPRPermissionsBuilder: mockGDPRPermissionsBuilder,
-				TCF2ConfigBuilder:      mockTCF2ConfigBuilder,
+				BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: mockBidRequest},
+				Account:           config.Account{},
+				UserSyncs:         &emptyUsersync{},
 			}
 
 			// Run test
@@ -1218,8 +1258,17 @@ func TestGetBidCacheInfoEndToEnd(t *testing.T) {
 	}
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	pbc := pbc.NewClient(&http.Client{}, &cfg.CacheURL, &cfg.ExtCacheURL, testEngine)
-	vendorListFetcher := gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
-	e := NewExchange(adapters, pbc, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, vendorListFetcher, currencyConverter, nilCategoryFetcher{}).(*exchange)
+
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+
+	e := NewExchange(adapters, pbc, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, tcf2ConfigBuilder, currencyConverter, nilCategoryFetcher{}).(*exchange)
 	// 	3) Build all the parameters e.buildBidResponse(ctx.Background(), liveA... ) needs
 	liveAdapters := []openrtb_ext.BidderName{bidderName}
 
@@ -1411,7 +1460,7 @@ func TestBidReturnsCreative(t *testing.T) {
 	}
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.NilMetricsEngine{}
-	e.vendorListFetcher = gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
+
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 
 	//Run tests
@@ -1573,8 +1622,17 @@ func TestBidResponseCurrency(t *testing.T) {
 	}
 
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	vendorListFetcher := gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
-	e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, vendorListFetcher, currencyConverter, nilCategoryFetcher{}).(*exchange)
+
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+
+	e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, tcf2ConfigBuilder, currencyConverter, nilCategoryFetcher{}).(*exchange)
 
 	liveAdapters := make([]openrtb_ext.BidderName, 1)
 	liveAdapters[0] = "appnexus"
@@ -1713,8 +1771,16 @@ func TestBidResponseImpExtInfo(t *testing.T) {
 	cfg := &config.Configuration{Adapters: make(map[string]config.Adapter, 1)}
 	cfg.Adapters["appnexus"] = config.Adapter{Endpoint: "http://ib.adnxs.com"}
 
-	vendorListFetcher := gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
-	e := NewExchange(nil, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, nil, vendorListFetcher, nil, nilCategoryFetcher{}).(*exchange)
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+
+	e := NewExchange(nil, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, nil, gdprPermsBuilder, tcf2ConfigBuilder, nil, nilCategoryFetcher{}).(*exchange)
 
 	liveAdapters := make([]openrtb_ext.BidderName, 1)
 	liveAdapters[0] = "appnexus"
@@ -1807,16 +1873,23 @@ func TestRaceIntegration(t *testing.T) {
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 
 	auctionRequest := AuctionRequest{
-		BidRequestWrapper:      &openrtb_ext.RequestWrapper{BidRequest: getTestBuildRequest(t)},
-		Account:                config.Account{},
-		UserSyncs:              &emptyUsersync{},
-		GDPRPermissionsBuilder: mockGDPRPermissionsBuilder,
-		TCF2ConfigBuilder:      mockTCF2ConfigBuilder,
+		BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: getTestBuildRequest(t)},
+		Account:           config.Account{},
+		UserSyncs:         &emptyUsersync{},
 	}
 
 	debugLog := DebugLog{}
-	vendorListFetcher := gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
-	ex := NewExchange(adapters, &wellBehavedCache{}, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, vendorListFetcher, currencyConverter, &nilCategoryFetcher{}).(*exchange)
+
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	tcf2CfgBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+
+	ex := NewExchange(adapters, &wellBehavedCache{}, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, tcf2CfgBuilder, currencyConverter, &nilCategoryFetcher{}).(*exchange)
 	_, err = ex.HoldAuction(context.Background(), auctionRequest, &debugLog)
 	if err != nil {
 		t.Errorf("HoldAuction returned unexpected error: %v", err)
@@ -1908,8 +1981,17 @@ func TestPanicRecovery(t *testing.T) {
 	}
 
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	vendorListFetcher := gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
-	e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, vendorListFetcher, currencyConverter, nilCategoryFetcher{}).(*exchange)
+
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+
+	e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, tcf2ConfigBuilder, currencyConverter, nilCategoryFetcher{}).(*exchange)
 
 	chBids := make(chan *bidResponseWrapper, 1)
 	panicker := func(bidderRequest BidderRequest, conversions currency.Conversions) {
@@ -1982,8 +2064,15 @@ func TestPanicRecoveryHighLevel(t *testing.T) {
 		t.Errorf("Failed to create a category Fetcher: %v", error)
 	}
 
-	vendorListFetcher := gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
-	e := NewExchange(adapters, &mockCache{}, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, vendorListFetcher, currencyConverter, categoriesFetcher).(*exchange)
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+	e := NewExchange(adapters, &mockCache{}, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, tcf2ConfigBuilder, currencyConverter, categoriesFetcher).(*exchange)
 
 	e.adapterMap[openrtb_ext.BidderBeachfront] = panicingAdapter{}
 	e.adapterMap[openrtb_ext.BidderAppnexus] = panicingAdapter{}
@@ -2017,11 +2106,9 @@ func TestPanicRecoveryHighLevel(t *testing.T) {
 	}
 
 	auctionRequest := AuctionRequest{
-		BidRequestWrapper:      &openrtb_ext.RequestWrapper{BidRequest: request},
-		Account:                config.Account{},
-		UserSyncs:              &emptyUsersync{},
-		GDPRPermissionsBuilder: mockGDPRPermissionsBuilder,
-		TCF2ConfigBuilder:      mockTCF2ConfigBuilder,
+		BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: request},
+		Account:           config.Account{},
+		UserSyncs:         &emptyUsersync{},
 	}
 	debugLog := DebugLog{}
 	_, err = e.HoldAuction(context.Background(), auctionRequest, &debugLog)
@@ -2133,9 +2220,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 			EventsEnabled: spec.EventsEnabled,
 			DebugAllow:    true,
 		},
-		UserSyncs:              mockIdFetcher(spec.IncomingRequest.Usersyncs),
-		GDPRPermissionsBuilder: mockGDPRPermissionsBuilder,
-		TCF2ConfigBuilder:      mockTCF2ConfigBuilder,
+		UserSyncs: mockIdFetcher(spec.IncomingRequest.Usersyncs),
 	}
 	if spec.StartTime > 0 {
 		auctionRequest.StartTime = time.Unix(0, spec.StartTime*1e+6)
@@ -2262,7 +2347,14 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 		t.Fatalf("Failed to create a category Fetcher: %v", error)
 	}
 
-	vendorListFetcher := gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(privacyConfig.GDPR.TCF2, config.AccountGDPR{}),
+	}.Builder
 
 	bidderToSyncerKey := map[string]string{}
 	for _, bidderName := range openrtb_ext.CoreBidderNames() {
@@ -2279,9 +2371,10 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 		me:                metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.CoreBidderNames(), nil),
 		cache:             &wellBehavedCache{},
 		cacheTime:         0,
-		vendorListFetcher: vendorListFetcher,
 		currencyConverter: currency.NewRateConverter(&http.Client{}, "", time.Duration(0)),
 		gdprDefaultValue:  gdprDefaultValue,
+		gdprPermsBuilder:  gdprPermsBuilder,
+		tcf2ConfigBuilder: tcf2ConfigBuilder,
 		privacyConfig:     privacyConfig,
 		categoriesFetcher: categoriesFetcher,
 		bidderInfo:        bidderInfos,
@@ -3695,10 +3788,17 @@ func TestStoredAuctionResponses(t *testing.T) {
 	e := new(exchange)
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.NilMetricsEngine{}
-	e.vendorListFetcher = gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
 	e.categoriesFetcher = categoriesFetcher
 	e.bidIDGenerator = &mockBidIDGenerator{false, false}
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
+	e.gdprPermsBuilder = fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	e.tcf2ConfigBuilder = fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
 
 	// Define mock incoming bid requeset
 	mockBidRequest := &openrtb2.BidRequest{
@@ -3749,10 +3849,7 @@ func TestStoredAuctionResponses(t *testing.T) {
 			Account:                config.Account{},
 			UserSyncs:              &emptyUsersync{},
 			StoredAuctionResponses: test.storedAuctionResp,
-			GDPRPermissionsBuilder: mockGDPRPermissionsBuilder,
-			TCF2ConfigBuilder:      mockTCF2ConfigBuilder,
 		}
-
 		// Run test
 		outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, &DebugLog{})
 		if test.errorExpected {
@@ -3924,10 +4021,17 @@ func TestAuctionDebugEnabled(t *testing.T) {
 	e := new(exchange)
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.NilMetricsEngine{}
-	e.vendorListFetcher = gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker)
 	e.categoriesFetcher = categoriesFetcher
 	e.bidIDGenerator = &mockBidIDGenerator{false, false}
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
+	e.gdprPermsBuilder = fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	e.tcf2ConfigBuilder = fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
 
 	ctx := context.Background()
 
@@ -3937,13 +4041,11 @@ func TestAuctionDebugEnabled(t *testing.T) {
 	}
 
 	auctionRequest := AuctionRequest{
-		BidRequestWrapper:      &openrtb_ext.RequestWrapper{BidRequest: bidRequest},
-		Account:                config.Account{DebugAllow: false},
-		UserSyncs:              &emptyUsersync{},
-		StartTime:              time.Now(),
-		RequestType:            metrics.ReqTypeORTB2Web,
-		GDPRPermissionsBuilder: mockGDPRPermissionsBuilder,
-		TCF2ConfigBuilder:      mockTCF2ConfigBuilder,
+		BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: bidRequest},
+		Account:           config.Account{DebugAllow: false},
+		UserSyncs:         &emptyUsersync{},
+		StartTime:         time.Now(),
+		RequestType:       metrics.ReqTypeORTB2Web,
 	}
 
 	debugLog := &DebugLog{DebugOverride: true, DebugEnabledOrOverridden: true}
@@ -4238,13 +4340,3 @@ func (m *mockBidder) MakeBids(internalRequest *openrtb2.BidRequest, externalRequ
 	args := m.Called(internalRequest, externalRequest, response)
 	return args.Get(0).(*adapters.BidderResponse), args.Get(1).([]error)
 }
-
-func mockGDPRPermissionsBuilder(cfg config.GDPR, tcf2Config gdpr.TCF2ConfigReader, vendorIDs map[openrtb_ext.BidderName]uint16, fetcher gdpr.VendorListFetcher) gdpr.Permissions {
-	return &permissionsMock{allowAllBidders: true}
-}
-
-func mockTCF2ConfigBuilder(hostConfig config.TCF2, accountConfig config.AccountGDPR) gdpr.TCF2ConfigReader {
-	return &config.TCF2{
-		Enabled: hostConfig.Enabled,
-	}
-}
diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go
index 18cc6fc4..6aeb7f69 100644
--- a/exchange/targeting_test.go
+++ b/exchange/targeting_test.go
@@ -83,12 +83,22 @@ func runTargetingAuction(t *testing.T, mockBids map[openrtb_ext.BidderName][]*op
 		t.Errorf("Failed to create a category Fetcher: %v", error)
 	}
 
+	gdprPermsBuilder := fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+		},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+
 	ex := &exchange{
 		adapterMap:        buildAdapterMap(mockBids, server.URL, server.Client()),
 		me:                &metricsConfig.NilMetricsEngine{},
 		cache:             &wellBehavedCache{},
 		cacheTime:         time.Duration(0),
-		vendorListFetcher: gdpr.NewVendorListFetcher(context.Background(), config.GDPR{}, &http.Client{}, gdpr.VendorListURLMaker),
+		gdprPermsBuilder:  gdprPermsBuilder,
+		tcf2ConfigBuilder: tcf2ConfigBuilder,
 		currencyConverter: currency.NewRateConverter(&http.Client{}, "", time.Duration(0)),
 		gdprDefaultValue:  gdpr.SignalYes,
 		categoriesFetcher: categoriesFetcher,
@@ -108,11 +118,9 @@ func runTargetingAuction(t *testing.T, mockBids map[openrtb_ext.BidderName][]*op
 	}
 
 	auctionRequest := AuctionRequest{
-		BidRequestWrapper:      &openrtb_ext.RequestWrapper{BidRequest: req},
-		Account:                config.Account{},
-		UserSyncs:              &emptyUsersync{},
-		GDPRPermissionsBuilder: mockGDPRPermissionsBuilder,
-		TCF2ConfigBuilder:      mockTCF2ConfigBuilder,
+		BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
+		Account:           config.Account{},
+		UserSyncs:         &emptyUsersync{},
 	}
 
 	debugLog := DebugLog{}
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index f68223b2..1d12f766 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -55,6 +55,22 @@ func (p *permissionsMock) AuctionActivitiesAllowed(ctx context.Context, bidderCo
 	return permissions, p.activitiesError
 }
 
+type fakePermissionsBuilder struct {
+	permissions gdpr.Permissions
+}
+
+func (fpb fakePermissionsBuilder) Builder(gdpr.TCF2ConfigReader) gdpr.Permissions {
+	return fpb.permissions
+}
+
+type fakeTCF2ConfigBuilder struct {
+	cfg gdpr.TCF2ConfigReader
+}
+
+func (fcr fakeTCF2ConfigBuilder) Builder(hostConfig config.TCF2, accountConfig config.AccountGDPR) gdpr.TCF2ConfigReader {
+	return fcr.cfg
+}
+
 func assertReq(t *testing.T, bidderRequests []BidderRequest,
 	applyCOPPA bool, consentedVendors map[string]bool) {
 	// assert individual bidder requests
@@ -495,9 +511,20 @@ func TestCleanOpenRTBRequests(t *testing.T) {
 	}
 
 	for _, test := range testCases {
+
 		metricsMock := metrics.MetricsEngineMock{}
 		bidderToSyncerKey := map[string]string{}
-		bidderRequests, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, bidderToSyncerKey, &metricsMock, gdpr.SignalNo, gdpr.AlwaysAllow{}, privacyConfig, &config.TCF2{})
+
+		gdprPermsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+			},
+		}.Builder
+		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+		}.Builder
+
+		bidderRequests, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, bidderToSyncerKey, &metricsMock, gdpr.SignalNo, privacyConfig, gdprPermsBuilder, tcf2ConfigBuilder)
 		if test.hasError {
 			assert.NotNil(t, err, "Error shouldn't be nil")
 		} else {
@@ -554,7 +581,17 @@ func TestCleanOpenRTBRequestsWithFPD(t *testing.T) {
 	for _, test := range testCases {
 		metricsMock := metrics.MetricsEngineMock{}
 		bidderToSyncerKey := map[string]string{}
-		bidderRequests, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, bidderToSyncerKey, &metricsMock, gdpr.SignalNo, gdpr.AlwaysAllow{}, config.Privacy{}, &config.TCF2{})
+
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+			},
+		}.Builder
+		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+		}.Builder
+
+		bidderRequests, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, bidderToSyncerKey, &metricsMock, gdpr.SignalNo, config.Privacy{}, gdprPermissionsBuilder, tcf2ConfigBuilder)
 		assert.Empty(t, err, "No errors should be returned")
 		for _, bidderRequest := range bidderRequests {
 			bidderName := bidderRequest.BidderName
@@ -806,11 +843,22 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) {
 	for _, test := range testCases {
 		metricsMock := metrics.MetricsEngineMock{}
 		bidderToSyncerKey := map[string]string{}
+
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+			},
+		}.Builder
+		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+		}.Builder
+
 		auctionReq := AuctionRequest{
 			BidRequestWrapper:  &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Imp: test.imps}},
 			UserSyncs:          &emptyUsersync{},
 			StoredBidResponses: test.storedBidResponses,
 		}
+
 		actualBidderRequests, _, err := cleanOpenRTBRequests(
 			context.Background(),
 			auctionReq,
@@ -818,9 +866,9 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) {
 			bidderToSyncerKey,
 			&metricsMock,
 			gdpr.SignalNo,
-			gdpr.AlwaysAllow{},
 			config.Privacy{},
-			&config.TCF2{})
+			gdprPermissionsBuilder,
+			tcf2ConfigBuilder)
 		assert.Empty(t, err, "No errors should be returned")
 		assert.Len(t, actualBidderRequests, len(test.expectedBidderRequests), "result len doesn't match for testCase %s", test.description)
 		for _, actualBidderRequest := range actualBidderRequests {
@@ -972,6 +1020,15 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) {
 			Account:           accountConfig,
 		}
 
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+			},
+		}.Builder
+		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(config.TCF2{}, accountConfig.GDPR),
+		}.Builder
+
 		bidderToSyncerKey := map[string]string{}
 		bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(
 			context.Background(),
@@ -980,9 +1037,9 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) {
 			bidderToSyncerKey,
 			&metrics.MetricsEngineMock{},
 			gdpr.SignalNo,
-			gdpr.AlwaysAllow{},
 			privacyConfig,
-			&config.TCF2{})
+			gdprPermissionsBuilder,
+			tcf2ConfigBuilder)
 		result := bidderRequests[0]
 
 		assert.Nil(t, errs)
@@ -1035,6 +1092,15 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) {
 			UserSyncs:         &emptyUsersync{},
 		}
 
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+			},
+		}.Builder
+		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+		}.Builder
+
 		privacyConfig := config.Privacy{
 			CCPA: config.CCPA{
 				Enforce: true,
@@ -1042,7 +1108,8 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) {
 		}
 		bidderToSyncerKey := map[string]string{}
 		metrics := metrics.MetricsEngineMock{}
-		_, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, bidderToSyncerKey, &metrics, gdpr.SignalNo, gdpr.AlwaysAllow{}, privacyConfig, &config.TCF2{})
+
+		_, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, bidderToSyncerKey, &metrics, gdpr.SignalNo, privacyConfig, gdprPermissionsBuilder, tcf2ConfigBuilder)
 
 		assert.ElementsMatch(t, []error{test.expectError}, errs, test.description)
 	}
@@ -1082,9 +1149,19 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) {
 			UserSyncs:         &emptyUsersync{},
 		}
 
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+			},
+		}.Builder
+		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+		}.Builder
+
 		bidderToSyncerKey := map[string]string{}
 		metrics := metrics.MetricsEngineMock{}
-		bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, bidderToSyncerKey, &metrics, gdpr.SignalNo, &gdpr.AlwaysAllow{}, config.Privacy{}, &config.TCF2{})
+
+		bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, bidderToSyncerKey, &metrics, gdpr.SignalNo, config.Privacy{}, gdprPermissionsBuilder, tcf2ConfigBuilder)
 		result := bidderRequests[0]
 
 		assert.Nil(t, errs)
@@ -1168,9 +1245,18 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) {
 			UserSyncs:         &emptyUsersync{},
 		}
 
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+			},
+		}.Builder
+		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+		}.Builder
+
 		bidderToSyncerKey := map[string]string{}
 		metrics := metrics.MetricsEngineMock{}
-		bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, bidderToSyncerKey, &metrics, gdpr.SignalNo, gdpr.AlwaysAllow{}, config.Privacy{}, &config.TCF2{})
+		bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, bidderToSyncerKey, &metrics, gdpr.SignalNo, config.Privacy{}, gdprPermissionsBuilder, tcf2ConfigBuilder)
 		if test.hasError == true {
 			assert.NotNil(t, errs)
 			assert.Len(t, bidderRequests, 0)
@@ -1225,9 +1311,19 @@ func TestCleanOpenRTBRequestsBidderParams(t *testing.T) {
 			UserSyncs:         &emptyUsersync{},
 		}
 
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+			},
+		}.Builder
+		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+		}.Builder
+
 		bidderToSyncerKey := map[string]string{}
 		metrics := metrics.MetricsEngineMock{}
-		bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, bidderToSyncerKey, &metrics, gdpr.SignalNo, gdpr.AlwaysAllow{}, config.Privacy{}, &config.TCF2{})
+
+		bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, bidderToSyncerKey, &metrics, gdpr.SignalNo, config.Privacy{}, gdprPermissionsBuilder, tcf2ConfigBuilder)
 		if test.hasError == true {
 			assert.NotNil(t, errs)
 			assert.Len(t, bidderRequests, 0)
@@ -1900,6 +1996,15 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) {
 			UserSyncs:         &emptyUsersync{},
 		}
 
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+			},
+		}.Builder
+		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+		}.Builder
+
 		privacyConfig := config.Privacy{
 			LMT: config.LMT{
 				Enforce: test.enforceLMT,
@@ -1908,7 +2013,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) {
 
 		bidderToSyncerKey := map[string]string{}
 		metrics := metrics.MetricsEngineMock{}
-		results, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, bidderToSyncerKey, &metrics, gdpr.SignalNo, &gdpr.AlwaysAllow{}, privacyConfig, &config.TCF2{})
+		results, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, bidderToSyncerKey, &metrics, gdpr.SignalNo, privacyConfig, gdprPermissionsBuilder, tcf2ConfigBuilder)
 		result := results[0]
 
 		assert.Nil(t, errs)
@@ -2114,6 +2219,21 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 			Account:           accountConfig,
 		}
 
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+				passGeo:         !test.gdprScrub,
+				passID:          !test.gdprScrub,
+				activitiesError: test.permissionsError,
+			},
+		}.Builder
+		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(
+				privacyConfig.GDPR.TCF2,
+				accountConfig.GDPR,
+			),
+		}.Builder
+
 		bidderToSyncerKey := map[string]string{}
 
 		gdprDefaultValue := gdpr.SignalYes
@@ -2128,9 +2248,9 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 			bidderToSyncerKey,
 			&metrics.MetricsEngineMock{},
 			gdprDefaultValue,
-			&permissionsMock{allowAllBidders: true, passGeo: !test.gdprScrub, passID: !test.gdprScrub, activitiesError: test.permissionsError},
 			privacyConfig,
-			gdpr.NewTCF2Config(privacyConfig.GDPR.TCF2, accountConfig.GDPR))
+			gdprPermissionsBuilder,
+			tcf2ConfigBuilder)
 		result := results[0]
 
 		if test.expectError {
@@ -2209,6 +2329,18 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) {
 			Account:           accountConfig,
 		}
 
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowedBidders:  test.gdprAllowedBidders,
+				passGeo:         true,
+				passID:          true,
+				activitiesError: nil,
+			},
+		}.Builder
+		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+			cfg: gdpr.NewTCF2Config(privacyConfig.GDPR.TCF2, accountConfig.GDPR),
+		}.Builder
+
 		metricsMock := metrics.MetricsEngineMock{}
 		metricsMock.Mock.On("RecordAdapterGDPRRequestBlocked", mock.Anything).Return()
 
@@ -2220,9 +2352,10 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) {
 			bidderToSyncerKey,
 			&metricsMock,
 			gdpr.SignalNo,
-			&permissionsMock{allowedBidders: test.gdprAllowedBidders, passGeo: true, passID: true, activitiesError: nil},
 			privacyConfig,
-			&privacyConfig.GDPR.TCF2)
+			gdprPermissionsBuilder,
+			tcf2ConfigBuilder,
+		)
 
 		// extract bidder name from each request in the results
 		bidders := []openrtb_ext.BidderName{}
@@ -2791,9 +2924,22 @@ func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) {
 		BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
 		UserSyncs:         &emptyUsersync{},
 	}
+
+	gdprPermissionsBuilder := fakePermissionsBuilder{
+		permissions: &permissionsMock{
+			allowAllBidders: true,
+			passGeo:         true,
+			passID:          true,
+			activitiesError: nil,
+		},
+	}.Builder
+	tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
+		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+	}.Builder
+
 	bidderToSyncerKey := map[string]string{}
 	metrics := metrics.MetricsEngineMock{}
-	bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, bidderToSyncerKey, &metrics, gdpr.SignalNo, gdpr.AlwaysAllow{}, config.Privacy{}, &config.TCF2{})
+	bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, bidderToSyncerKey, &metrics, gdpr.SignalNo, config.Privacy{}, gdprPermissionsBuilder, tcf2ConfigBuilder)
 
 	assert.Nil(t, errs)
 	assert.Len(t, bidderRequests, 2, "Bid request count is not 2")
diff --git a/metrics/go_metrics_test.go b/metrics/go_metrics_test.go
index dd2430d6..60cd101f 100644
--- a/metrics/go_metrics_test.go
+++ b/metrics/go_metrics_test.go
@@ -16,6 +16,7 @@ func TestNewMetrics(t *testing.T) {
 	m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{}, syncerKeys)
 
 	ensureContains(t, registry, "app_requests", m.AppRequestMeter)
+	ensureContains(t, registry, "debug_requests", m.DebugRequestMeter)
 	ensureContains(t, registry, "no_cookie_requests", m.NoCookieMeter)
 	ensureContains(t, registry, "request_time", m.RequestTimer)
 	ensureContains(t, registry, "amp_no_cookie_requests", m.AmpNoCookieMeter)
@@ -181,6 +182,72 @@ func TestRecordBidTypeDisabledConfig(t *testing.T) {
 	}
 }
 
+func TestRecordDebugRequest(t *testing.T) {
+	testCases := []struct {
+		description               string
+		givenDisabledMetrics      config.DisabledMetrics
+		givenDebugEnabledFlag     bool
+		givenPubID                string
+		expectedAccountDebugCount int64
+		expectedDebugCount        int64
+	}{
+		{
+			description: "Debug is enabled and account debug is enabled, both metrics should be updated",
+			givenDisabledMetrics: config.DisabledMetrics{
+				AccountAdapterDetails: true,
+				AccountDebug:          false,
+			},
+			givenDebugEnabledFlag:     true,
+			givenPubID:                "acct-id",
+			expectedAccountDebugCount: 1,
+			expectedDebugCount:        1,
+		},
+		{
+			description: "Debug and account debug are disabled, niether metrics should be updated",
+			givenDisabledMetrics: config.DisabledMetrics{
+				AccountAdapterDetails: true,
+				AccountDebug:          true,
+			},
+			givenDebugEnabledFlag:     false,
+			givenPubID:                "acct-id",
+			expectedAccountDebugCount: 0,
+			expectedDebugCount:        0,
+		},
+		{
+			description: "Debug is enabled and account debug is enabled, but unknown PubID leads to account debug being 0",
+			givenDisabledMetrics: config.DisabledMetrics{
+				AccountAdapterDetails: true,
+				AccountDebug:          false,
+			},
+			givenDebugEnabledFlag:     true,
+			givenPubID:                PublisherUnknown,
+			expectedAccountDebugCount: 0,
+			expectedDebugCount:        1,
+		},
+		{
+			description: "Debug is disabled, account debug is enabled, niether metric should update",
+			givenDisabledMetrics: config.DisabledMetrics{
+				AccountAdapterDetails: true,
+				AccountDebug:          false,
+			},
+			givenDebugEnabledFlag:     false,
+			givenPubID:                "acct-id",
+			expectedAccountDebugCount: 0,
+			expectedDebugCount:        0,
+		},
+	}
+	for _, test := range testCases {
+		registry := metrics.NewRegistry()
+		m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, test.givenDisabledMetrics, nil)
+
+		m.RecordDebugRequest(test.givenDebugEnabledFlag, test.givenPubID)
+		am := m.getAccountMetrics(test.givenPubID)
+
+		assert.Equal(t, test.expectedDebugCount, m.DebugRequestMeter.Count())
+		assert.Equal(t, test.expectedAccountDebugCount, am.debugRequestMeter.Count())
+	}
+}
+
 func TestRecordDNSTime(t *testing.T) {
 	testCases := []struct {
 		description         string
diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go
index c7b293da..f8f6cc7d 100644
--- a/metrics/prometheus/prometheus_test.go
+++ b/metrics/prometheus/prometheus_test.go
@@ -156,6 +156,54 @@ func TestRequestMetric(t *testing.T) {
 		})
 }
 
+func TestDebugRequestMetric(t *testing.T) {
+	testCases := []struct {
+		description                      string
+		givenDebugEnabledFlag            bool
+		givenAccountDebugMetricsDisabled bool
+		expectedAccountDebugCount        float64
+		expectedDebugCount               float64
+	}{
+		{
+			description:                      "Debug is enabled and account debug is enabled, both metrics should be updated",
+			givenDebugEnabledFlag:            true,
+			givenAccountDebugMetricsDisabled: false,
+			expectedDebugCount:               1,
+			expectedAccountDebugCount:        1,
+		},
+		{
+			description:                      "Debug and account debug are disabled, niether metrics should be updated",
+			givenDebugEnabledFlag:            false,
+			givenAccountDebugMetricsDisabled: true,
+			expectedDebugCount:               0,
+			expectedAccountDebugCount:        0,
+		},
+		{
+			description:                      "Debug is enabled but account debug is disabled, only non-account debug count should increment",
+			givenDebugEnabledFlag:            true,
+			givenAccountDebugMetricsDisabled: true,
+			expectedDebugCount:               1,
+			expectedAccountDebugCount:        0,
+		},
+		{
+			description:                      "Debug is disabled and account debug is enabled, niether metrics should increment",
+			givenDebugEnabledFlag:            false,
+			givenAccountDebugMetricsDisabled: false,
+			expectedDebugCount:               0,
+			expectedAccountDebugCount:        0,
+		},
+	}
+
+	for _, test := range testCases {
+		m := createMetricsForTesting()
+		m.metricsDisabled.AccountDebug = test.givenAccountDebugMetricsDisabled
+		m.RecordDebugRequest(test.givenDebugEnabledFlag, "acct-id")
+
+		assertCounterVecValue(t, "", "account debug requests", m.accountDebugRequests, test.expectedAccountDebugCount, prometheus.Labels{accountLabel: "acct-id"})
+		assertCounterValue(t, "", "debug requests", m.debugRequests, test.expectedDebugCount)
+	}
+}
+
 func TestRequestMetricWithoutCookie(t *testing.T) {
 	requestType := metrics.ReqTypeORTB2Web
 	performTest := func(m *Metrics, cookieFlag metrics.CookieFlag) {