Repository: prebid/prebid-server
Pull request #1671 (2021-01-21 18:52:44)

Handle empty consent string during cookie sync and setuid
The main purpose of this commit is to properly handle an empty consent during a cookie sync. The logic is similar to changes recently made to the openrtb endpoints.
- If the GDPR signal is determined to be 0 indicating GDPR does not apply (either because it was explicitly set to 0 in the request or was ambiguous and normalized to 0), cookie sync is allowed.
- Else If the GDPR signal is determined to be 1 indicating GDPR applies (either because it was explicitly set to 1 in the request or was ambiguous and normalized to 1), and the consent string is empty, cookie sync is not allowed.
- Else the GDPR signal was determined to be 1 with a non-empty consent string so process the consent string to determine whether cookie sync is allowed

Other things to note:
- You’ll notice that the three methods that implement the Permissions interface all generally behave in the same way now for clarity. They attempt to normalize the GDPR signal, early return if the signal is No, early return if the consent string is empty and then process the consent string.
- the GDPR processing switch statements in `endpoints/auction.go` and `endpoints/setuid.go` were refactored to flatten the logic with early returns.
- I didn’t bother to tabularize the new tests I added in `impl_test.go`. I’ll handle that in another PR dedicated to that test file soon.


Patch:
diff --git a/adapters/adform/adform.go b/adapters/adform/adform.go
index 5eeeb132..e84df5b8 100644
--- a/adapters/adform/adform.go
+++ b/adapters/adform/adform.go
@@ -44,23 +44,11 @@ type adformRequest struct {
 	adUnits       []*adformAdUnit
 	gdprApplies   string
 	consent       string
-	digitrust     *adformDigitrust
 	currency      string
 	eids          string
 	url           string
 }
 
-type adformDigitrust struct {
-	Id      string                 `json:"id"`
-	Version int                    `json:"version"`
-	Keyv    int                    `json:"keyv"`
-	Privacy adformDigitrustPrivacy `json:"privacy"`
-}
-
-type adformDigitrustPrivacy struct {
-	Optout bool `json:"optout"`
-}
-
 type adformAdUnit struct {
 	MasterTagId json.Number `json:"mid"`
 	PriceType   string      `json:"priceType,omitempty"`
@@ -216,24 +204,6 @@ func pbsRequestToAdformRequest(a *AdformAdapter, request *pbs.PBSRequest, bidder
 		gdprApplies = ""
 	}
 	consent := request.ParseConsent()
-	var digitrustData *openrtb_ext.ExtUserDigiTrust
-	if request.User != nil {
-		var extUser *openrtb_ext.ExtUser
-		if err := json.Unmarshal(request.User.Ext, &extUser); err == nil {
-			digitrustData = extUser.DigiTrust
-		}
-	}
-
-	var digitrust *adformDigitrust = nil
-	if digitrustData != nil {
-		digitrust = new(adformDigitrust)
-		digitrust.Id = digitrustData.ID
-		digitrust.Keyv = digitrustData.KeyV
-		digitrust.Version = 1
-		digitrust.Privacy = adformDigitrustPrivacy{
-			Optout: digitrustData.Pref != 0,
-		}
-	}
 
 	return &adformRequest{
 		adUnits:       adUnits,
@@ -247,7 +217,6 @@ func pbsRequestToAdformRequest(a *AdformAdapter, request *pbs.PBSRequest, bidder
 		tid:           request.Tid,
 		gdprApplies:   gdprApplies,
 		consent:       consent,
-		digitrust:     digitrust,
 		currency:      defaultCurrency,
 	}, nil
 }
@@ -371,18 +340,9 @@ func (r *adformRequest) buildAdformHeaders(a *AdformAdapter) http.Header {
 		header.Set("Referer", r.referer)
 	}
 
-	cookie := make([]string, 0, 2)
 	if r.userId != "" {
-		cookie = append(cookie, fmt.Sprintf("uid=%s", r.userId))
-	}
-	if r.digitrust != nil {
-		if digitrustBytes, err := json.Marshal(r.digitrust); err == nil {
-			digitrust := base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(digitrustBytes)
-			// Cookie name and structure are described here: https://github.com/digi-trust/dt-cdn/wiki/Cookies-for-Platforms
-			cookie = append(cookie, fmt.Sprintf("DigiTrust.v1.identity=%s", digitrust))
-		}
+		header.Set("Cookie", fmt.Sprintf("uid=%s;", r.userId))
 	}
-	header.Set("Cookie", strings.Join(cookie, ";"))
 
 	return header
 }
@@ -506,27 +466,14 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro
 
 	eids := ""
 	consent := ""
-	var digitrustData *openrtb_ext.ExtUserDigiTrust
 	if request.User != nil {
 		var extUser openrtb_ext.ExtUser
 		if err := json.Unmarshal(request.User.Ext, &extUser); err == nil {
 			consent = extUser.Consent
-			digitrustData = extUser.DigiTrust
 			eids = encodeEids(extUser.Eids)
 		}
 	}
 
-	var digitrust *adformDigitrust = nil
-	if digitrustData != nil {
-		digitrust = new(adformDigitrust)
-		digitrust.Id = digitrustData.ID
-		digitrust.Keyv = digitrustData.KeyV
-		digitrust.Version = 1
-		digitrust.Privacy = adformDigitrustPrivacy{
-			Optout: digitrustData.Pref != 0,
-		}
-	}
-
 	requestCurrency := defaultCurrency
 	if len(request.Cur) != 0 {
 		hasDefaultCurrency := false
@@ -552,7 +499,6 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro
 		tid:           tid,
 		gdprApplies:   gdprApplies,
 		consent:       consent,
-		digitrust:     digitrust,
 		currency:      requestCurrency,
 		eids:          eids,
 		url:           url,
diff --git a/adapters/amx/amx.go b/adapters/amx/amx.go
index 6ce1574a..a9dfd06f 100644
--- a/adapters/amx/amx.go
+++ b/adapters/amx/amx.go
@@ -17,7 +17,7 @@ import (
 const vastImpressionFormat = "<Impression><![CDATA[%s]]></Impression>"
 const vastSearchPoint = "</Impression>"
 const nbrHeaderName = "x-nbr"
-const adapterVersion = "pbs1.0"
+const adapterVersion = "pbs1.1"
 
 // AMXAdapter is the AMX bid adapter
 type AMXAdapter struct {
@@ -149,6 +149,7 @@ func (adapter *AMXAdapter) MakeBids(request *openrtb.BidRequest, externalRequest
 
 	for _, sb := range bidResp.SeatBid {
 		for _, bid := range sb.Bid {
+			bid := bid
 			bidExt, bidExtErr := getBidExt(bid.Ext)
 			if bidExtErr != nil {
 				errs = append(errs, bidExtErr)
@@ -165,11 +166,12 @@ func (adapter *AMXAdapter) MakeBids(request *openrtb.BidRequest, externalRequest
 				// remove the NURL so a client/player doesn't fire it twice
 				b.Bid.NURL = ""
 			}
+
 			bidResponse.Bids = append(bidResponse.Bids, b)
 		}
 	}
-	return bidResponse, errs
 
+	return bidResponse, errs
 }
 
 func getBidExt(ext json.RawMessage) (amxBidExt, error) {
diff --git a/adapters/decenterads/decenterads.go b/adapters/decenterads/decenterads.go
new file mode 100644
index 00000000..5719bf1e
--- /dev/null
+++ b/adapters/decenterads/decenterads.go
@@ -0,0 +1,124 @@
+package decenterads
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+	"strconv"
+
+	"github.com/mxmCherry/openrtb"
+	"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
+}
+
+func (a *adapter) MakeRequests(request *openrtb.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	impressions := request.Imp
+	result := make([]*adapters.RequestData, 0, len(impressions))
+	errs := make([]error, 0, len(impressions))
+
+	for _, impression := range impressions {
+		var impExt map[string]json.RawMessage
+		if err := json.Unmarshal(impression.Ext, &impExt); err != nil {
+			errs = append(errs, fmt.Errorf("unable to parse bidder parameers: %s", err))
+			continue
+		}
+
+		bidderExt, bidderExtExists := impExt["bidder"]
+		if !bidderExtExists || len(bidderExt) == 0 {
+			errs = append(errs, errors.New("bidder parameters required"))
+			continue
+		}
+
+		impression.Ext = bidderExt
+		request.Imp = []openrtb.Imp{impression}
+		body, err := json.Marshal(request)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+		result = append(result, &adapters.RequestData{
+			Method:  "POST",
+			Uri:     a.endpoint,
+			Body:    body,
+			Headers: headers,
+		})
+	}
+
+	request.Imp = impressions
+	return result, errs
+}
+
+func (a *adapter) MakeBids(request *openrtb.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	var errs []error
+
+	switch responseData.StatusCode {
+	case http.StatusNoContent:
+		return nil, nil
+	case http.StatusBadRequest:
+		return nil, []error{&errortypes.BadInput{
+			Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode),
+		}}
+	case http.StatusOK:
+		break
+	default:
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode),
+		}}
+	}
+
+	var bidResponse openrtb.BidResponse
+	err := json.Unmarshal(responseData.Body, &bidResponse)
+	if err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: err.Error(),
+		}}
+	}
+
+	response := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+
+	for _, seatBid := range bidResponse.SeatBid {
+		for i := range seatBid.Bid {
+			response.Bids = append(response.Bids, &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: getMediaTypeForImp(seatBid.Bid[i].ImpID, request.Imp),
+			})
+		}
+	}
+
+	return response, errs
+}
+
+func getMediaTypeForImp(impID string, imps []openrtb.Imp) openrtb_ext.BidType {
+	for _, imp := range imps {
+		if imp.ID == impID {
+			if imp.Banner != nil {
+				return openrtb_ext.BidTypeBanner
+			} else if imp.Video != nil {
+				return openrtb_ext.BidTypeVideo
+			} else if imp.Native != nil {
+				return openrtb_ext.BidTypeNative
+			} else if imp.Audio != nil {
+				return openrtb_ext.BidTypeAudio
+			}
+		}
+	}
+	return openrtb_ext.BidTypeBanner
+}
+
+// Builder builds a new instance of the DecenterAds 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
+}
diff --git a/adapters/eplanning/eplanning.go b/adapters/eplanning/eplanning.go
index 21c61389..8d0f7e8b 100644
--- a/adapters/eplanning/eplanning.go
+++ b/adapters/eplanning/eplanning.go
@@ -26,6 +26,9 @@ const sec = "ROS"
 const dfpClientID = "1"
 const requestTargetInventory = "1"
 
+var priorityOrderForMobileSizesAsc = []string{"1x1", "300x50", "320x50", "300x250"}
+var priorityOrderForDesktopSizesAsc = []string{"1x1", "970x90", "970x250", "160x600", "300x600", "728x90", "300x250"}
+
 var cleanNameSteps = []cleanNameStep{
 	{regexp.MustCompile(`_|\.|-|\/`), ""},
 	{regexp.MustCompile(`\)\(|\(|\)|:`), "_"},
@@ -67,10 +70,11 @@ func (adapter *EPlanningAdapter) MakeRequests(request *openrtb.BidRequest, reqIn
 	spacesStrings := make([]string, 0, totalImps)
 	totalRequests := 0
 	clientID := ""
+	isMobile := isMobileDevice(request)
 
 	for i := 0; i < totalImps; i++ {
 		imp := request.Imp[i]
-		extImp, err := verifyImp(&imp)
+		extImp, err := verifyImp(&imp, isMobile)
 		if err != nil {
 			errors = append(errors, err)
 			continue
@@ -187,6 +191,10 @@ func (adapter *EPlanningAdapter) MakeRequests(request *openrtb.BidRequest, reqIn
 	return requests, errors
 }
 
+func isMobileDevice(request *openrtb.BidRequest) bool {
+	return request.Device != nil && (request.Device.DeviceType == openrtb.DeviceTypeMobileTablet || request.Device.DeviceType == openrtb.DeviceTypePhone || request.Device.DeviceType == openrtb.DeviceTypeTablet)
+}
+
 func cleanName(name string) string {
 	for _, step := range cleanNameSteps {
 		name = step.expression.ReplaceAllString(name, step.replacementString)
@@ -194,7 +202,7 @@ func cleanName(name string) string {
 	return name
 }
 
-func verifyImp(imp *openrtb.Imp) (*openrtb_ext.ExtImpEPlanning, error) {
+func verifyImp(imp *openrtb.Imp, isMobile bool) (*openrtb_ext.ExtImpEPlanning, error) {
 	var bidderExt adapters.ExtImpBidder
 
 	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
@@ -217,7 +225,7 @@ func verifyImp(imp *openrtb.Imp) (*openrtb_ext.ExtImpEPlanning, error) {
 		}
 	}
 
-	width, height := getSizeFromImp(imp)
+	width, height := getSizeFromImp(imp, isMobile)
 
 	if width == 0 && height == 0 {
 		impExt.SizeString = nullSize
@@ -232,17 +240,34 @@ func verifyImp(imp *openrtb.Imp) (*openrtb_ext.ExtImpEPlanning, error) {
 	return &impExt, nil
 }
 
-func getSizeFromImp(imp *openrtb.Imp) (uint64, uint64) {
+func searchSizePriority(hashedFormats map[string]int, format []openrtb.Format, priorityOrderForSizesAsc []string) (uint64, uint64) {
+	for i := len(priorityOrderForSizesAsc) - 1; i >= 0; i-- {
+		if formatIndex, wasFound := hashedFormats[priorityOrderForSizesAsc[i]]; wasFound {
+			return format[formatIndex].W, format[formatIndex].H
+		}
+	}
+	return format[0].W, format[0].H
+}
+
+func getSizeFromImp(imp *openrtb.Imp, isMobile bool) (uint64, uint64) {
 	if imp.Banner.W != nil && imp.Banner.H != nil {
 		return *imp.Banner.W, *imp.Banner.H
 	}
 
 	if imp.Banner.Format != nil {
-		for _, format := range imp.Banner.Format {
+		hashedFormats := make(map[string]int, len(imp.Banner.Format))
+
+		for i, format := range imp.Banner.Format {
 			if format.W != 0 && format.H != 0 {
-				return format.W, format.H
+				hashedFormats[fmt.Sprintf("%dx%d", format.W, format.H)] = i
 			}
 		}
+
+		if isMobile {
+			return searchSizePriority(hashedFormats, imp.Banner.Format, priorityOrderForMobileSizesAsc)
+		} else {
+			return searchSizePriority(hashedFormats, imp.Banner.Format, priorityOrderForDesktopSizesAsc)
+		}
 	}
 
 	return 0, 0
@@ -278,12 +303,14 @@ func (adapter *EPlanningAdapter) MakeBids(internalRequest *openrtb.BidRequest, e
 		}}
 	}
 
+	isMobile := isMobileDevice(internalRequest)
+
 	bidResponse := adapters.NewBidderResponse()
 
 	spaceNameToImpID := make(map[string]string)
 
 	for _, imp := range internalRequest.Imp {
-		extImp, err := verifyImp(&imp)
+		extImp, err := verifyImp(&imp, isMobile)
 		if err != nil {
 			continue
 		}
diff --git a/adapters/info.go b/adapters/info.go
index 7f3ad9c3..19897ac7 100644
--- a/adapters/info.go
+++ b/adapters/info.go
@@ -206,6 +206,11 @@ type BidderInfo struct {
 	Capabilities            *CapabilitiesInfo `yaml:"capabilities" json:"capabilities"`
 	AliasOf                 string            `json:"aliasOf,omitempty"`
 	ModifyingVastXmlAllowed bool              `yaml:"modifyingVastXmlAllowed" json:"-" xml:"-"`
+	Debug                   *DebugInfo        `yaml:"debug,omitempty" json:"-" xml:"-"`
+}
+
+type DebugInfo struct {
+	Allow bool `yaml:"allow" json:"allow"`
 }
 
 type MaintainerInfo struct {
diff --git a/adapters/pulsepoint/pulsepoint.go b/adapters/pulsepoint/pulsepoint.go
index 23f11f9a..b07a2cba 100644
--- a/adapters/pulsepoint/pulsepoint.go
+++ b/adapters/pulsepoint/pulsepoint.go
@@ -1,17 +1,19 @@
 package pulsepoint
 
 import (
-	"bytes"
-	"context"
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"net/http"
 	"strconv"
+
+	"bytes"
+	"context"
+	"io/ioutil"
 	"strings"
 
 	"github.com/mxmCherry/openrtb"
 	"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"
 	"github.com/prebid/prebid-server/pbs"
@@ -23,15 +25,169 @@ type PulsePointAdapter struct {
 	URI  string
 }
 
+// Builds an instance of PulsePointAdapter
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	bidder := &PulsePointAdapter{
+		URI: config.Endpoint,
+	}
+	return bidder, nil
+}
+
+func (a *PulsePointAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	errs := make([]error, 0, len(request.Imp))
+
+	var err error
+	pubID := ""
+	imps := make([]openrtb.Imp, 0, len(request.Imp))
+	for i := 0; i < len(request.Imp); i++ {
+		imp := request.Imp[i]
+		var bidderExt adapters.ExtImpBidder
+		if err = json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+			errs = append(errs, &errortypes.BadInput{
+				Message: err.Error(),
+			})
+			continue
+		}
+		var pulsepointExt openrtb_ext.ExtImpPulsePoint
+		if err = json.Unmarshal(bidderExt.Bidder, &pulsepointExt); err != nil {
+			errs = append(errs, &errortypes.BadInput{
+				Message: err.Error(),
+			})
+			continue
+		}
+		// parse pubid and keep it for reference
+		if pubID == "" && pulsepointExt.PubID > 0 {
+			pubID = strconv.Itoa(pulsepointExt.PubID)
+		}
+		// tag id to be sent
+		imp.TagID = strconv.Itoa(pulsepointExt.TagID)
+		imps = append(imps, imp)
+	}
+
+	// verify there are valid impressions
+	if len(imps) == 0 {
+		return nil, errs
+	}
+
+	// add the publisher id from ext to the site.pub.id or app.pub.id
+	if request.Site != nil {
+		site := *request.Site
+		if site.Publisher != nil {
+			publisher := *site.Publisher
+			publisher.ID = pubID
+			site.Publisher = &publisher
+		} else {
+			site.Publisher = &openrtb.Publisher{ID: pubID}
+		}
+		request.Site = &site
+	} else if request.App != nil {
+		app := *request.App
+		if app.Publisher != nil {
+			publisher := *app.Publisher
+			publisher.ID = pubID
+			app.Publisher = &publisher
+		} else {
+			app.Publisher = &openrtb.Publisher{ID: pubID}
+		}
+		request.App = &app
+	}
+
+	request.Imp = imps
+	reqJSON, err := json.Marshal(request)
+	if err != nil {
+		errs = append(errs, err)
+		return nil, errs
+	}
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	return []*adapters.RequestData{{
+		Method:  "POST",
+		Uri:     a.URI,
+		Body:    reqJSON,
+		Headers: headers,
+	}}, errs
+}
+
+func (a *PulsePointAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	// passback
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+	// bad requests
+	if response.StatusCode == http.StatusBadRequest {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Bad user input: HTTP status %d", response.StatusCode),
+		}}
+	}
+	// error
+	if response.StatusCode != http.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Bad server response: HTTP status %d", response.StatusCode),
+		}}
+	}
+	// parse response
+	var bidResp openrtb.BidResponse
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)
+	// map imps by id
+	impsByID := make(map[string]openrtb.Imp)
+	for i := 0; i < len(internalRequest.Imp); i++ {
+		impsByID[internalRequest.Imp[i].ID] = internalRequest.Imp[i]
+	}
+
+	var errs []error
+	for _, sb := range bidResp.SeatBid {
+		for i := 0; i < len(sb.Bid); i++ {
+			bid := sb.Bid[i]
+			imp := impsByID[bid.ImpID]
+			bidType := getBidType(imp)
+			if &imp != nil && bidType != "" {
+				bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+					Bid:     &bid,
+					BidType: bidType,
+				})
+			}
+		}
+	}
+	return bidResponse, errs
+}
+
+func getBidType(imp openrtb.Imp) openrtb_ext.BidType {
+	// derive the bidtype purely from the impression itself
+	if imp.Banner != nil {
+		return openrtb_ext.BidTypeBanner
+	} else if imp.Video != nil {
+		return openrtb_ext.BidTypeVideo
+	} else if imp.Audio != nil {
+		return openrtb_ext.BidTypeAudio
+	} else if imp.Native != nil {
+		return openrtb_ext.BidTypeNative
+	}
+	return ""
+}
+
+/////////////////////////////////
+// Legacy implementation: Start
+/////////////////////////////////
+
+func NewPulsePointLegacyAdapter(config *adapters.HTTPAdapterConfig, uri string) *PulsePointAdapter {
+	a := adapters.NewHTTPAdapter(config)
+
+	return &PulsePointAdapter{
+		http: a,
+		URI:  uri,
+	}
+}
+
 // used for cookies and such
 func (a *PulsePointAdapter) Name() string {
 	return "pulsepoint"
 }
 
-func (a *PulsePointAdapter) SkipNoCookies() bool {
-	return false
-}
-
 // parameters for pulsepoint adapter.
 type PulsepointParams struct {
 	PublisherId int    `json:"cp"`
@@ -39,6 +195,10 @@ type PulsepointParams struct {
 	AdSize      string `json:"cf"`
 }
 
+func (a *PulsePointAdapter) SkipNoCookies() bool {
+	return false
+}
+
 func (a *PulsePointAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder *pbs.PBSBidder) (pbs.PBSBidSlice, error) {
 	mediaTypes := []pbs.MediaType{pbs.MEDIA_TYPE_BANNER}
 	ppReq, err := adapters.MakeOpenRTBGeneric(req, bidder, a.Name(), mediaTypes)
@@ -195,11 +355,6 @@ func (a *PulsePointAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidde
 	return bids, nil
 }
 
-func NewPulsePointLegacyAdapter(config *adapters.HTTPAdapterConfig, uri string) *PulsePointAdapter {
-	a := adapters.NewHTTPAdapter(config)
-
-	return &PulsePointAdapter{
-		http: a,
-		URI:  uri,
-	}
-}
+/////////////////////////////////
+// Legacy implementation: End
+/////////////////////////////////
diff --git a/config/accounts.go b/config/accounts.go
index 0faf5f5f..54809245 100644
--- a/config/accounts.go
+++ b/config/accounts.go
@@ -19,6 +19,7 @@ type Account struct {
 	EventsEnabled bool        `mapstructure:"events_enabled" json:"events_enabled"`
 	CCPA          AccountCCPA `mapstructure:"ccpa" json:"ccpa"`
 	GDPR          AccountGDPR `mapstructure:"gdpr" json:"gdpr"`
+	DebugAllow    bool        `mapstructure:"debug_allow" json:"debug_allow"`
 }
 
 // AccountCCPA represents account-specific CCPA configuration
diff --git a/config/config.go b/config/config.go
index 6577c1fb..ae1f62e9 100755
--- a/config/config.go
+++ b/config/config.go
@@ -217,7 +217,7 @@ func (cfg *GDPR) validate(errs []error) []error {
 		errs = append(errs, fmt.Errorf("gdpr.amp_exception has been discontinued and must be removed from your config. If you need to disable GDPR for AMP, you may do so per-account (gdpr.integration_enabled.amp) or at the host level for the default account (account_defaults.gdpr.integration_enabled.amp)"))
 	}
 	if cfg.TCF1.FetchGVL == true {
-		glog.Warning("gdpr.tcf1.fetch_gvl is deprecated and will be removed in a future version, at which point TCF1 will always use the fallback GVL")
+		errs = append(errs, fmt.Errorf("gdpr.tcf1.fetch_gvl has been discontinued and must be removed from your config. TCF1 will always use the fallback GVL going forward"))
 	}
 	return errs
 }
@@ -590,6 +590,7 @@ func (cfg *Configuration) setDerivedDefaults() {
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/match/bounce/current?version=1&networkId=72582&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderCpmstar, "https://server.cpmstar.com/usersync.aspx?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDatablocks, "https://sync.v5prebid.datablocks.net/s2ssync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D")
+	// openrtb_ext.BidderDecenterAds doesn't have a good default.
 	// openrtb_ext.BidderDMX doesn't have a good default.
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDeepintent, "https://match.deepintent.com/usersync/136?id=unk&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddeepintent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5BUID%5D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEmxDigital, "https://cs.emxdgt.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Demx_digital%26uid%3D%24UID")
@@ -601,7 +602,7 @@ func (cfg *Configuration) setDerivedDefaults() {
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGrid, "https://x.bidswitch.net/check_uuid/"+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dgrid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BBSW_UUID%7D?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGumGum, "https://rtb.gumgum.com/usync/prbds2s?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dgumgum%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderImprovedigital, "https://ad.360yield.com/server_match?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dimprovedigital%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BPUB_USER_ID%7D")
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderIx, "https://ssum.casalemedia.com/usermatchredir?s=184932&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dix%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
+	// openrtb_ext.BidderIx doesn't have a good default.
 	// openrtb_ext.BidderInvibes doesn't have a good default.
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderKrushmedia, "https://cs.krushmedia.com/4e4abdd5ecc661643458a730b1aa927d.gif?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dkrushmedia%26uid%3D%5BUID%5D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLifestreet, "https://ads.lfstmedia.com/idsync/137062?synced=1&ttl=1s&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlifestreet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24")
@@ -794,7 +795,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.33across.endpoint", "http://ssc.33across.com/api/v1/hb")
 	v.SetDefault("adapters.33across.partner_id", "")
 	v.SetDefault("adapters.acuityads.endpoint", "http://{{.Host}}.admanmedia.com/bid?token={{.AccountID}}")
-	v.SetDefault("adapters.adform.endpoint", "http://adx.adform.net/adx")
+	v.SetDefault("adapters.adform.endpoint", "https://adx.adform.net/adx")
 	v.SetDefault("adapters.adgeneration.endpoint", "https://d.socdm.com/adsv/v1")
 	v.SetDefault("adapters.adhese.endpoint", "https://ads-{{.AccountID}}.adhese.com/json")
 	v.SetDefault("adapters.adkernel.endpoint", "http://{{.Host}}/hb?zone={{.ZoneID}}")
@@ -828,6 +829,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/cvx/server/hb/ortb/25")
 	v.SetDefault("adapters.cpmstar.endpoint", "https://server.cpmstar.com/openrtbbidrq.aspx")
 	v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}")
+	v.SetDefault("adapters.decenterads.endpoint", "http://supply.decenterads.com/?c=o&m=rtb")
 	v.SetDefault("adapters.deepintent.endpoint", "https://prebid.deepintent.com/prebid")
 	v.SetDefault("adapters.dmx.endpoint", "https://dmx-direct.districtm.io/b/v2")
 	v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com")
@@ -910,7 +912,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("gdpr.timeouts_ms.init_vendorlist_fetches", 0)
 	v.SetDefault("gdpr.timeouts_ms.active_vendorlist_fetch", 0)
 	v.SetDefault("gdpr.non_standard_publishers", []string{""})
-	v.SetDefault("gdpr.tcf1.fetch_gvl", true)
+	v.SetDefault("gdpr.tcf1.fetch_gvl", false)
 	v.SetDefault("gdpr.tcf1.fallback_gvl_path", "./static/tcf1/fallback_gvl.json")
 	v.SetDefault("gdpr.tcf2.enabled", true)
 	v.SetDefault("gdpr.tcf2.purpose1.enabled", true)
@@ -937,6 +939,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("blacklisted_accts", []string{""})
 	v.SetDefault("account_required", false)
 	v.SetDefault("account_defaults.disabled", false)
+	v.SetDefault("account_defaults.debug_allow", true)
 	v.SetDefault("certificates_file", "")
 	v.SetDefault("auto_gen_source_tid", true)
 
diff --git a/endpoints/auction.go b/endpoints/auction.go
index 0604c224..92e9769d 100644
--- a/endpoints/auction.go
+++ b/endpoints/auction.go
@@ -189,21 +189,16 @@ func (a *auction) recoverSafely(inner func(*pbs.PBSBidder, metrics.AdapterLabels
 }
 
 func (a *auction) shouldUsersync(ctx context.Context, bidder openrtb_ext.BidderName, gdprPrivacyPolicy gdprPrivacy.Policy) bool {
-	switch gdprPrivacyPolicy.Signal {
-	case "0":
-		return true
-	case "1":
-		if gdprPrivacyPolicy.Consent == "" {
-			return false
-		}
-		fallthrough
-	default:
-		if canSync, err := a.gdprPerms.HostCookiesAllowed(ctx, gdprPrivacyPolicy.Consent); !canSync || err != nil {
-			return false
-		}
-		canSync, err := a.gdprPerms.BidderSyncAllowed(ctx, bidder, gdprPrivacyPolicy.Consent)
-		return canSync && err == nil
+	gdprSignal := gdpr.SignalAmbiguous
+	if signal, err := gdpr.SignalParse(gdprPrivacyPolicy.Signal); err != nil {
+		gdprSignal = signal
+	}
+
+	if canSync, err := a.gdprPerms.HostCookiesAllowed(ctx, gdprSignal, gdprPrivacyPolicy.Consent); err != nil || !canSync {
+		return false
 	}
+	canSync, err := a.gdprPerms.BidderSyncAllowed(ctx, bidder, gdprSignal, gdprPrivacyPolicy.Consent)
+	return canSync && err == nil
 }
 
 // cache video bids only for Web
diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index 21876af9..bf3935f0 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -248,13 +248,14 @@ func (req *cookieSyncRequest) filterForGDPR(permissions gdpr.Permissions) {
 		return
 	}
 
-	if allowSync, err := permissions.HostCookiesAllowed(context.Background(), req.Consent); err != nil || !allowSync {
+	// At this point we know the gdpr signal is Yes because the upstream call to parseRequest already denormalized the signal if it was ambiguous
+	if allowSync, err := permissions.HostCookiesAllowed(context.Background(), gdpr.SignalYes, req.Consent); err != nil || !allowSync {
 		req.Bidders = nil
 		return
 	}
 
 	for i := 0; i < len(req.Bidders); i++ {
-		if allowSync, err := permissions.BidderSyncAllowed(context.Background(), openrtb_ext.BidderName(req.Bidders[i]), req.Consent); err != nil || !allowSync {
+		if allowSync, err := permissions.BidderSyncAllowed(context.Background(), openrtb_ext.BidderName(req.Bidders[i]), gdpr.SignalYes, req.Consent); err != nil || !allowSync {
 			req.Bidders = append(req.Bidders[:i], req.Bidders[i+1:]...)
 			i--
 		}
diff --git a/endpoints/setuid.go b/endpoints/setuid.go
index caa3ae17..4bff02ac 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -143,32 +143,38 @@ func checkChromeBrowserVersion(ua string, index int, chromeStrLength int) bool {
 	return result
 }
 
-func preventSyncsGDPR(gdprEnabled string, gdprConsent string, perms gdpr.Permissions) (bool, int, string) {
-	switch gdprEnabled {
-	case "0":
-		return false, 0, ""
-	case "1":
-		if gdprConsent == "" {
-			return true, http.StatusBadRequest, "gdpr_consent is required when gdpr=1"
-		}
-		fallthrough
-	case "":
-		if allowed, err := perms.HostCookiesAllowed(context.Background(), gdprConsent); err != nil {
-			if _, ok := err.(*gdpr.ErrorMalformedConsent); ok {
-				return true, http.StatusBadRequest, "gdpr_consent was invalid. " + err.Error()
-			} else {
-				// We can't really distinguish between requests that are for a new version of the global vendor list, and
-				// ones which are simply malformed (version number is much too large).
-				// Since we try to fetch new versions as requests come in for them, PBS *should* self-correct
-				// rather quickly, meaning that most of these will be malformed strings.
-				return true, http.StatusBadRequest, "No global vendor list was available to interpret this consent string. If this is a new, valid version, it should become available soon."
-			}
-		} else if !allowed {
-			return true, http.StatusOK, "The gdpr_consent string prevents cookies from being saved"
-		} else {
-			return false, 0, ""
-		}
-	default:
+func preventSyncsGDPR(gdprEnabled string, gdprConsent string, perms gdpr.Permissions) (shouldReturn bool, status int, body string) {
+
+	if gdprEnabled != "" && gdprEnabled != "0" && gdprEnabled != "1" {
 		return true, http.StatusBadRequest, "the gdpr query param must be either 0 or 1. You gave " + gdprEnabled
 	}
+
+	if gdprEnabled == "1" && gdprConsent == "" {
+		return true, http.StatusBadRequest, "gdpr_consent is required when gdpr=1"
+	}
+
+	gdprSignal := gdpr.SignalAmbiguous
+
+	if i, err := strconv.Atoi(gdprEnabled); err == nil {
+		gdprSignal = gdpr.Signal(i)
+	}
+
+	allowed, err := perms.HostCookiesAllowed(context.Background(), gdprSignal, gdprConsent)
+	if err != nil {
+		if _, ok := err.(*gdpr.ErrorMalformedConsent); ok {
+			return true, http.StatusBadRequest, "gdpr_consent was invalid. " + err.Error()
+		}
+
+		// We can't really distinguish between requests that are for a new version of the global vendor list, and
+		// ones which are simply malformed (version number is much too large).
+		// Since we try to fetch new versions as requests come in for them, PBS *should* self-correct
+		// rather quickly, meaning that most of these will be malformed strings.
+		return true, http.StatusBadRequest, "No global vendor list was available to interpret this consent string. If this is a new, valid version, it should become available soon."
+	}
+
+	if allowed {
+		return false, 0, ""
+	}
+
+	return true, http.StatusOK, "The gdpr_consent string prevents cookies from being saved"
 }
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 31342a4c..f05a4d81 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -35,6 +35,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/conversant"
 	"github.com/prebid/prebid-server/adapters/cpmstar"
 	"github.com/prebid/prebid-server/adapters/datablocks"
+	"github.com/prebid/prebid-server/adapters/decenterads"
 	"github.com/prebid/prebid-server/adapters/deepintent"
 	"github.com/prebid/prebid-server/adapters/dmx"
 	"github.com/prebid/prebid-server/adapters/emx_digital"
@@ -65,6 +66,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/orbidder"
 	"github.com/prebid/prebid-server/adapters/pubmatic"
 	"github.com/prebid/prebid-server/adapters/pubnative"
+	"github.com/prebid/prebid-server/adapters/pulsepoint"
 	"github.com/prebid/prebid-server/adapters/revcontent"
 	"github.com/prebid/prebid-server/adapters/rhythmone"
 	"github.com/prebid/prebid-server/adapters/rtbhouse"
@@ -135,6 +137,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderConversant:       conversant.Builder,
 		openrtb_ext.BidderCpmstar:          cpmstar.Builder,
 		openrtb_ext.BidderDatablocks:       datablocks.Builder,
+		openrtb_ext.BidderDecenterAds:      decenterads.Builder,
 		openrtb_ext.BidderDeepintent:       deepintent.Builder,
 		openrtb_ext.BidderDmx:              dmx.Builder,
 		openrtb_ext.BidderEmxDigital:       emx_digital.Builder,
@@ -166,6 +169,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderOrbidder:         orbidder.Builder,
 		openrtb_ext.BidderPubmatic:         pubmatic.Builder,
 		openrtb_ext.BidderPubnative:        pubnative.Builder,
+		openrtb_ext.BidderPulsepoint:       pulsepoint.Builder,
 		openrtb_ext.BidderRevcontent:       revcontent.Builder,
 		openrtb_ext.BidderRhythmone:        rhythmone.Builder,
 		openrtb_ext.BidderRTBHouse:         rtbhouse.Builder,
diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go
index 6e12ed00..7e271376 100644
--- a/exchange/adapter_util.go
+++ b/exchange/adapter_util.go
@@ -8,7 +8,6 @@ import (
 
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/adapters/lifestreet"
-	"github.com/prebid/prebid-server/adapters/pulsepoint"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
@@ -39,7 +38,12 @@ func buildExchangeBidders(cfg *config.Configuration, infos adapters.BidderInfos,
 
 	exchangeBidders := make(map[openrtb_ext.BidderName]adaptedBidder, len(bidders))
 	for bidderName, bidder := range bidders {
-		exchangeBidders[bidderName] = adaptBidder(bidder, client, cfg, me, bidderName)
+		info, infoFound := infos[string(bidderName)]
+		if !infoFound {
+			errs = append(errs, fmt.Errorf("%v: bidder info not found", bidder))
+			continue
+		}
+		exchangeBidders[bidderName] = adaptBidder(bidder, client, cfg, me, bidderName, info.Debug)
 	}
 
 	return exchangeBidders, nil
@@ -58,7 +62,7 @@ func buildBidders(adapterConfig map[string]config.Adapter, infos adapters.Bidder
 		}
 
 		// Ignore Legacy Bidders
-		if bidderName == openrtb_ext.BidderLifestreet || bidderName == openrtb_ext.BidderPulsepoint {
+		if bidderName == openrtb_ext.BidderLifestreet {
 			continue
 		}
 
@@ -99,12 +103,6 @@ func buildExchangeBiddersLegacy(adapterConfig map[string]config.Adapter, infos a
 		bidders[openrtb_ext.BidderLifestreet] = adaptLegacyAdapter(adapter)
 	}
 
-	// Pulsepoint
-	if infos[string(openrtb_ext.BidderPulsepoint)].Status == adapters.StatusActive {
-		adapter := pulsepoint.NewPulsePointLegacyAdapter(adapters.DefaultHTTPAdapterConfig, adapterConfig[string(openrtb_ext.BidderPulsepoint)].Endpoint)
-		bidders[openrtb_ext.BidderPulsepoint] = adaptLegacyAdapter(adapter)
-	}
-
 	return bidders
 }
 
diff --git a/exchange/bidder.go b/exchange/bidder.go
index e4867b0e..5b5852bd 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -49,7 +49,7 @@ type adaptedBidder interface {
 	//
 	// Any errors will be user-facing in the API.
 	// Error messages should help publishers understand what might account for "bad" bids.
-	requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error)
+	requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error)
 }
 
 // pbsOrtbBid is a Bid returned by an adaptedBidder.
@@ -93,7 +93,7 @@ type pbsOrtbSeatBid struct {
 //
 // The name refers to the "Adapter" architecture pattern, and should not be confused with a Prebid "Adapter"
 // (which is being phased out and replaced by Bidder for OpenRTB auctions)
-func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Configuration, me metrics.MetricsEngine, name openrtb_ext.BidderName) adaptedBidder {
+func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Configuration, me metrics.MetricsEngine, name openrtb_ext.BidderName, debugInfo *adapters.DebugInfo) adaptedBidder {
 	return &bidderAdapter{
 		Bidder:     bidder,
 		BidderName: name,
@@ -102,10 +102,18 @@ func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Config
 		config: bidderAdapterConfig{
 			Debug:              cfg.Debug,
 			DisableConnMetrics: cfg.Metrics.Disabled.AdapterConnectionMetrics,
+			DebugInfo:          adapters.DebugInfo{Allow: parseDebugInfo(debugInfo)},
 		},
 	}
 }
 
+func parseDebugInfo(info *adapters.DebugInfo) bool {
+	if info == nil {
+		return true
+	}
+	return info.Allow
+}
+
 type bidderAdapter struct {
 	Bidder     adapters.Bidder
 	BidderName openrtb_ext.BidderName
@@ -117,9 +125,10 @@ type bidderAdapter struct {
 type bidderAdapterConfig struct {
 	Debug              config.Debug
 	DisableConnMetrics bool
+	DebugInfo          adapters.DebugInfo
 }
 
-func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) {
+func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
 	reqData, errs := bidder.Bidder.MakeRequests(request, reqInfo)
 
 	if len(reqData) == 0 {
@@ -155,8 +164,14 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.Bi
 	for i := 0; i < len(reqData); i++ {
 		httpInfo := <-responseChannel
 		// If this is a test bid, capture debugging info from the requests.
-		if debugInfo := ctx.Value(DebugContextKey); debugInfo != nil && debugInfo.(bool) {
-			seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
+		// Write debug data to ext in case if:
+		// - debugContextKey (url param) in true
+		// - account debug is allowed
+		// - bidder debug is allowed
+		if accountDebugAllowed && bidder.config.DebugInfo.Allow {
+			if debugInfo := ctx.Value(DebugContextKey); debugInfo != nil && debugInfo.(bool) {
+				seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
+			}
 		}
 
 		if httpInfo.err == nil {
@@ -310,17 +325,19 @@ func getAssetByID(id int64, assets []nativeRequests.Asset) (nativeRequests.Asset
 func makeExt(httpInfo *httpCallInfo) *openrtb_ext.ExtHttpCall {
 	if httpInfo.err == nil {
 		return &openrtb_ext.ExtHttpCall{
-			Uri:          httpInfo.request.Uri,
-			RequestBody:  string(httpInfo.request.Body),
-			ResponseBody: string(httpInfo.response.Body),
-			Status:       httpInfo.response.StatusCode,
+			Uri:            httpInfo.request.Uri,
+			RequestBody:    string(httpInfo.request.Body),
+			ResponseBody:   string(httpInfo.response.Body),
+			Status:         httpInfo.response.StatusCode,
+			RequestHeaders: httpInfo.request.Headers,
 		}
 	} else if httpInfo.request == nil {
 		return &openrtb_ext.ExtHttpCall{}
 	} else {
 		return &openrtb_ext.ExtHttpCall{
-			Uri:         httpInfo.request.Uri,
-			RequestBody: string(httpInfo.request.Body),
+			Uri:            httpInfo.request.Uri,
+			RequestBody:    string(httpInfo.request.Body),
+			RequestHeaders: httpInfo.request.Headers,
 		}
 	}
 }
diff --git a/exchange/bidder_validate_bids.go b/exchange/bidder_validate_bids.go
index 3700bac4..cf74dfb1 100644
--- a/exchange/bidder_validate_bids.go
+++ b/exchange/bidder_validate_bids.go
@@ -28,8 +28,8 @@ type validatedBidder struct {
 	bidder adaptedBidder
 }
 
-func (v *validatedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) {
-	seatBid, errs := v.bidder.requestBid(ctx, request, name, bidAdjustment, conversions, reqInfo)
+func (v *validatedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
+	seatBid, errs := v.bidder.requestBid(ctx, request, name, bidAdjustment, conversions, reqInfo, accountDebugAllowed)
 	if validationErrors := removeInvalidBids(request, seatBid); len(validationErrors) > 0 {
 		errs = append(errs, validationErrors...)
 	}
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 99ac8a88..cfb9c6d3 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -14,7 +14,6 @@ import (
 	"strings"
 	"time"
 
-	uuid "github.com/gofrs/uuid"
 	"github.com/prebid/prebid-server/stored_requests"
 
 	"github.com/golang/glog"
@@ -135,7 +134,15 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 		_, targData.cacheHost, targData.cachePath = e.cache.GetExtCacheData()
 	}
 
+	if debugLog == nil {
+		debugLog = &DebugLog{Enabled: false}
+	}
+
 	debugInfo := getDebugInfo(r.BidRequest, requestExt)
+
+	debugInfo = debugInfo && r.Account.DebugAllow
+	debugLog.Enabled = debugLog.Enabled && r.Account.DebugAllow
+
 	if debugInfo {
 		ctx = e.makeDebugContext(ctx, debugInfo)
 	}
@@ -163,10 +170,11 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 	// Get currency rates conversions for the auction
 	conversions := e.currencyConverter.Rates()
 
-	adapterBids, adapterExtra, anyBidsReturned := e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions)
+	adapterBids, adapterExtra, anyBidsReturned := e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, r.Account.DebugAllow)
 
 	var auc *auction
 	var cacheErrs []error
+	var bidResponseExt *openrtb_ext.ExtBidResponse
 	if anyBidsReturned {
 
 		var bidCategory map[string]string
@@ -194,34 +202,35 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 				dealErrs := applyDealSupport(r.BidRequest, auc, bidCategory)
 				errs = append(errs, dealErrs...)
 			}
-			cacheErrs := auc.doCache(ctx, e.cache, targData, evTracking, r.BidRequest, 60, &r.Account.CacheTTL, bidCategory, debugLog)
+
+			bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, debugInfo, errs)
+			if debugLog.Enabled {
+				if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil {
+					debugLog.Data.Response = string(bidRespExtBytes)
+				} else {
+					debugLog.Data.Response = "Unable to marshal response ext for debugging"
+					errs = append(errs, err)
+				}
+			}
+
+			cacheErrs = auc.doCache(ctx, e.cache, targData, evTracking, r.BidRequest, 60, &r.Account.CacheTTL, bidCategory, debugLog)
 			if len(cacheErrs) > 0 {
 				errs = append(errs, cacheErrs...)
 			}
+
 			targData.setTargeting(auc, r.BidRequest.App != nil, bidCategory)
 
 		}
-	}
+		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, debugInfo, errs)
+	} else {
+		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, debugInfo, errs)
 
-	bidResponseExt := e.makeExtBidResponse(adapterBids, adapterExtra, r, debugInfo, errs)
+		if debugLog.Enabled {
 
-	// Ensure caching errors are added in case auc.doCache was called and errors were returned
-	if len(cacheErrs) > 0 {
-		bidderCacheErrs := errsToBidderErrors(cacheErrs)
-		bidResponseExt.Errors[openrtb_ext.PrebidExtKey] = append(bidResponseExt.Errors[openrtb_ext.PrebidExtKey], bidderCacheErrs...)
-	}
-
-	if debugLog != nil && debugLog.Enabled {
-		if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil {
-			debugLog.Data.Response = string(bidRespExtBytes)
-		} else {
-			debugLog.Data.Response = "Unable to marshal response ext for debugging"
-			errs = append(errs, err)
-		}
-		if !anyBidsReturned {
-			if rawUUID, err := uuid.NewV4(); err == nil {
-				debugLog.CacheKey = rawUUID.String()
+			if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil {
+				debugLog.Data.Response = string(bidRespExtBytes)
 			} else {
+				debugLog.Data.Response = "Unable to marshal response ext for debugging"
 				errs = append(errs, err)
 			}
 		}
@@ -342,7 +351,8 @@ func (e *exchange) getAllBids(
 	ctx context.Context,
 	bidderRequests []BidderRequest,
 	bidAdjustments map[string]float64,
-	conversions currency.Conversions) (
+	conversions currency.Conversions,
+	accountDebugAllowed bool) (
 	map[openrtb_ext.BidderName]*pbsOrtbSeatBid,
 	map[openrtb_ext.BidderName]*seatResponseExtra, bool) {
 	// Set up pointers to the bid results
@@ -373,7 +383,7 @@ func (e *exchange) getAllBids(
 			}
 			var reqInfo adapters.ExtraRequestInfo
 			reqInfo.PbsEntryPoint = bidderRequest.BidderLabels.RType
-			bids, err := e.adapterMap[bidderRequest.BidderCoreName].requestBid(ctx, bidderRequest.BidRequest, bidderRequest.BidderName, adjustmentFactor, conversions, &reqInfo)
+			bids, err := e.adapterMap[bidderRequest.BidderCoreName].requestBid(ctx, bidderRequest.BidRequest, bidderRequest.BidderName, adjustmentFactor, conversions, &reqInfo, accountDebugAllowed)
 
 			// Add in time reporting
 			elapsed := time.Since(start)
@@ -758,7 +768,7 @@ func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pb
 
 	for bidderName, responseExtra := range adapterExtra {
 
-		if debugInfo {
+		if debugInfo && len(responseExtra.HttpCalls) > 0 {
 			bidResponseExt.Debug.HttpCalls[bidderName] = responseExtra.HttpCalls
 		}
 		// Only make an entry for bidder errors if the bidder reported any.
diff --git a/exchange/legacy.go b/exchange/legacy.go
index dc81e7a1..b4845b76 100644
--- a/exchange/legacy.go
+++ b/exchange/legacy.go
@@ -33,7 +33,7 @@ type adaptedAdapter struct {
 //
 // This is not ideal. OpenRTB provides a superset of the legacy data structures.
 // For requests which use those features, the best we can do is respond with "no bid".
-func (bidder *adaptedAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) {
+func (bidder *adaptedAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
 	legacyRequest, legacyBidder, errs := bidder.toLegacyAdapterInputs(request, name)
 	if legacyRequest == nil || legacyBidder == nil {
 		return nil, errs
diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go
index bfc238e1..f24fd6c5 100644
--- a/gdpr/gdpr.go
+++ b/gdpr/gdpr.go
@@ -3,9 +3,11 @@ package gdpr
 import (
 	"context"
 	"net/http"
+	"strconv"
 
 	"github.com/prebid/go-gdpr/vendorlist"
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
@@ -13,12 +15,12 @@ type Permissions interface {
 	// Determines whether or not the host company is allowed to read/write cookies.
 	//
 	// If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent.
-	HostCookiesAllowed(ctx context.Context, consent string) (bool, error)
+	HostCookiesAllowed(ctx context.Context, gdprSignal Signal, consent string) (bool, error)
 
 	// Determines whether or not the given bidder is allowed to user personal info for ad targeting.
 	//
 	// If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent.
-	BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error)
+	BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal Signal, consent string) (bool, error)
 
 	// Determines whether or not to send PI information to a bidder, or mask it out.
 	//
@@ -65,3 +67,21 @@ type ErrorMalformedConsent struct {
 func (e *ErrorMalformedConsent) Error() string {
 	return "malformed consent string " + e.consent + ": " + e.cause.Error()
 }
+
+// SignalParse parses a raw signal and returns
+func SignalParse(rawSignal string) (Signal, error) {
+	if rawSignal == "" {
+		return SignalAmbiguous, nil
+	}
+
+	i, err := strconv.Atoi(rawSignal)
+
+	if err != nil {
+		return SignalAmbiguous, err
+	}
+	if i != 0 && i != 1 {
+		return SignalAmbiguous, &errortypes.BadInput{Message: "GDPR signal should be integer 0 or 1"}
+	}
+
+	return Signal(i), nil
+}
diff --git a/gdpr/impl.go b/gdpr/impl.go
index c7998251..06b625da 100644
--- a/gdpr/impl.go
+++ b/gdpr/impl.go
@@ -33,20 +33,28 @@ type permissionsImpl struct {
 	fetchVendorList map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error)
 }
 
-func (p *permissionsImpl) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) {
+func (p *permissionsImpl) HostCookiesAllowed(ctx context.Context, gdprSignal Signal, consent string) (bool, error) {
+	gdprSignal = p.normalizeGDPR(gdprSignal)
+
+	if gdprSignal == SignalNo {
+		return true, nil
+	}
+
 	return p.allowSync(ctx, uint16(p.cfg.HostVendorID), consent)
 }
 
-func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) {
+func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal Signal, consent string) (bool, error) {
+	gdprSignal = p.normalizeGDPR(gdprSignal)
+
+	if gdprSignal == SignalNo {
+		return true, nil
+	}
+
 	id, ok := p.vendorIDs[bidder]
 	if ok {
 		return p.allowSync(ctx, id, consent)
 	}
 
-	if consent == "" {
-		return p.cfg.UsersyncIfAmbiguous, nil
-	}
-
 	return false, nil
 }
 
@@ -55,13 +63,7 @@ func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrt
 		return true, true, true, nil
 	}
 
-	if gdprSignal == SignalAmbiguous {
-		if p.cfg.UsersyncIfAmbiguous {
-			gdprSignal = SignalNo
-		} else {
-			gdprSignal = SignalYes
-		}
-	}
+	gdprSignal = p.normalizeGDPR(gdprSignal)
 
 	if gdprSignal == SignalNo {
 		return true, true, true, nil
@@ -82,10 +84,22 @@ func (p *permissionsImpl) defaultVendorPermissions() (allowPI bool, allowGeo boo
 	return false, false, false, nil
 }
 
+func (p *permissionsImpl) normalizeGDPR(gdprSignal Signal) Signal {
+	if gdprSignal != SignalAmbiguous {
+		return gdprSignal
+	}
+
+	if p.cfg.UsersyncIfAmbiguous {
+		return SignalNo
+	}
+
+	return SignalYes
+}
+
 func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, consent string) (bool, error) {
-	// If we're not given a consent string, respect the preferences in the app config.
+
 	if consent == "" {
-		return p.cfg.UsersyncIfAmbiguous, nil
+		return false, nil
 	}
 
 	parsedConsent, vendor, err := p.parseVendor(ctx, vendorID, consent)
@@ -229,18 +243,18 @@ type AllowHostCookies struct {
 }
 
 // HostCookiesAllowed always returns true
-func (p *AllowHostCookies) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) {
+func (p *AllowHostCookies) HostCookiesAllowed(ctx context.Context, gdprSignal Signal, consent string) (bool, error) {
 	return true, nil
 }
 
 // Exporting to allow for easy test setups
 type AlwaysAllow struct{}
 
-func (a AlwaysAllow) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) {
+func (a AlwaysAllow) HostCookiesAllowed(ctx context.Context, gdprSignal Signal, consent string) (bool, error) {
 	return true, nil
 }
 
-func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) {
+func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal Signal, consent string) (bool, error) {
 	return true, nil
 }
 
@@ -251,11 +265,11 @@ func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext
 // Exporting to allow for easy test setups
 type AlwaysFail struct{}
 
-func (a AlwaysFail) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) {
+func (a AlwaysFail) HostCookiesAllowed(ctx context.Context, gdprSignal Signal, consent string) (bool, error) {
 	return false, nil
 }
 
-func (a AlwaysFail) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) {
+func (a AlwaysFail) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal Signal, consent string) (bool, error) {
 	return false, nil
 }
 
diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go
index de6da7b4..95a17109 100644
--- a/gdpr/vendorlist-fetching.go
+++ b/gdpr/vendorlist-fetching.go
@@ -28,22 +28,21 @@ type saveVendors func(uint16, api.VendorList)
 
 func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http.Client, urlMaker func(uint16, uint8) string, tcfSpecVersion uint8) func(ctx context.Context, id uint16) (vendorlist.VendorList, error) {
 	var fallback api.VendorList
-	if tcfSpecVersion == tcf1SpecVersion && len(cfg.TCF1.FallbackGVLPath) > 0 {
-		fallback = loadFallbackGVL(cfg.TCF1.FallbackGVLPath)
-	}
 
-	// If we are not going to try fetching the GVL dynamically, we have a simple fetcher.
-	if !cfg.TCF1.FetchGVL && tcfSpecVersion == tcf1SpecVersion {
-		if fallback != nil {
-			return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) {
-				return fallback, nil
-			}
-		}
+	if tcfSpecVersion == tcf1SpecVersion && len(cfg.TCF1.FallbackGVLPath) == 0 {
 		return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) {
 			return nil, makeVendorListNotFoundError(vendorListVersion)
 		}
 	}
 
+	if tcfSpecVersion == tcf1SpecVersion {
+		fallback = loadFallbackGVL(cfg.TCF1.FallbackGVLPath)
+
+		return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) {
+			return fallback, nil
+		}
+	}
+
 	cacheSave, cacheLoad := newVendorListCache(fallback)
 
 	preloadContext, cancel := context.WithTimeout(initCtx, cfg.Timeouts.InitTimeout())
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index c0839632..576b1d1a 100755
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -74,6 +74,7 @@ const (
 	BidderCpmstar          BidderName = "cpmstar"
 	BidderDatablocks       BidderName = "datablocks"
 	BidderDmx              BidderName = "dmx"
+	BidderDecenterAds      BidderName = "decenterads"
 	BidderDeepintent       BidderName = "deepintent"
 	BidderEmxDigital       BidderName = "emx_digital"
 	BidderEngageBDR        BidderName = "engagebdr"
@@ -173,6 +174,7 @@ func CoreBidderNames() []BidderName {
 		BidderConversant,
 		BidderCpmstar,
 		BidderDatablocks,
+		BidderDecenterAds,
 		BidderDeepintent,
 		BidderDmx,
 		BidderEmxDigital,
diff --git a/openrtb_ext/imp_decenterads.go b/openrtb_ext/imp_decenterads.go
new file mode 100644
index 00000000..101b85c1
--- /dev/null
+++ b/openrtb_ext/imp_decenterads.go
@@ -0,0 +1,5 @@
+package openrtb_ext
+
+type ExtImpDecenterAds struct {
+	PlacementID string `json:"placementId"`
+}
diff --git a/openrtb_ext/imp_pulsepoint.go b/openrtb_ext/imp_pulsepoint.go
new file mode 100644
index 00000000..c168c80f
--- /dev/null
+++ b/openrtb_ext/imp_pulsepoint.go
@@ -0,0 +1,9 @@
+package openrtb_ext
+
+// ExtImpPulsePoint defines the json spec for bidrequest.imp[i].ext.pulsepoint
+// PubId/TagId are mandatory params
+
+type ExtImpPulsePoint struct {
+	PubID int `json:"cp"`
+	TagID int `json:"ct"`
+}
diff --git a/openrtb_ext/response.go b/openrtb_ext/response.go
index 589dad40..02370d19 100644
--- a/openrtb_ext/response.go
+++ b/openrtb_ext/response.go
@@ -55,10 +55,11 @@ type ExtBidderError struct {
 
 // ExtHttpCall defines the contract for a bidresponse.ext.debug.httpcalls.{bidder}[i]
 type ExtHttpCall struct {
-	Uri          string `json:"uri"`
-	RequestBody  string `json:"requestbody"`
-	ResponseBody string `json:"responsebody"`
-	Status       int    `json:"status"`
+	Uri            string              `json:"uri"`
+	RequestBody    string              `json:"requestbody"`
+	RequestHeaders map[string][]string `json:"requestheaders"`
+	ResponseBody   string              `json:"responsebody"`
+	Status         int                 `json:"status"`
 }
 
 // CookieStatus describes the allowed values for bidresponse.ext.usersync.{bidder}.status
diff --git a/static/bidder-info/decenterads.yaml b/static/bidder-info/decenterads.yaml
new file mode 100644
index 00000000..c9b349cd
--- /dev/null
+++ b/static/bidder-info/decenterads.yaml
@@ -0,0 +1,14 @@
+maintainer:
+  email: "support@decenterads.com"
+modifyingVastXmlAllowed: true
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
diff --git a/static/bidder-info/pulsepoint.yaml b/static/bidder-info/pulsepoint.yaml
index 716e4530..056a0bf3 100644
--- a/static/bidder-info/pulsepoint.yaml
+++ b/static/bidder-info/pulsepoint.yaml
@@ -4,6 +4,12 @@ capabilities:
   app:
     mediaTypes:
       - banner
+      - video
+      - audio
+      - native
   site:
     mediaTypes:
       - banner
+      - video
+      - audio
+      - native
diff --git a/static/bidder-params/decenterads.json b/static/bidder-params/decenterads.json
new file mode 100644
index 00000000..970e9903
--- /dev/null
+++ b/static/bidder-params/decenterads.json
@@ -0,0 +1,18 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "DecenterAds Adapter Params",
+  "description": "A schema which validates params accepted by the DecenterAds adapter",
+  "type": "object",
+  "properties": {
+    "placementId": {
+      "type": "string",
+      "minLength": 1,
+      "description": "An ID which identifies the DecenterAds placement"
+    },
+    "customParams": {
+      "type": "object",
+      "description": "User-defined targeting key-value pairs."
+    }
+  },
+  "required" : [ "placementId" ]
+}
diff --git a/static/bidder-params/pulsepoint.json b/static/bidder-params/pulsepoint.json
index c4704c3b..673fd2ef 100644
--- a/static/bidder-params/pulsepoint.json
+++ b/static/bidder-params/pulsepoint.json
@@ -11,12 +11,7 @@
     "ct": {
       "type": "integer",
       "description": "An ID which identifies the ad slot being sold"
-    },
-    "cf": {
-      "type": "string",
-      "pattern": "^[0-9]+[xX][0-9]+$",
-      "description": "The size of the ad slot being sold. This should be a string like 300X250"
     }
   },
-  "required": ["cp", "ct", "cf"]
+  "required": ["cp", "ct"]
 }


Test patch:
diff --git a/adapters/adform/adform_test.go b/adapters/adform/adform_test.go
index 8ce55a12..558453e4 100644
--- a/adapters/adform/adform_test.go
+++ b/adapters/adform/adform_test.go
@@ -27,7 +27,7 @@ import (
 
 func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderAdform, config.Adapter{
-		Endpoint: "http://adx.adform.net/adx"})
+		Endpoint: "https://adx.adform.net/adx"})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
@@ -300,7 +300,7 @@ func preparePrebidRequestBody(requestData aBidInfo, t *testing.T) *bytes.Buffer
 
 func TestOpenRTBRequest(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderAdform, config.Adapter{
-		Endpoint: "http://adx.adform.net"})
+		Endpoint: "https://adx.adform.net"})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
@@ -525,12 +525,6 @@ func getRegs() openrtb.Regs {
 }
 
 func getUserExt() []byte {
-	digitrust := openrtb_ext.ExtUserDigiTrust{
-		ID:   "digitrustId",
-		KeyV: 1,
-		Pref: 0,
-	}
-
 	eids := []openrtb_ext.ExtUserEid{
 		{
 			Source: "test.com",
@@ -556,9 +550,8 @@ func getUserExt() []byte {
 	}
 
 	userExt := openrtb_ext.ExtUser{
-		Eids:      eids,
-		Consent:   "abc",
-		DigiTrust: &digitrust,
+		Eids:    eids,
+		Consent: "abc",
 	}
 	userExtData, err := json.Marshal(userExt)
 	if err == nil {
@@ -630,7 +623,7 @@ func assertAdformServerRequest(testData aBidInfo, r *http.Request, isOpenRtb boo
 	if ok, err := equal(testData.referrer, r.Header.Get("Referer"), "Referer"); !ok {
 		return err
 	}
-	if ok, err := equal(fmt.Sprintf("uid=%s;DigiTrust.v1.identity=eyJpZCI6ImRpZ2l0cnVzdElkIiwidmVyc2lvbiI6MSwia2V5diI6MSwicHJpdmFjeSI6eyJvcHRvdXQiOmZhbHNlfX0", testData.buyerUID), r.Header.Get("Cookie"), "Buyer ID"); !ok {
+	if ok, err := equal(fmt.Sprintf("uid=%s;", testData.buyerUID), r.Header.Get("Cookie"), "Buyer ID"); !ok {
 		return err
 	}
 	return nil
diff --git a/adapters/adform/adformtest/exemplary/multiformat-impression.json b/adapters/adform/adformtest/exemplary/multiformat-impression.json
index efd4aca6..5b3067ab 100644
--- a/adapters/adform/adformtest/exemplary/multiformat-impression.json
+++ b/adapters/adform/adformtest/exemplary/multiformat-impression.json
@@ -35,7 +35,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTEyMzQ1JnJjdXI9VVNE&bWlkPTU0MzIxJnJjdXI9VVNE"
+          "uri": "https://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTEyMzQ1JnJjdXI9VVNE&bWlkPTU0MzIxJnJjdXI9VVNE"
         },
         "mockResponse": {
           "status": 200,
diff --git a/adapters/adform/adformtest/exemplary/single-banner-impression.json b/adapters/adform/adformtest/exemplary/single-banner-impression.json
index fd7f3cde..8a5f81c8 100644
--- a/adapters/adform/adformtest/exemplary/single-banner-impression.json
+++ b/adapters/adform/adformtest/exemplary/single-banner-impression.json
@@ -23,7 +23,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTEyMzQ1JnJjdXI9VVNE"
+        "uri": "https://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTEyMzQ1JnJjdXI9VVNE"
       },
       "mockResponse": {
         "status": 200,
diff --git a/adapters/adform/adformtest/exemplary/single-video-impression.json b/adapters/adform/adformtest/exemplary/single-video-impression.json
index e22977e6..383e091b 100644
--- a/adapters/adform/adformtest/exemplary/single-video-impression.json
+++ b/adapters/adform/adformtest/exemplary/single-video-impression.json
@@ -19,7 +19,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTU0MzIxJnJjdXI9VVNE"
+        "uri": "https://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTU0MzIxJnJjdXI9VVNE"
       },
       "mockResponse": {
         "status": 200,
diff --git a/adapters/adform/adformtest/supplemental/user-nil.json b/adapters/adform/adformtest/supplemental/user-nil.json
index 34b857d5..5f02fe85 100644
--- a/adapters/adform/adformtest/supplemental/user-nil.json
+++ b/adapters/adform/adformtest/supplemental/user-nil.json
@@ -44,7 +44,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=1&gdpr_consent=abc2&ip=&pt=gross&rp=4&stid=&bWlkPTEmcmN1cj1VU0Q"
+        "uri": "https://adx.adform.net/adx?CC=1&fd=1&gdpr=1&gdpr_consent=abc2&ip=&pt=gross&rp=4&stid=&bWlkPTEmcmN1cj1VU0Q"
       },
       "mockResponse": {
         "status": 204
diff --git a/adapters/admixer/usersync_test.go b/adapters/admixer/usersync_test.go
index d31f7b10..79f023a2 100644
--- a/adapters/admixer/usersync_test.go
+++ b/adapters/admixer/usersync_test.go
@@ -11,7 +11,7 @@ import (
 )
 
 func TestAdmixerSyncer(t *testing.T) {
-	syncURL := "https://inv-nets.admixer.net/adxcm.aspx?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=1&rurl=localhost%2Fsetuid%3Fbidder%3Dadmixer%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24"
+	syncURL := "http://anyHost/anyPath"
 	syncURLTemplate := template.Must(
 		template.New("sync-template").Parse(syncURL),
 	)
@@ -28,7 +28,7 @@ func TestAdmixerSyncer(t *testing.T) {
 	})
 
 	assert.NoError(t, err)
-	assert.Equal(t, "https://inv-nets.admixer.net/adxcm.aspx?gdpr=A&gdpr_consent=B&us_privacy=C&redir=1&rurl=localhost%2Fsetuid%3Fbidder%3Dadmixer%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D%24%24visitor_cookie%24%24", syncInfo.URL)
+	assert.Equal(t, "http://anyHost/anyPath", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
 	assert.EqualValues(t, 511, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
diff --git a/adapters/amx/amxtest/exemplary/app-simple.json b/adapters/amx/amxtest/exemplary/app-simple.json
index b2f53849..c208bff9 100644
--- a/adapters/amx/amxtest/exemplary/app-simple.json
+++ b/adapters/amx/amxtest/exemplary/app-simple.json
@@ -64,7 +64,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0",
+        "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1",
         "body": {
           "app": {
             "bundle": "639881495",
diff --git a/adapters/amx/amxtest/exemplary/display-multiple.json b/adapters/amx/amxtest/exemplary/display-multiple.json
new file mode 100644
index 00000000..f885bdb2
--- /dev/null
+++ b/adapters/amx/amxtest/exemplary/display-multiple.json
@@ -0,0 +1,292 @@
+{
+  "mockBidRequest": {
+    "device": {
+      "dnt": 0,
+      "h": 1120,
+      "ip": "98.249.0.0",
+      "language": "en",
+      "os": "macos",
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36",
+      "w": 1792
+    },
+    "id": "TL3JS6F43CKNDQFY",
+    "imp": [
+      {
+        "banner": {
+          "format": [
+            {
+              "h": 600,
+              "w": 300
+            }
+          ],
+          "h": 600,
+          "pos": 1,
+          "w": 300
+        },
+        "ext": {
+          "bidder": {
+            "tagId": "cHJlYmlkLm9yZw"
+          }
+        },
+        "tagid": "example-tag-id",
+        "id": "1",
+        "secure": 1
+      }
+    ],
+    "regs": {
+      "ext": {
+        "gdpr": 0,
+        "us_privacy": "1---"
+      }
+    },
+    "site": {
+      "domain": "www.example.com",
+      "ext": {
+        "amp": 0
+      },
+      "publisher": {
+        "id": "unused_publisher_id"
+      },
+      "page": "https://www.example.com/es6/es6_objects.htm",
+      "ref": "https://www.example.com/es6/es6_objects.htm"
+    },
+    "source": {
+      "ext": {
+        "schain": {
+          "complete": 1,
+          "nodes": [
+            {
+              "asi": "amxrtb.com",
+              "hp": 1,
+              "sid": "1234"
+            }
+          ],
+          "ver": "1.0"
+        }
+      }
+    },
+    "test": 0,
+    "tmax": 300,
+    "user": {
+      "ext": {
+        "eids": [
+          {
+            "source": "amxid",
+            "uids": [
+              {
+                "atype": 1,
+                "id": "88de601e-3d98-48e7-81d7-00000000"
+              }
+            ]
+          },
+          {
+            "source": "adserver.org",
+            "uids": [
+              {
+                "id": "1234567",
+                "ext": {
+                  "rtiPartner": "TDID"
+                }
+              }
+            ]
+          }
+        ],
+        "gdpr": 0,
+        "us_privacy": "1---"
+      }
+    }
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1",
+      "body": {
+        "device": {
+          "dnt": 0,
+          "h": 1120,
+          "ip": "98.249.0.0",
+          "language": "en",
+          "os": "macos",
+          "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36",
+          "w": 1792
+        },
+        "id": "TL3JS6F43CKNDQFY",
+        "imp": [
+          {
+            "banner": {
+              "format": [
+                {
+                  "h": 600,
+                  "w": 300
+                }
+              ],
+              "h": 600,
+              "pos": 1,
+              "w": 300
+            },
+            "ext": {
+              "bidder": {
+                "tagId": "cHJlYmlkLm9yZw"
+              }
+            },
+            "tagid": "example-tag-id",
+            "id": "1",
+            "secure": 1
+          }
+        ],
+        "regs": {
+          "ext": {
+            "gdpr": 0,
+            "us_privacy": "1---"
+          }
+        },
+        "site": {
+          "domain": "www.example.com",
+          "ext": {
+            "amp": 0
+          },
+          "publisher": {
+            "id": "cHJlYmlkLm9yZw"
+          },
+          "page": "https://www.example.com/es6/es6_objects.htm",
+          "ref": "https://www.example.com/es6/es6_objects.htm"
+        },
+        "source": {
+          "ext": {
+            "schain": {
+              "complete": 1,
+              "nodes": [
+                {
+                  "asi": "amxrtb.com",
+                  "hp": 1,
+                  "sid": "1234"
+                }
+              ],
+              "ver": "1.0"
+            }
+          }
+        },
+        "tmax": 300,
+        "user": {
+          "ext": {
+            "eids": [
+              {
+                "source": "amxid",
+                "uids": [
+                  {
+                    "atype": 1,
+                    "id": "88de601e-3d98-48e7-81d7-00000000"
+                  }
+                ]
+              },
+              {
+                "source": "adserver.org",
+                "uids": [
+                  {
+                    "id": "1234567",
+                    "ext": {
+                      "rtiPartner": "TDID"
+                    }
+                  }
+                ]
+              }
+            ],
+            "gdpr": 0,
+            "us_privacy": "1---"
+          }
+        }
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "cur": "USD",
+        "id": "os4jorvupkyaa===",
+        "seatbid": [
+          {
+            "bid": [
+              {
+                "adid": "253510977",
+                "adm": "<i></i><script>_c=[];</script><script src=\"https://assets.a-mo.net/js/c.js\"></script>",
+                "adomain": [
+                  "dell.com.au"
+                ],
+                "cid": "668",
+                "crid": "253510977",
+                "ext": {
+                },
+                "h": 250,
+                "id": "8911104898220857797",
+                "impid": "6a362d3a9db4eba300x250",
+                "nurl": "https://1x1.a-mo.net/hbx/bwin",
+                "price": 0.50,
+                "w": 300
+              },
+              {
+                "adid": "253510977",
+                "adm": "<i></i><script>_c=[];</script><script src=\"https://assets.a-mo.net/js/c.js\"></script>",
+                "adomain": [
+                  "dell.com.au"
+                ],
+                "cid": "668",
+                "crid": "253510977",
+                "ext": {},
+                "h": 250,
+                "id": "430444686086263488",
+                "impid": "6a362d3a9db4eba300x250",
+                "nurl": "https://1x1.a-mo.net/hbx/bwin",
+                "price": 0.50,
+                "w": 300
+              }
+            ],
+            "group": 0
+          }
+        ]
+      }
+    }
+  }],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "adid": "253510977",
+            "adm": "<i></i><script>_c=[];</script><script src=\"https://assets.a-mo.net/js/c.js\"></script>",
+            "adomain": [
+              "dell.com.au"
+            ],
+            "cid": "668",
+            "crid": "253510977",
+            "ext": {},
+            "h": 250,
+            "id": "8911104898220857797",
+            "impid": "6a362d3a9db4eba300x250",
+            "nurl": "https://1x1.a-mo.net/hbx/bwin",
+            "price": 0.50,
+            "w": 300
+          },
+          "type": "banner"
+        },
+        {
+          "bid": {
+            "adid": "253510977",
+            "adm": "<i></i><script>_c=[];</script><script src=\"https://assets.a-mo.net/js/c.js\"></script>",
+            "adomain": [
+              "dell.com.au"
+            ],
+            "cid": "668",
+            "crid": "253510977",
+            "ext": {},
+            "h": 250,
+            "id": "430444686086263488",
+            "impid": "6a362d3a9db4eba300x250",
+            "nurl": "https://1x1.a-mo.net/hbx/bwin",
+            "price": 0.50,
+            "w": 300
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/amx/amxtest/exemplary/video-simple.json b/adapters/amx/amxtest/exemplary/video-simple.json
index 8fb3baa2..678722ad 100644
--- a/adapters/amx/amxtest/exemplary/video-simple.json
+++ b/adapters/amx/amxtest/exemplary/video-simple.json
@@ -93,7 +93,7 @@
   },
   "httpCalls": [{
     "expectedRequest": {
-      "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0",
+      "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1",
       "body": {
         "device": {
           "dnt": 0,
diff --git a/adapters/amx/amxtest/exemplary/web-simple.json b/adapters/amx/amxtest/exemplary/web-simple.json
index 74854f91..f75bd85a 100644
--- a/adapters/amx/amxtest/exemplary/web-simple.json
+++ b/adapters/amx/amxtest/exemplary/web-simple.json
@@ -98,7 +98,7 @@
   },
   "httpCalls": [{
     "expectedRequest": {
-      "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0",
+      "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1",
       "body": {
         "device": {
           "dnt": 0,
diff --git a/adapters/amx/amxtest/supplemental/204-response.json b/adapters/amx/amxtest/supplemental/204-response.json
index 09571a03..f51347d3 100644
--- a/adapters/amx/amxtest/supplemental/204-response.json
+++ b/adapters/amx/amxtest/supplemental/204-response.json
@@ -47,7 +47,7 @@
   },
   "httpCalls": [{
     "expectedRequest": {
-      "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0",
+      "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1",
       "body": {
         "device": {
           "dnt": 0,
diff --git a/adapters/amx/amxtest/supplemental/400-response.json b/adapters/amx/amxtest/supplemental/400-response.json
index f10cea89..bb20bc94 100644
--- a/adapters/amx/amxtest/supplemental/400-response.json
+++ b/adapters/amx/amxtest/supplemental/400-response.json
@@ -47,7 +47,7 @@
   },
   "httpCalls": [{
     "expectedRequest": {
-      "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0",
+      "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1",
       "body": {
         "device": {
           "dnt": 0,
diff --git a/adapters/amx/amxtest/supplemental/500-response.json b/adapters/amx/amxtest/supplemental/500-response.json
index fe5d8993..c56a217c 100644
--- a/adapters/amx/amxtest/supplemental/500-response.json
+++ b/adapters/amx/amxtest/supplemental/500-response.json
@@ -47,7 +47,7 @@
   },
   "httpCalls": [{
     "expectedRequest": {
-      "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0",
+      "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1",
       "body": {
         "device": {
           "dnt": 0,
diff --git a/adapters/decenterads/decenterads_test.go b/adapters/decenterads/decenterads_test.go
new file mode 100644
index 00000000..ca86e891
--- /dev/null
+++ b/adapters/decenterads/decenterads_test.go
@@ -0,0 +1,18 @@
+package decenterads
+
+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.BidderDecenterAds, config.Adapter{
+		Endpoint: "http://example.com/?c=o&m=ortb"})
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+	adapterstest.RunJSONBidderTest(t, "decenteradstest", bidder)
+}
diff --git a/adapters/decenterads/decenteradstest/exemplary/simple-banner.json b/adapters/decenterads/decenteradstest/exemplary/simple-banner.json
new file mode 100644
index 00000000..fd2c09d0
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/exemplary/simple-banner.json
@@ -0,0 +1,128 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+        {
+            "id": "test-imp-id",
+            "banner": {
+                "format": [
+                    {
+                      "w": 300,
+                      "h": 250
+                    },
+                    {
+                      "w": 300,
+                      "h": 600
+                    }
+                ]
+            },
+            "ext": {
+                "bidder": {
+                  "placementId": "3"
+                }
+            }
+        }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    }
+},
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+              {
+                "id": "test-imp-id",
+                "banner": {
+                  "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+                },
+              "ext": {
+                "placementId": "3"
+              }
+            }
+          ],
+          "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 id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://supply.decenterads.com/?c=o&m=adm&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                    "cid": "test_cid",
+                    "crid": "test_crid",
+                    "dealid": "test_dealid",
+                    "w": 300,
+                    "h": 250,
+                    "ext": {
+                      "prebid": {
+                        "type": "banner"
+                      }
+                    }
+                  }
+                ]
+              }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://supply.decenterads.com/?c=o&m=adm&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/decenterads/decenteradstest/exemplary/simple-video.json b/adapters/decenterads/decenteradstest/exemplary/simple-video.json
new file mode 100644
index 00000000..b85d302b
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/exemplary/simple-video.json
@@ -0,0 +1,116 @@
+{
+  "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"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "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": {
+                "placementId": "3"
+              }
+            }
+          ]
+        }
+      },
+      "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\"><Ad id=\"20001\" sequence=\"1\"><InLine><AdSystem version=\"4.0\"><![CDATA[iabtechlab]]></AdSystem><AdTitle><![CDATA[Inline Simple Ad]]></AdTitle><Impression><![CDATA[]]></Impression><Creatives><Creative id=\"5480\" sequence=\"1\"><Linear><Duration>00:01:00</Duration><MediaFiles><MediaFile id=\"5246\" delivery=\"progressive\" type=\"video/mp4\" bitrate=\"600\" width=\"640\" height=\"360\" minBitrate=\"500\" maxBitrate=\"700\" scalable=\"1\" maintainAspectRatio=\"1\" codec=\"0\"><![CDATA[https://s0.2mdn.net/4253510/google_ddm_animation_480P.mp4]]></MediaFile></MediaFiles><VideoClicks><ClickThrough id=\"blog\"><![CDATA[https://example.com]]></ClickThrough></VideoClicks></Linear></Creative></Creatives></InLine></Ad></VAST>",
+                "cid": "test_cid",
+                "crid": "test_crid",
+                "dealid": "test_dealid",
+                "ext": {
+                  "prebid": {
+                    "type": "video"
+                  }
+                }
+              }
+            ],
+            "seat": "decenterads"
+          }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "<VAST version=\"3.0\"><Ad id=\"20001\" sequence=\"1\"><InLine><AdSystem version=\"4.0\"><![CDATA[iabtechlab]]></AdSystem><AdTitle><![CDATA[Inline Simple Ad]]></AdTitle><Impression><![CDATA[]]></Impression><Creatives><Creative id=\"5480\" sequence=\"1\"><Linear><Duration>00:01:00</Duration><MediaFiles><MediaFile id=\"5246\" delivery=\"progressive\" type=\"video/mp4\" bitrate=\"600\" width=\"640\" height=\"360\" minBitrate=\"500\" maxBitrate=\"700\" scalable=\"1\" maintainAspectRatio=\"1\" codec=\"0\"><![CDATA[https://s0.2mdn.net/4253510/google_ddm_animation_480P.mp4]]></MediaFile></MediaFiles><VideoClicks><ClickThrough id=\"blog\"><![CDATA[https://example.com]]></ClickThrough></VideoClicks></Linear></Creative></Creatives></InLine></Ad></VAST>",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/decenterads/decenteradstest/exemplary/simple-web-banner.json b/adapters/decenterads/decenteradstest/exemplary/simple-web-banner.json
new file mode 100644
index 00000000..8f8aa0e9
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/exemplary/simple-web-banner.json
@@ -0,0 +1,126 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "3"
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "1",
+      "domain": "test.com"
+    },
+    "device": {
+      "ip": "123.123.123.123"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "placementId": "3"
+              }
+            }
+          ],
+          "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 id=\"adm-banner-1\" width=\"468\" height=\"60\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://supply.decenterads.com/?c=o&m=adm&k=bc2d316f39931a07d9a8dd249bf85fc0\"></iframe>",
+                  "cid": "test_cid",
+                  "crid": "test_crid",
+                  "dealid": "test_dealid",
+                  "w": 468,
+                  "h": 60,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }
+              ],
+              "seat": "decenterads"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "<iframe id=\"adm-banner-1\" width=\"468\" height=\"60\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://supply.decenterads.com/?c=o&m=adm&k=bc2d316f39931a07d9a8dd249bf85fc0\"></iframe>",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "w": 468,
+            "h": 60,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/decenterads/decenteradstest/params/race/banner.json b/adapters/decenterads/decenteradstest/params/race/banner.json
new file mode 100644
index 00000000..dbdac1ad
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/params/race/banner.json
@@ -0,0 +1,3 @@
+{
+  "TagID": "6"
+}
\ No newline at end of file
diff --git a/adapters/decenterads/decenteradstest/params/race/video.json b/adapters/decenterads/decenteradstest/params/race/video.json
new file mode 100644
index 00000000..6e2e0b38
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/params/race/video.json
@@ -0,0 +1,3 @@
+{
+  "TagID": "7"
+}
\ No newline at end of file
diff --git a/adapters/decenterads/decenteradstest/supplemental/bad-imp-ext.json b/adapters/decenterads/decenteradstest/supplemental/bad-imp-ext.json
new file mode 100644
index 00000000..83455ca7
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/supplemental/bad-imp-ext.json
@@ -0,0 +1,41 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "decenterads": {
+            "placementId": "1"
+          }
+        }
+      }
+    ],
+    "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/decenterads/decenteradstest/supplemental/bad_response.json b/adapters/decenterads/decenteradstest/supplemental/bad_response.json
new file mode 100644
index 00000000..9d9d977b
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/supplemental/bad_response.json
@@ -0,0 +1,83 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "1"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "placementId": "1"
+              }
+            }
+          ],
+          "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 openrtb.BidResponse",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/decenterads/decenteradstest/supplemental/bad_status_code.json b/adapters/decenterads/decenteradstest/supplemental/bad_status_code.json
new file mode 100644
index 00000000..4a81da36
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/supplemental/bad_status_code.json
@@ -0,0 +1,77 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "1"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {}
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "placementId": "1"
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {}
+        }
+      },
+      "mockResponse": {
+        "status": 400,
+        "body": {}
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "unexpected status code: 400",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/decenterads/decenteradstest/supplemental/imp_ext_empty_object.json b/adapters/decenterads/decenteradstest/supplemental/imp_ext_empty_object.json
new file mode 100644
index 00000000..db4053f2
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/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/decenterads/decenteradstest/supplemental/imp_ext_string.json b/adapters/decenterads/decenteradstest/supplemental/imp_ext_string.json
new file mode 100644
index 00000000..db938d6a
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/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": "unable to parse bidder parameers: json: cannot unmarshal string into Go value of type map[string]json.RawMessage",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/decenterads/decenteradstest/supplemental/status-204.json b/adapters/decenterads/decenteradstest/supplemental/status-204.json
new file mode 100644
index 00000000..3eff0fdc
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/supplemental/status-204.json
@@ -0,0 +1,78 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "1"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "placementId": "1"
+              }
+            }
+          ],
+          "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/decenterads/decenteradstest/supplemental/status-404.json b/adapters/decenterads/decenteradstest/supplemental/status-404.json
new file mode 100644
index 00000000..a162dd89
--- /dev/null
+++ b/adapters/decenterads/decenteradstest/supplemental/status-404.json
@@ -0,0 +1,83 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "1"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "ext": {
+                "placementId": "1"
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 404,
+        "body": {}
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "unexpected status code: 404",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/decenterads/params_test.go b/adapters/decenterads/params_test.go
new file mode 100644
index 00000000..3d3708be
--- /dev/null
+++ b/adapters/decenterads/params_test.go
@@ -0,0 +1,48 @@
+package decenterads
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// TestValidParams makes sure that the decenterads 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.BidderDecenterAds, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected decenterads params: %s", validParam)
+		}
+	}
+}
+
+// TestInvalidParams makes sure that the decenterads 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.BidderDecenterAds, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"placementId": "11"}`,
+}
+
+var invalidParams = []string{
+	`{"id": "456"}`,
+	`{"placementid": "3456"}`,
+	`{"placement_id": 346}`,
+	`{"placementID": ""}`,
+	`{"placementId": 234}`,
+}
diff --git a/adapters/eplanning/eplanningtest/supplemental/banner-2-sizes-in-priority-list-mobile.json b/adapters/eplanning/eplanningtest/supplemental/banner-2-sizes-in-priority-list-mobile.json
new file mode 100644
index 00000000..b1f0677c
--- /dev/null
+++ b/adapters/eplanning/eplanningtest/supplemental/banner-2-sizes-in-priority-list-mobile.json
@@ -0,0 +1,81 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "devicetype": 4
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 1,
+              "h": 1
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "ci": "12345"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=300x250%3A300x250&ncb=1&ur=FILE",
+        "body": {}
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "sI": { "k": "12345" },
+          "sec": "ROS",
+          "sp": [
+            {
+              "k": "300x250",
+              "a": [{
+                "i": "123456789abcdef",
+                "pr": "0.5",
+                "adm": "<div>test</div>",
+                "crid": "abcdef123456789",
+                "id": "adid12345",
+                "w": 300,
+                "h": 250
+              }]
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "123456789abcdef",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "<div>test</div>",
+            "adid": "adid12345",
+            "crid": "abcdef123456789",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/eplanning/eplanningtest/supplemental/banner-3-sizes-in-priority-list-desktop.json b/adapters/eplanning/eplanningtest/supplemental/banner-3-sizes-in-priority-list-desktop.json
new file mode 100644
index 00000000..31197c38
--- /dev/null
+++ b/adapters/eplanning/eplanningtest/supplemental/banner-3-sizes-in-priority-list-desktop.json
@@ -0,0 +1,85 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "devicetype": 2
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 970,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            },
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "ci": "12345"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=300x250%3A300x250&ncb=1&ur=FILE",
+        "body": {}
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "sI": { "k": "12345" },
+          "sec": "ROS",
+          "sp": [
+            {
+              "k": "300x250",
+              "a": [{
+                "i": "123456789abcdef",
+                "pr": "0.5",
+                "adm": "<div>test</div>",
+                "crid": "abcdef123456789",
+                "id": "adid12345",
+                "w": 300,
+                "h": 250
+              }]
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "123456789abcdef",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "<div>test</div>",
+            "adid": "adid12345",
+            "crid": "abcdef123456789",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/eplanning/eplanningtest/supplemental/banner-3-sizes-in-priority-list-mobile.json b/adapters/eplanning/eplanningtest/supplemental/banner-3-sizes-in-priority-list-mobile.json
new file mode 100644
index 00000000..661955e8
--- /dev/null
+++ b/adapters/eplanning/eplanningtest/supplemental/banner-3-sizes-in-priority-list-mobile.json
@@ -0,0 +1,85 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "devicetype": 4
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 1,
+              "h": 1
+            },
+            {
+              "w": 300,
+              "h": 50
+            },
+            {
+              "w": 320,
+              "h": 50
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "ci": "12345"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=320x50%3A320x50&ncb=1&ur=FILE",
+        "body": {}
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "sI": { "k": "12345" },
+          "sec": "ROS",
+          "sp": [
+            {
+              "k": "320x50",
+              "a": [{
+                "i": "123456789abcdef",
+                "pr": "0.5",
+                "adm": "<div>test</div>",
+                "crid": "abcdef123456789",
+                "id": "adid12345",
+                "w": 320,
+                "h": 50
+              }]
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "123456789abcdef",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "<div>test</div>",
+            "adid": "adid12345",
+            "crid": "abcdef123456789",
+            "w": 320,
+            "h": 50
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/eplanning/eplanningtest/supplemental/banner-3-sizes-inout-priority-list-mobile.json b/adapters/eplanning/eplanningtest/supplemental/banner-3-sizes-inout-priority-list-mobile.json
new file mode 100644
index 00000000..f13df90d
--- /dev/null
+++ b/adapters/eplanning/eplanningtest/supplemental/banner-3-sizes-inout-priority-list-mobile.json
@@ -0,0 +1,85 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "devicetype": 1
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 970,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 70
+            },
+            {
+              "w": 160,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "ci": "12345"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=970x250%3A970x250&ncb=1&ur=FILE",
+        "body": {}
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "sI": { "k": "12345" },
+          "sec": "ROS",
+          "sp": [
+            {
+              "k": "970x250",
+              "a": [{
+                "i": "123456789abcdef",
+                "pr": "0.5",
+                "adm": "<div>test</div>",
+                "crid": "abcdef123456789",
+                "id": "adid12345",
+                "w": 970,
+                "h": 250
+              }]
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "123456789abcdef",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "<div>test</div>",
+            "adid": "adid12345",
+            "crid": "abcdef123456789",
+            "w": 970,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/eplanning/eplanningtest/supplemental/banner-4-sizes-in-priority-list-desktop.json b/adapters/eplanning/eplanningtest/supplemental/banner-4-sizes-in-priority-list-desktop.json
new file mode 100644
index 00000000..103be4e4
--- /dev/null
+++ b/adapters/eplanning/eplanningtest/supplemental/banner-4-sizes-in-priority-list-desktop.json
@@ -0,0 +1,89 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "devicetype": 4
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 1,
+              "h": 1
+            },
+            {
+              "w": 300,
+              "h": 50
+            },
+            {
+              "w": 320,
+              "h": 50
+            },
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "ci": "12345"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=300x250%3A300x250&ncb=1&ur=FILE",
+        "body": {}
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "sI": { "k": "12345" },
+          "sec": "ROS",
+          "sp": [
+            {
+              "k": "300x250",
+              "a": [{
+                "i": "123456789abcdef",
+                "pr": "0.5",
+                "adm": "<div>test</div>",
+                "crid": "abcdef123456789",
+                "id": "adid12345",
+                "w": 300,
+                "h": 250
+              }]
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "123456789abcdef",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "<div>test</div>",
+            "adid": "adid12345",
+            "crid": "abcdef123456789",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/pulsepoint/params_test.go b/adapters/pulsepoint/params_test.go
new file mode 100644
index 00000000..ac2b314b
--- /dev/null
+++ b/adapters/pulsepoint/params_test.go
@@ -0,0 +1,59 @@
+package pulsepoint
+
+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-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderPulsepoint, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected pulsepoint params: %s \n Error: %s", validParam, err)
+		}
+	}
+}
+
+// TestInvalidParams makes sure that the pubmatic 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.BidderPulsepoint, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected pulsepoint params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"cp":1000, "ct": 2000}`,
+	`{"cp":1001, "ct": 2001}`,
+	`{"cp":1001, "ct": 2001, "cf": "1x1"}`,
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{"cp":"1000"}`,
+	`{"ct":"1000"}`,
+	`{"cp":1000}`,
+	`{"ct":1000}`,
+	`{"cp":1000, "ct":"1000"}`,
+	`{"cp":1000, "ct": "abcd"}`,
+	`{"cp":"abcd", "ct": 1000}`,
+	`{"cp":"1000.2", "ct": "1000.1"}`,
+}
diff --git a/adapters/pulsepoint/pulsepoint_test.go b/adapters/pulsepoint/pulsepoint_test.go
index 3eede431..33023d05 100644
--- a/adapters/pulsepoint/pulsepoint_test.go
+++ b/adapters/pulsepoint/pulsepoint_test.go
@@ -1,26 +1,43 @@
 package pulsepoint
 
 import (
+	"encoding/json"
+	"net/http"
+	"testing"
+
+	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/openrtb_ext"
+
 	"bytes"
 	"context"
-	"encoding/json"
 	"fmt"
 	"io/ioutil"
-	"net/http"
 	"net/http/httptest"
-	"testing"
 	"time"
 
-	"github.com/mxmCherry/openrtb"
-	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/adapters/adapterstest"
 	"github.com/prebid/prebid-server/cache/dummycache"
 	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/pbs"
 	"github.com/prebid/prebid-server/usersync"
 )
 
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderPulsepoint, config.Adapter{
+		Endpoint: "http://bidder.pulsepoint.com"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "pulsepointtest", bidder)
+}
+
+/////////////////////////////////
+// Legacy implementation: Start
+/////////////////////////////////
+
 /**
  * Verify adapter names are setup correctly.
  */
@@ -75,7 +92,6 @@ func TestPulsePointOpenRTBRequest(t *testing.T) {
 	bidder := req.Bidders[0]
 	adapter := NewPulsePointLegacyAdapter(adapters.DefaultHTTPAdapterConfig, server.URL)
 	adapter.Call(ctx, req, bidder)
-	fmt.Println(service.LastBidRequest)
 	adapterstest.VerifyIntValue(len(service.LastBidRequest.Imp), 1, t)
 	adapterstest.VerifyStringValue(service.LastBidRequest.Imp[0].TagID, "1001", t)
 	adapterstest.VerifyStringValue(service.LastBidRequest.Site.Publisher.ID, "2001", t)
@@ -290,3 +306,7 @@ func CreateService(tagsToBid map[string]bool) adapterstest.OrtbMockService {
 	service.LastBidRequest = &lastBidRequest
 	return service
 }
+
+/////////////////////////////////
+// Legacy implementation: End
+/////////////////////////////////
diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/banner-app.json b/adapters/pulsepoint/pulsepointtest/exemplary/banner-app.json
new file mode 100644
index 00000000..e99ca648
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/exemplary/banner-app.json
@@ -0,0 +1,101 @@
+{
+    "mockBidRequest": {
+        "id": "request-id",
+        "app": {
+            "bundle": "com.publisher.app",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 1001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "request-id",
+                "app": {
+                    "bundle": "com.publisher.app",
+                    "publisher": {
+                        "id": "1234",
+                        "name": "publisher.com"
+                    }
+                },
+                "imp": [{
+                    "id": "banner-1",
+                    "tagid": "1001",
+                    "banner": {
+                        "w": 320,
+                        "h": 50
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 1001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "id": "response-id",
+                "seatbid": [{
+                    "bid": [{
+                        "id": "banner-1-bid",
+                        "impid": "banner-1",
+                        "price": 3.5,
+                        "adm": "<div>Creative</div>",
+                        "adomain": [
+                            "advertiser.com"
+                        ],
+                        "crid": "20",
+                        "w": 300,
+                        "h": 250
+                    }],
+                    "seat": "pulsepoint-seat"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [{
+        "bids": [{
+            "bid": {
+                "id": "banner-1-bid",
+                "impid": "banner-1",
+                "price": 3.5,
+                "adm": "<div>Creative</div>",
+                "adomain": [
+                    "advertiser.com"
+                ],
+                "crid": "20",
+                "w": 300,
+                "h": 250
+            },
+            "type": "banner"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/banner.json b/adapters/pulsepoint/pulsepointtest/exemplary/banner.json
new file mode 100644
index 00000000..d4cf797d
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/exemplary/banner.json
@@ -0,0 +1,101 @@
+{
+    "mockBidRequest": {
+        "id": "request-id",
+        "site": {
+            "page": "http://publisher.com/index.html",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 1001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "request-id",
+                "site": {
+                    "page": "http://publisher.com/index.html",
+                    "publisher": {
+                        "id": "1234",
+                        "name": "publisher.com"
+                    }
+                },
+                "imp": [{
+                    "id": "banner-1",
+                    "tagid": "1001",
+                    "banner": {
+                        "w": 320,
+                        "h": 50
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 1001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "id": "response-id",
+                "seatbid": [{
+                    "bid": [{
+                        "id": "banner-1-bid",
+                        "impid": "banner-1",
+                        "price": 3.5,
+                        "adm": "<div>Creative</div>",
+                        "adomain": [
+                            "advertiser.com"
+                        ],
+                        "crid": "20",
+                        "w": 300,
+                        "h": 250
+                    }],
+                    "seat": "pulsepoint-seat"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [{
+        "bids": [{
+            "bid": {
+                "id": "banner-1-bid",
+                "impid": "banner-1",
+                "price": 3.5,
+                "adm": "<div>Creative</div>",
+                "adomain": [
+                    "advertiser.com"
+                ],
+                "crid": "20",
+                "w": 300,
+                "h": 250
+            },
+            "type": "banner"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-app.json b/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-app.json
new file mode 100644
index 00000000..6e16f997
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-app.json
@@ -0,0 +1,96 @@
+{
+    "mockBidRequest": {
+        "id": "request-id",
+        "app": {
+            "bundle": "com.publisher.app"
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 1001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "request-id",
+                "app": {
+                    "bundle": "com.publisher.app",
+                    "publisher": {
+                        "id": "1234"
+                    }
+                },
+                "imp": [{
+                    "id": "banner-1",
+                    "tagid": "1001",
+                    "banner": {
+                        "w": 320,
+                        "h": 50
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 1001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "id": "response-id",
+                "seatbid": [{
+                    "bid": [{
+                        "id": "banner-1-bid",
+                        "impid": "banner-1",
+                        "price": 3.5,
+                        "adm": "<div>Creative</div>",
+                        "adomain": [
+                            "advertiser.com"
+                        ],
+                        "crid": "20",
+                        "w": 300,
+                        "h": 250
+                    }],
+                    "seat": "pulsepoint-seat"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [{
+        "bids": [{
+            "bid": {
+                "id": "banner-1-bid",
+                "impid": "banner-1",
+                "price": 3.5,
+                "adm": "<div>Creative</div>",
+                "adomain": [
+                    "advertiser.com"
+                ],
+                "crid": "20",
+                "w": 300,
+                "h": 250
+            },
+            "type": "banner"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-site.json b/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-site.json
new file mode 100644
index 00000000..6d658a24
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-site.json
@@ -0,0 +1,96 @@
+{
+    "mockBidRequest": {
+        "id": "request-id",
+        "site": {
+            "page": "http://publisher.com/index.html"
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 1001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "request-id",
+                "site": {
+                    "page": "http://publisher.com/index.html",
+                    "publisher": {
+                        "id": "1234"
+                    }
+                },
+                "imp": [{
+                    "id": "banner-1",
+                    "tagid": "1001",
+                    "banner": {
+                        "w": 320,
+                        "h": 50
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 1001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "id": "response-id",
+                "seatbid": [{
+                    "bid": [{
+                        "id": "banner-1-bid",
+                        "impid": "banner-1",
+                        "price": 3.5,
+                        "adm": "<div>Creative</div>",
+                        "adomain": [
+                            "advertiser.com"
+                        ],
+                        "crid": "20",
+                        "w": 300,
+                        "h": 250
+                    }],
+                    "seat": "pulsepoint-seat"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [{
+        "bids": [{
+            "bid": {
+                "id": "banner-1-bid",
+                "impid": "banner-1",
+                "price": 3.5,
+                "adm": "<div>Creative</div>",
+                "adomain": [
+                    "advertiser.com"
+                ],
+                "crid": "20",
+                "w": 300,
+                "h": 250
+            },
+            "type": "banner"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/multi-imps.json b/adapters/pulsepoint/pulsepointtest/exemplary/multi-imps.json
new file mode 100644
index 00000000..1a103553
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/exemplary/multi-imps.json
@@ -0,0 +1,151 @@
+{
+    "mockBidRequest": {
+        "id": "some-request-id",
+        "site": {
+            "page": "http://publisher.com/index.html",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 1001
+                }
+            }
+        }, {
+            "id": "video-1",
+            "video": {
+                "w": 400,
+                "h": 300,
+                "mimes": ["video/x-flv"],
+                "minduration": 20
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 2001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "some-request-id",
+                "site": {
+                    "page": "http://publisher.com/index.html",
+                    "publisher": {
+                        "id": "1234",
+                        "name": "publisher.com"
+                    }
+                },
+                "imp": [{
+                    "id": "banner-1",
+                    "tagid": "1001",
+                    "banner": {
+                        "w": 320,
+                        "h": 50
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 1001
+                        }
+                    }
+                }, {
+                    "id": "video-1",
+                    "tagid": "2001",
+                    "video": {
+                        "w": 400,
+                        "h": 300,
+                        "mimes": ["video/x-flv"],
+                        "minduration": 20
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 2001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "id": "response-id",
+                "seatbid": [{
+                    "bid": [{
+                        "id": "banner-1-bid",
+                        "impid": "banner-1",
+                        "price": 3.5,
+                        "adm": "<div>Creative</div>",
+                        "adomain": [
+                            "advertiser.com"
+                        ],
+                        "crid": "20",
+                        "w": 300,
+                        "h": 250
+                    }, {
+                        "id": "video-1-bid",
+                        "impid": "video-1",
+                        "price": 4.5,
+                        "adm": "<vast>Creative</vast>",
+                        "adomain": [
+                            "advertiser.com"
+                        ],
+                        "crid": "201"
+                    }],
+                    "seat": "pulsepoint-seat"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [{
+        "bids": [{
+            "bid": {
+                "id": "banner-1-bid",
+                "impid": "banner-1",
+                "price": 3.5,
+                "adm": "<div>Creative</div>",
+                "adomain": [
+                    "advertiser.com"
+                ],
+                "crid": "20",
+                "w": 300,
+                "h": 250
+            },
+            "type": "banner"
+        }, {
+            "bid": {
+                "id": "video-1-bid",
+                "impid": "video-1",
+                "price": 4.5,
+                "adm": "<vast>Creative</vast>",
+                "adomain": [
+                    "advertiser.com"
+                ],
+                "crid": "201"
+            },
+            "type": "video"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/native.json b/adapters/pulsepoint/pulsepointtest/exemplary/native.json
new file mode 100644
index 00000000..72c8532d
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/exemplary/native.json
@@ -0,0 +1,97 @@
+{
+    "mockBidRequest": {
+        "id": "some-request-id",
+        "site": {
+            "page": "http://publisher.com/index.html",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "native-1",
+            "native": {
+                "ver": "1.0",
+                "request": "{\"layout\":501,\"adunit\":501,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":26}},{\"id\":2,\"required\":0,\"data\":{\"type\":2,\"len\":90}}]}"
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 2001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "some-request-id",
+                "site": {
+                    "page": "http://publisher.com/index.html",
+                    "publisher": {
+                        "id": "1234",
+                        "name": "publisher.com"
+                    }
+                },
+                "imp": [{
+                    "id": "native-1",
+                    "tagid": "2001",
+                    "native": {
+                        "ver": "1.0",
+                        "request": "{\"layout\":501,\"adunit\":501,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":26}},{\"id\":2,\"required\":0,\"data\":{\"type\":2,\"len\":90}}]}"
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 2001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "id": "response-id",
+                "seatbid": [{
+                    "bid": [{
+                        "id": "native-1-bid",
+                        "impid": "native-1",
+                        "price": 3.5,
+                        "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Adv:\"}},{\"data\":{\"type\":2,\"value\":\"Teeth Whitening\"},\"id\":2}],\"imptrackers\":[\"https://tracker.pulsepoint.com//\"],\"link\":{\"url\":\"http://click.pulsepoint.com/\"},\"ver\":\"1.0\"}}",
+                        "adomain": [
+                            "advertiser.com"
+                        ],
+                        "crid": "20"
+                    }],
+                    "seat": "pulsepoint-seat"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [{
+        "bids": [{
+            "bid": {
+                "id": "native-1-bid",
+                "impid": "native-1",
+                "price": 3.5,
+                "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Adv:\"}},{\"data\":{\"type\":2,\"value\":\"Teeth Whitening\"},\"id\":2}],\"imptrackers\":[\"https://tracker.pulsepoint.com//\"],\"link\":{\"url\":\"http://click.pulsepoint.com/\"},\"ver\":\"1.0\"}}",
+                "adomain": [
+                    "advertiser.com"
+                ],
+                "crid": "20"
+            },
+            "type": "native"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/video.json b/adapters/pulsepoint/pulsepointtest/exemplary/video.json
new file mode 100644
index 00000000..980f49f2
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/exemplary/video.json
@@ -0,0 +1,105 @@
+{
+    "mockBidRequest": {
+        "id": "some-request-id",
+        "site": {
+            "page": "http://publisher.com/index.html",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "video-1",
+            "video": {
+                "w": 400,
+                "h": 300,
+                "mimes": ["video/x-flv"],
+                "minduration": 20
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 2001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "some-request-id",
+                "site": {
+                    "page": "http://publisher.com/index.html",
+                    "publisher": {
+                        "id": "1234",
+                        "name": "publisher.com"
+                    }
+                },
+                "imp": [{
+                    "id": "video-1",
+                    "tagid": "2001",
+                    "video": {
+                        "w": 400,
+                        "h": 300,
+                        "mimes": ["video/x-flv"],
+                        "minduration": 20
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 2001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "id": "response-id",
+                "seatbid": [{
+                    "bid": [{
+                        "id": "video-1-bid",
+                        "impid": "video-1",
+                        "price": 3.5,
+                        "adm": "<vast>Creative</vast>",
+                        "adomain": [
+                            "advertiser.com"
+                        ],
+                        "crid": "20",
+                        "w": 300,
+                        "h": 250
+                    }],
+                    "seat": "pulsepoint-seat"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [{
+        "bids": [{
+            "bid": {
+                "id": "video-1-bid",
+                "impid": "video-1",
+                "price": 3.5,
+                "adm": "<vast>Creative</vast>",
+                "adomain": [
+                    "advertiser.com"
+                ],
+                "crid": "20",
+                "w": 300,
+                "h": 250
+            },
+            "type": "video"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/params/race/banner.json b/adapters/pulsepoint/pulsepointtest/params/race/banner.json
index 1f2e34df..bc3e9371 100644
--- a/adapters/pulsepoint/pulsepointtest/params/race/banner.json
+++ b/adapters/pulsepoint/pulsepointtest/params/race/banner.json
@@ -1,5 +1,4 @@
 {
-  "cf": "300X250",
   "cp": 512379,
   "ct": 486653
 }
diff --git a/adapters/pulsepoint/pulsepointtest/params/race/native.json b/adapters/pulsepoint/pulsepointtest/params/race/native.json
new file mode 100644
index 00000000..57867bc2
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/params/race/native.json
@@ -0,0 +1,4 @@
+{
+  "cp": 512379,
+  "ct": 486655
+}
diff --git a/adapters/pulsepoint/pulsepointtest/params/race/video.json b/adapters/pulsepoint/pulsepointtest/params/race/video.json
new file mode 100644
index 00000000..d8ef37eb
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/params/race/video.json
@@ -0,0 +1,4 @@
+{
+  "cp": 512379,
+  "ct": 486654
+}
diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/bad-bid-data.json b/adapters/pulsepoint/pulsepointtest/supplemental/bad-bid-data.json
new file mode 100644
index 00000000..b5209ed4
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/supplemental/bad-bid-data.json
@@ -0,0 +1,90 @@
+{
+    "mockBidRequest": {
+        "id": "request-id",
+        "site": {
+            "page": "http://publisher.com/index.html",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 1001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "request-id",
+                "site": {
+                    "page": "http://publisher.com/index.html",
+                    "publisher": {
+                        "id": "1234",
+                        "name": "publisher.com"
+                    }
+                },
+                "imp": [{
+                    "id": "banner-1",
+                    "tagid": "1001",
+                    "banner": {
+                        "w": 320,
+                        "h": 50
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 1001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "id": "response-id",
+                "seatbid": [{
+                    "bid": [{
+                        "id": "banner-1-bid",
+                        "impid": "banner-2",
+                        "price": 3.5,
+                        "adm": "<div>Creative</div>",
+                        "adomain": [
+                            "advertiser.com"
+                        ],
+                        "crid": "20",
+                        "w": "300",
+                        "h": "250"
+                    }],
+                    "seat": "pulsepoint-seat"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [{
+          "value": "json: cannot unmarshal string into Go struct field Bid.seatbid.bid.w of type uint64",
+          "comparison": "literal"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/bad-input.json b/adapters/pulsepoint/pulsepointtest/supplemental/bad-input.json
new file mode 100644
index 00000000..3fe1422b
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/supplemental/bad-input.json
@@ -0,0 +1,70 @@
+{
+    "mockBidRequest": {
+        "id": "request-id",
+        "site": {
+            "page": "http://publisher.com/index.html",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 1001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "request-id",
+                "site": {
+                    "page": "http://publisher.com/index.html",
+                    "publisher": {
+                        "id": "1234",
+                        "name": "publisher.com"
+                    }
+                },
+                "imp": [{
+                    "id": "banner-1",
+                    "tagid": "1001",
+                    "banner": {
+                        "w": 320,
+                        "h": 50
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 1001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 400
+        }
+    }],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [{
+        "value": "Bad user input: HTTP status 400",
+        "comparison": "literal"
+    }]
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid-params.json b/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid-params.json
new file mode 100644
index 00000000..2d0c74d3
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid-params.json
@@ -0,0 +1,32 @@
+{
+    "mockBidRequest": {
+        "id": "request-id",
+        "site": {
+            "page": "http://publisher.com/index.html",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": {
+                "bidder": {
+                    "cp": "1234",
+                    "ct": "1001"
+                }
+            }
+        }]
+    },
+    "httpCalls": [],
+    "expectedMakeRequestsErrors": [{
+        "value": "json: cannot unmarshal string into Go struct field ExtImpPulsePoint.cp of type int",
+        "comparison": "literal"
+    }],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": []
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid.ext.json b/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid.ext.json
new file mode 100644
index 00000000..7de9bd3c
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid.ext.json
@@ -0,0 +1,27 @@
+{
+    "mockBidRequest": {
+        "id": "request-id",
+        "site": {
+            "page": "http://publisher.com/index.html",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": ""
+        }]
+    },
+    "httpCalls": [],
+    "expectedMakeRequestsErrors": [{
+        "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder",
+        "comparison": "literal"
+    }],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": []
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/error.json b/adapters/pulsepoint/pulsepointtest/supplemental/error.json
new file mode 100644
index 00000000..c469e3bc
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/supplemental/error.json
@@ -0,0 +1,70 @@
+{
+    "mockBidRequest": {
+        "id": "request-id",
+        "site": {
+            "page": "http://publisher.com/index.html",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 1001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "request-id",
+                "site": {
+                    "page": "http://publisher.com/index.html",
+                    "publisher": {
+                        "id": "1234",
+                        "name": "publisher.com"
+                    }
+                },
+                "imp": [{
+                    "id": "banner-1",
+                    "tagid": "1001",
+                    "banner": {
+                        "w": 320,
+                        "h": 50
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 1001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 503
+        }
+    }],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [{
+        "value": "Bad server response: HTTP status 503",
+        "comparison": "literal"
+    }]
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/impid-mismatch.json b/adapters/pulsepoint/pulsepointtest/supplemental/impid-mismatch.json
new file mode 100644
index 00000000..30b57e1e
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/supplemental/impid-mismatch.json
@@ -0,0 +1,87 @@
+{
+    "mockBidRequest": {
+        "id": "request-id",
+        "site": {
+            "page": "http://publisher.com/index.html",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 1001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "request-id",
+                "site": {
+                    "page": "http://publisher.com/index.html",
+                    "publisher": {
+                        "id": "1234",
+                        "name": "publisher.com"
+                    }
+                },
+                "imp": [{
+                    "id": "banner-1",
+                    "tagid": "1001",
+                    "banner": {
+                        "w": 320,
+                        "h": 50
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 1001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "id": "response-id",
+                "seatbid": [{
+                    "bid": [{
+                        "id": "banner-1-bid",
+                        "impid": "banner-2",
+                        "price": 3.5,
+                        "adm": "<div>Creative</div>",
+                        "adomain": [
+                            "advertiser.com"
+                        ],
+                        "crid": "20",
+                        "w": 300,
+                        "h": 250
+                    }],
+                    "seat": "pulsepoint-seat"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [{
+        "bids": []
+    }]
+}
\ No newline at end of file
diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/passback.json b/adapters/pulsepoint/pulsepointtest/supplemental/passback.json
new file mode 100644
index 00000000..434f5a7a
--- /dev/null
+++ b/adapters/pulsepoint/pulsepointtest/supplemental/passback.json
@@ -0,0 +1,68 @@
+{
+    "mockBidRequest": {
+        "id": "request-id",
+        "site": {
+            "page": "http://publisher.com/index.html",
+            "publisher": {
+                "id": "123456789",
+                "name": "publisher.com"
+            }
+        },
+        "imp": [{
+            "id": "banner-1",
+            "banner": {
+                "w": 320,
+                "h": 50
+            },
+            "ext": {
+                "bidder": {
+                    "cp": 1234,
+                    "ct": 1001
+                }
+            }
+        }]
+    },
+    "httpCalls": [{
+        "expectedRequest": {
+            "headers": {
+                "Content-Type": [
+                    "application/json;charset=utf-8"
+                ],
+                "Accept": [
+                    "application/json"
+                ]
+            },
+            "uri": "http://bidder.pulsepoint.com",
+            "body": {
+                "id": "request-id",
+                "site": {
+                    "page": "http://publisher.com/index.html",
+                    "publisher": {
+                        "id": "1234",
+                        "name": "publisher.com"
+                    }
+                },
+                "imp": [{
+                    "id": "banner-1",
+                    "tagid": "1001",
+                    "banner": {
+                        "w": 320,
+                        "h": 50
+                    },
+                    "ext": {
+                        "bidder": {
+                            "cp": 1234,
+                            "ct": 1001
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse": {
+            "status": 204,
+            "body": {}
+        }
+    }],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": []
+}
\ No newline at end of file
diff --git a/config/config_test.go b/config/config_test.go
index 57631395..7ff5f0fa 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -527,12 +527,6 @@ func TestNegativeRequestSize(t *testing.T) {
 	assertOneError(t, cfg.validate(), "cfg.max_request_size must be >= 0. Got -1")
 }
 
-func TestNegativeVendorID(t *testing.T) {
-	cfg := newDefaultConfig(t)
-	cfg.GDPR.HostVendorID = -1
-	assertOneError(t, cfg.validate(), "gdpr.host_vendor_id must be in the range [0, 65535]. Got -1")
-}
-
 func TestNegativePrometheusTimeout(t *testing.T) {
 	cfg := newDefaultConfig(t)
 	cfg.Metrics.Prometheus.Port = 8001
@@ -540,10 +534,44 @@ func TestNegativePrometheusTimeout(t *testing.T) {
 	assertOneError(t, cfg.validate(), "metrics.prometheus.timeout_ms must be positive if metrics.prometheus.port is defined. Got timeout=0 and port=8001")
 }
 
-func TestOverflowedVendorID(t *testing.T) {
+func TestInvalidHostVendorID(t *testing.T) {
+	tests := []struct {
+		description  string
+		vendorID     int
+		wantErrorMsg string
+	}{
+		{
+			description:  "Negative GDPR.HostVendorID",
+			vendorID:     -1,
+			wantErrorMsg: "gdpr.host_vendor_id must be in the range [0, 65535]. Got -1",
+		},
+		{
+			description:  "Overflowed GDPR.HostVendorID",
+			vendorID:     (0xffff) + 1,
+			wantErrorMsg: "gdpr.host_vendor_id must be in the range [0, 65535]. Got 65536",
+		},
+	}
+
+	for _, tt := range tests {
+		cfg := newDefaultConfig(t)
+		cfg.GDPR.HostVendorID = tt.vendorID
+		errs := cfg.validate()
+
+		assert.Equal(t, 1, len(errs), tt.description)
+		assert.EqualError(t, errs[0], tt.wantErrorMsg, tt.description)
+	}
+}
+
+func TestInvalidFetchGVL(t *testing.T) {
+	cfg := newDefaultConfig(t)
+	cfg.GDPR.TCF1.FetchGVL = true
+	assertOneError(t, cfg.validate(), "gdpr.tcf1.fetch_gvl has been discontinued and must be removed from your config. TCF1 will always use the fallback GVL going forward")
+}
+
+func TestInvalidAMPException(t *testing.T) {
 	cfg := newDefaultConfig(t)
-	cfg.GDPR.HostVendorID = (0xffff) + 1
-	assertOneError(t, cfg.validate(), "gdpr.host_vendor_id must be in the range [0, 65535]. Got 65536")
+	cfg.GDPR.AMPException = true
+	assertOneError(t, cfg.validate(), "gdpr.amp_exception has been discontinued and must be removed from your config. If you need to disable GDPR for AMP, you may do so per-account (gdpr.integration_enabled.amp) or at the host level for the default account (account_defaults.gdpr.integration_enabled.amp)")
 }
 
 func TestNegativeCurrencyConverterFetchInterval(t *testing.T) {
diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go
index db26a84b..ed9a526d 100644
--- a/endpoints/auction_test.go
+++ b/endpoints/auction_test.go
@@ -22,6 +22,8 @@ import (
 	gdprPolicy "github.com/prebid/prebid-server/privacy/gdpr"
 	"github.com/prebid/prebid-server/usersync/usersyncers"
 	"github.com/spf13/viper"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestSortBidsAndAddKeywordsForMobile(t *testing.T) {
@@ -376,32 +378,64 @@ func TestCacheVideoOnly(t *testing.T) {
 }
 
 func TestShouldUsersync(t *testing.T) {
-	doTest := func(gdprApplies string, consent string, allowBidderSync bool, allowHostCookies bool, expectAllow bool) {
-		t.Helper()
+	tests := []struct {
+		description      string
+		signal           string
+		allowHostCookies bool
+		allowBidderSync  bool
+		wantAllow        bool
+	}{
+		{
+			description:      "Don't sync - GDPR on, host cookies disallows and bidder sync disallows",
+			signal:           "1",
+			allowHostCookies: false,
+			allowBidderSync:  false,
+			wantAllow:        false,
+		},
+		{
+			description:      "Don't sync - GDPR on, host cookies disallows and bidder sync allows",
+			signal:           "1",
+			allowHostCookies: false,
+			allowBidderSync:  true,
+			wantAllow:        false,
+		},
+		{
+			description:      "Don't sync - GDPR on, host cookies allows and bidder sync disallows",
+			signal:           "1",
+			allowHostCookies: true,
+			allowBidderSync:  false,
+			wantAllow:        false,
+		},
+		{
+			description:      "Sync - GDPR on, host cookies allows and bidder sync allows",
+			signal:           "1",
+			allowHostCookies: true,
+			allowBidderSync:  true,
+			wantAllow:        true,
+		},
+		{
+			description:      "Don't sync - invalid GDPR signal, host cookies disallows and bidder sync disallows",
+			signal:           "2",
+			allowHostCookies: false,
+			allowBidderSync:  false,
+			wantAllow:        false,
+		},
+	}
+
+	for _, tt := range tests {
 		deps := auction{
-			cfg:     nil,
-			syncers: nil,
 			gdprPerms: &auctionMockPermissions{
-				allowBidderSync:  allowBidderSync,
-				allowHostCookies: allowHostCookies,
+				allowBidderSync:  tt.allowBidderSync,
+				allowHostCookies: tt.allowHostCookies,
 			},
-			metricsEngine: nil,
 		}
 		gdprPrivacyPolicy := gdprPolicy.Policy{
-			Signal:  gdprApplies,
-			Consent: consent,
+			Signal: tt.signal,
 		}
 
-		allowSyncs := deps.shouldUsersync(context.Background(), openrtb_ext.BidderAdform, gdprPrivacyPolicy)
-		if allowSyncs != expectAllow {
-			t.Errorf("Expected syncs: %t, allowed syncs: %t", expectAllow, allowSyncs)
-		}
+		allow := deps.shouldUsersync(context.Background(), openrtb_ext.BidderAdform, gdprPrivacyPolicy)
+		assert.Equal(t, tt.wantAllow, allow, tt.description)
 	}
-	doTest("0", "", false, false, true)
-	doTest("1", "", true, true, false)
-	doTest("1", "a", true, false, false)
-	doTest("1", "a", false, true, false)
-	doTest("1", "a", true, true, true)
 }
 
 type auctionMockPermissions struct {
@@ -412,11 +446,11 @@ type auctionMockPermissions struct {
 	allowID          bool
 }
 
-func (m *auctionMockPermissions) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) {
+func (m *auctionMockPermissions) HostCookiesAllowed(ctx context.Context, gdprSignal gdpr.Signal, consent string) (bool, error) {
 	return m.allowHostCookies, nil
 }
 
-func (m *auctionMockPermissions) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) {
+func (m *auctionMockPermissions) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal gdpr.Signal, consent string) (bool, error) {
 	return m.allowBidderSync, nil
 }
 
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index c790fcd9..a6352387 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -245,11 +245,11 @@ type gdprPerms struct {
 	allowedBidders map[openrtb_ext.BidderName]usersync.Usersyncer
 }
 
-func (g *gdprPerms) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) {
+func (g *gdprPerms) HostCookiesAllowed(ctx context.Context, gdprSignal gdpr.Signal, consent string) (bool, error) {
 	return g.allowHost, nil
 }
 
-func (g *gdprPerms) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) {
+func (g *gdprPerms) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal gdpr.Signal, consent string) (bool, error) {
 	_, ok := g.allowedBidders[bidder]
 	return ok, nil
 }
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 60285958..fc98608e 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -101,12 +101,13 @@ func TestSetUIDEndpoint(t *testing.T) {
 			description:           "Add the uid for the requested bidder to the list of existing syncs",
 		},
 		{
-			uri:                  "/setuid?bidder=pubmatic&uid=123&gdpr=0",
-			validFamilyNames:     []string{"pubmatic"},
-			existingSyncs:        nil,
-			expectedSyncs:        map[string]string{"pubmatic": "123"},
-			expectedResponseCode: http.StatusOK,
-			description:          "Don't care about GDPR consent if GDPR is set to 0",
+			uri:                   "/setuid?bidder=pubmatic&uid=123&gdpr=0",
+			validFamilyNames:      []string{"pubmatic"},
+			existingSyncs:         nil,
+			gdprAllowsHostCookies: true,
+			expectedSyncs:         map[string]string{"pubmatic": "123"},
+			expectedResponseCode:  http.StatusOK,
+			description:           "Don't care about GDPR consent if GDPR is set to 0",
 		},
 		{
 			uri:                  "/setuid?bidder=pubmatic&uid=123",
@@ -426,7 +427,7 @@ type mockPermsSetUID struct {
 	allowPI   bool
 }
 
-func (g *mockPermsSetUID) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) {
+func (g *mockPermsSetUID) HostCookiesAllowed(ctx context.Context, gdprSignal gdpr.Signal, consent string) (bool, error) {
 	var err error
 	if g.errorHost {
 		err = errors.New("something went wrong")
@@ -434,7 +435,7 @@ func (g *mockPermsSetUID) HostCookiesAllowed(ctx context.Context, consent string
 	return g.allowHost, err
 }
 
-func (g *mockPermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) {
+func (g *mockPermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal gdpr.Signal, consent string) (bool, error) {
 	return false, nil
 }
 
diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go
index d7059648..92eb0129 100644
--- a/exchange/adapter_util_test.go
+++ b/exchange/adapter_util_test.go
@@ -10,7 +10,6 @@ import (
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/adapters/appnexus"
 	"github.com/prebid/prebid-server/adapters/lifestreet"
-	"github.com/prebid/prebid-server/adapters/pulsepoint"
 	"github.com/prebid/prebid-server/adapters/rubicon"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/currency"
@@ -41,7 +40,7 @@ func TestBuildAdaptersSuccess(t *testing.T) {
 
 	appnexusBidder, _ := appnexus.Builder(openrtb_ext.BidderAppnexus, config.Adapter{})
 	appnexusBidderWithInfo := adapters.EnforceBidderInfo(appnexusBidder, infoActive)
-	appnexusBidderAdapted := adaptBidder(appnexusBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderAppnexus)
+	appnexusBidderAdapted := adaptBidder(appnexusBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderAppnexus, nil)
 	appnexusBidderValidated := addValidatedBidderMiddleware(appnexusBidderAdapted)
 
 	idLegacyAdapted := &adaptedAdapter{lifestreet.NewLifestreetLegacyAdapter(adapters.DefaultHTTPAdapterConfig, "anyEndpoint")}
@@ -78,11 +77,11 @@ func TestBuildExchangeBidders(t *testing.T) {
 
 	appnexusBidder, _ := appnexus.Builder(openrtb_ext.BidderAppnexus, config.Adapter{})
 	appnexusBidderWithInfo := adapters.EnforceBidderInfo(appnexusBidder, infoActive)
-	appnexusBidderAdapted := adaptBidder(appnexusBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderAppnexus)
+	appnexusBidderAdapted := adaptBidder(appnexusBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderAppnexus, nil)
 
 	rubiconBidder, _ := rubicon.Builder(openrtb_ext.BidderRubicon, config.Adapter{})
 	rubiconBidderWithInfo := adapters.EnforceBidderInfo(rubiconBidder, infoActive)
-	rubiconBidderAdapted := adaptBidder(rubiconBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderRubicon)
+	rubiconBidderAdapted := adaptBidder(rubiconBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderRubicon, nil)
 
 	testCases := []struct {
 		description     string
@@ -214,9 +213,9 @@ func TestBuildBidders(t *testing.T) {
 		},
 		{
 			description:   "Success - Ignores Legacy",
-			adapterConfig: map[string]config.Adapter{"appnexus": {}, "lifestreet": {}, "pulsepoint": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive, "lifestreet": infoActive, "pulsepoint": infoActive},
-			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder, openrtb_ext.BidderLifestreet: inconsequentialBuilder, openrtb_ext.BidderPulsepoint: inconsequentialBuilder},
+			adapterConfig: map[string]config.Adapter{"appnexus": {}, "lifestreet": {}},
+			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive, "lifestreet": infoActive},
+			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder, openrtb_ext.BidderLifestreet: inconsequentialBuilder},
 			expectedBidders: map[openrtb_ext.BidderName]adapters.Bidder{
 				openrtb_ext.BidderAppnexus: adapters.EnforceBidderInfo(appnexusBidder, infoActive),
 			},
@@ -267,7 +266,6 @@ func TestBuildExchangeBiddersLegacy(t *testing.T) {
 	cfg := config.Adapter{Endpoint: "anyEndpoint"}
 
 	expectedLifestreet := &adaptedAdapter{lifestreet.NewLifestreetLegacyAdapter(adapters.DefaultHTTPAdapterConfig, "anyEndpoint")}
-	expectedPulsepoint := &adaptedAdapter{pulsepoint.NewPulsePointLegacyAdapter(adapters.DefaultHTTPAdapterConfig, "anyEndpoint")}
 
 	testCases := []struct {
 		description   string
@@ -276,29 +274,23 @@ func TestBuildExchangeBiddersLegacy(t *testing.T) {
 		expected      map[openrtb_ext.BidderName]adaptedBidder
 	}{
 		{
-			description:   "All Active",
-			adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg},
-			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoActive, "pulsepoint": infoActive},
-			expected:      map[openrtb_ext.BidderName]adaptedBidder{"lifestreet": expectedLifestreet, "pulsepoint": expectedPulsepoint},
+			description:   "Active",
+			adapterConfig: map[string]config.Adapter{"lifestreet": cfg},
+			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoActive},
+			expected:      map[openrtb_ext.BidderName]adaptedBidder{"lifestreet": expectedLifestreet},
 		},
 		{
-			description:   "All Disabled",
-			adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg},
-			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoDisabled, "pulsepoint": infoDisabled},
+			description:   "Disabled",
+			adapterConfig: map[string]config.Adapter{"lifestreet": cfg},
+			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoDisabled},
 			expected:      map[openrtb_ext.BidderName]adaptedBidder{},
 		},
 		{
-			description:   "All Unknown",
-			adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg},
-			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoUnknown, "pulsepoint": infoUnknown},
+			description:   "Unknown",
+			adapterConfig: map[string]config.Adapter{"lifestreet": cfg},
+			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoUnknown},
 			expected:      map[openrtb_ext.BidderName]adaptedBidder{},
 		},
-		{
-			description:   "Mixed",
-			adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg},
-			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoActive, "pulsepoint": infoUnknown},
-			expected:      map[openrtb_ext.BidderName]adaptedBidder{"lifestreet": expectedLifestreet},
-		},
 	}
 
 	for _, test := range testCases {
@@ -405,7 +397,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) {
 
 type fakeAdaptedBidder struct{}
 
-func (fakeAdaptedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) {
+func (fakeAdaptedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
 	return nil, nil
 }
 
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index 8266daaa..b809c7e0 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -35,6 +35,16 @@ import (
 // 1. The Bidder implementation is called with the arguments we expect.
 // 2. The returned values are correct for a non-test bid.
 func TestSingleBidder(t *testing.T) {
+	type aTest struct {
+		debugInfo    *adapters.DebugInfo
+		httpCallsLen int
+	}
+
+	testCases := []*aTest{
+		{&adapters.DebugInfo{Allow: false}, 0},
+		{&adapters.DebugInfo{Allow: true}, 1},
+	}
+
 	respStatus := 200
 	respBody := "{\"bid\":false}"
 	server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody))
@@ -46,24 +56,6 @@ func TestSingleBidder(t *testing.T) {
 	bidAdjustment := 2.0
 	firstInitialPrice := 3.0
 	secondInitialPrice := 4.0
-	mockBidderResponse := &adapters.BidderResponse{
-		Bids: []*adapters.TypedBid{
-			{
-				Bid: &openrtb.Bid{
-					Price: firstInitialPrice,
-				},
-				BidType:      openrtb_ext.BidTypeBanner,
-				DealPriority: 4,
-			},
-			{
-				Bid: &openrtb.Bid{
-					Price: secondInitialPrice,
-				},
-				BidType:      openrtb_ext.BidTypeVideo,
-				DealPriority: 5,
-			},
-		},
-	}
 
 	bidderImpl := &goodSingleBidder{
 		httpRequest: &adapters.RequestData{
@@ -72,53 +64,81 @@ func TestSingleBidder(t *testing.T) {
 			Body:    []byte("{\"key\":\"val\"}"),
 			Headers: http.Header{},
 		},
-		bidResponse: mockBidderResponse,
+		bidResponse: nil,
 	}
-	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
-	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
 
-	// Make sure the goodSingleBidder was called with the expected arguments.
-	if bidderImpl.httpResponse == nil {
-		t.Errorf("The Bidder should be called with the server's response.")
-	}
-	if bidderImpl.httpResponse.StatusCode != respStatus {
-		t.Errorf("Bad response status. Expected %d, got %d", respStatus, bidderImpl.httpResponse.StatusCode)
-	}
-	if string(bidderImpl.httpResponse.Body) != respBody {
-		t.Errorf("Bad response body. Expected %s, got %s", respBody, string(bidderImpl.httpResponse.Body))
-	}
+	ctx := context.Background()
+	ctx = context.WithValue(ctx, DebugContextKey, true)
 
-	// Make sure the returned values are what we expect
-	if len(errs) != 0 {
-		t.Errorf("bidder.Bid returned %d errors. Expected 0", len(errs))
-	}
-	if len(seatBid.bids) != len(mockBidderResponse.Bids) {
-		t.Fatalf("Expected %d bids. Got %d", len(mockBidderResponse.Bids), len(seatBid.bids))
-	}
-	for index, typedBid := range mockBidderResponse.Bids {
-		if typedBid.Bid != seatBid.bids[index].bid {
-			t.Errorf("Bid %d did not point to the same bid returned by the Bidder.", index)
+	for _, test := range testCases {
+
+		mockBidderResponse := &adapters.BidderResponse{
+			Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb.Bid{
+						Price: firstInitialPrice,
+					},
+					BidType:      openrtb_ext.BidTypeBanner,
+					DealPriority: 4,
+				},
+				{
+					Bid: &openrtb.Bid{
+						Price: secondInitialPrice,
+					},
+					BidType:      openrtb_ext.BidTypeVideo,
+					DealPriority: 5,
+				},
+			},
 		}
-		if typedBid.BidType != seatBid.bids[index].bidType {
-			t.Errorf("Bid %d did not have the right type. Expected %s, got %s", index, typedBid.BidType, seatBid.bids[index].bidType)
+		bidderImpl.bidResponse = mockBidderResponse
+
+		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, test.debugInfo)
+		currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
+
+		seatBid, errs := bidder.requestBid(ctx, &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
+
+		// Make sure the goodSingleBidder was called with the expected arguments.
+		if bidderImpl.httpResponse == nil {
+			t.Errorf("The Bidder should be called with the server's response.")
 		}
-		if typedBid.DealPriority != seatBid.bids[index].dealPriority {
-			t.Errorf("Bid %d did not have the right deal priority. Expected %s, got %s", index, typedBid.BidType, seatBid.bids[index].bidType)
+		if bidderImpl.httpResponse.StatusCode != respStatus {
+			t.Errorf("Bad response status. Expected %d, got %d", respStatus, bidderImpl.httpResponse.StatusCode)
+		}
+		if string(bidderImpl.httpResponse.Body) != respBody {
+			t.Errorf("Bad response body. Expected %s, got %s", respBody, string(bidderImpl.httpResponse.Body))
+		}
+
+		// Make sure the returned values are what we expect
+		if len(errs) != 0 {
+			t.Errorf("bidder.Bid returned %d errors. Expected 0", len(errs))
+		}
+		if len(seatBid.bids) != len(mockBidderResponse.Bids) {
+			t.Fatalf("Expected %d bids. Got %d", len(mockBidderResponse.Bids), len(seatBid.bids))
+		}
+		for index, typedBid := range mockBidderResponse.Bids {
+			if typedBid.Bid != seatBid.bids[index].bid {
+				t.Errorf("Bid %d did not point to the same bid returned by the Bidder.", index)
+			}
+			if typedBid.BidType != seatBid.bids[index].bidType {
+				t.Errorf("Bid %d did not have the right type. Expected %s, got %s", index, typedBid.BidType, seatBid.bids[index].bidType)
+			}
+			if typedBid.DealPriority != seatBid.bids[index].dealPriority {
+				t.Errorf("Bid %d did not have the right deal priority. Expected %s, got %s", index, typedBid.BidType, seatBid.bids[index].bidType)
+			}
+		}
+		if mockBidderResponse.Bids[0].Bid.Price != bidAdjustment*firstInitialPrice {
+			t.Errorf("Bid[0].Price was not adjusted properly. Expected %f, got %f", bidAdjustment*firstInitialPrice, mockBidderResponse.Bids[0].Bid.Price)
+		}
+		if mockBidderResponse.Bids[1].Bid.Price != bidAdjustment*secondInitialPrice {
+			t.Errorf("Bid[1].Price was not adjusted properly. Expected %f, got %f", bidAdjustment*secondInitialPrice, mockBidderResponse.Bids[1].Bid.Price)
+		}
+		if len(seatBid.httpCalls) != test.httpCallsLen {
+			t.Errorf("The bidder shouldn't log HttpCalls when request.test == 0. Found %d", len(seatBid.httpCalls))
 		}
-	}
-	if mockBidderResponse.Bids[0].Bid.Price != bidAdjustment*firstInitialPrice {
-		t.Errorf("Bid[0].Price was not adjusted properly. Expected %f, got %f", bidAdjustment*firstInitialPrice, mockBidderResponse.Bids[0].Bid.Price)
-	}
-	if mockBidderResponse.Bids[1].Bid.Price != bidAdjustment*secondInitialPrice {
-		t.Errorf("Bid[1].Price was not adjusted properly. Expected %f, got %f", bidAdjustment*secondInitialPrice, mockBidderResponse.Bids[1].Bid.Price)
-	}
-	if len(seatBid.httpCalls) != 0 {
-		t.Errorf("The bidder shouldn't log HttpCalls when request.test == 0. Found %d", len(seatBid.httpCalls))
-	}
 
-	if len(seatBid.ext) != 0 {
-		t.Errorf("The bidder shouldn't define any seatBid.ext. Got %s", string(seatBid.ext))
+		if len(seatBid.ext) != 0 {
+			t.Errorf("The bidder shouldn't define any seatBid.ext. Got %s", string(seatBid.ext))
+		}
 	}
 }
 
@@ -162,9 +182,9 @@ func TestMultiBidder(t *testing.T) {
 			}},
 		bidResponse: mockBidderResponse,
 	}
-	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
+	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil)
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
+	seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
 
 	if seatBid == nil {
 		t.Fatalf("SeatBid should exist, because bids exist.")
@@ -524,7 +544,7 @@ func TestMultiCurrencies(t *testing.T) {
 		)
 
 		// Execute:
-		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
+		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil)
 		currencyConverter := currency.NewRateConverter(
 			&http.Client{},
 			mockedHTTPServer.URL,
@@ -540,6 +560,7 @@ func TestMultiCurrencies(t *testing.T) {
 			1,
 			currencyConverter.Rates(),
 			&adapters.ExtraRequestInfo{},
+			true,
 		)
 
 		// Verify:
@@ -675,7 +696,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) {
 		}
 
 		// Execute:
-		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
+		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil)
 		currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 		seatBid, errs := bidder.requestBid(
 			context.Background(),
@@ -684,6 +705,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) {
 			1,
 			currencyConverter.Rates(),
 			&adapters.ExtraRequestInfo{},
+			true,
 		)
 
 		// Verify:
@@ -841,7 +863,7 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) {
 		}
 
 		// Execute:
-		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
+		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil)
 		currencyConverter := currency.NewRateConverter(
 			&http.Client{},
 			mockedHTTPServer.URL,
@@ -856,6 +878,7 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) {
 			1,
 			currencyConverter.Rates(),
 			&adapters.ExtraRequestInfo{},
+			true,
 		)
 
 		// Verify:
@@ -886,6 +909,9 @@ func TestBadRequestLogging(t *testing.T) {
 	if ext.Status != 0 {
 		t.Errorf("The Status code should be 0. Got %d", ext.Status)
 	}
+	if len(ext.RequestHeaders) > 0 {
+		t.Errorf("The request headers should be empty. Got %s", ext.RequestHeaders)
+	}
 }
 
 // TestBadResponseLogging makes sure that openrtb_ext works properly if we don't get a sensible HTTP response.
@@ -894,6 +920,9 @@ func TestBadResponseLogging(t *testing.T) {
 		request: &adapters.RequestData{
 			Uri:  "test.com",
 			Body: []byte("request body"),
+			Headers: http.Header{
+				"header-1": []string{"value-1"},
+			},
 		},
 		err: errors.New("Bad response"),
 	}
@@ -910,6 +939,7 @@ func TestBadResponseLogging(t *testing.T) {
 	if ext.Status != 0 {
 		t.Errorf("The Status code should be 0. Got %d", ext.Status)
 	}
+	assert.Equal(t, info.request.Headers, http.Header(ext.RequestHeaders), "The request headers should be \"header-1:value-1\"")
 }
 
 // TestSuccessfulResponseLogging makes sure that openrtb_ext works properly if the HTTP request is successful.
@@ -918,6 +948,9 @@ func TestSuccessfulResponseLogging(t *testing.T) {
 		request: &adapters.RequestData{
 			Uri:  "test.com",
 			Body: []byte("request body"),
+			Headers: http.Header{
+				"header-1": []string{"value-1", "value-2"},
+			},
 		},
 		response: &adapters.ResponseData{
 			StatusCode: 200,
@@ -937,6 +970,7 @@ func TestSuccessfulResponseLogging(t *testing.T) {
 	if ext.Status != info.response.StatusCode {
 		t.Errorf("The Status code should be 0. Got %d", ext.Status)
 	}
+	assert.Equal(t, info.request.Headers, http.Header(ext.RequestHeaders), "The request headers should be \"%s\". Got %s", info.request.Headers, ext.RequestHeaders)
 }
 
 func TestMobileNativeTypes(t *testing.T) {
@@ -1020,7 +1054,7 @@ func TestMobileNativeTypes(t *testing.T) {
 			},
 			bidResponse: tc.mockBidderResponse,
 		}
-		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
+		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil)
 		currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 
 		seatBids, _ := bidder.requestBid(
@@ -1030,6 +1064,7 @@ func TestMobileNativeTypes(t *testing.T) {
 			1.0,
 			currencyConverter.Rates(),
 			&adapters.ExtraRequestInfo{},
+			true,
 		)
 
 		var actualValue string
@@ -1041,9 +1076,9 @@ func TestMobileNativeTypes(t *testing.T) {
 }
 
 func TestErrorReporting(t *testing.T) {
-	bidder := adaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
+	bidder := adaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil)
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	bids, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
+	bids, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
 	if bids != nil {
 		t.Errorf("There should be no seatbid if no http requests are returned.")
 	}
@@ -1224,9 +1259,9 @@ func TestCallRecordAdapterConnections(t *testing.T) {
 	metrics.On("RecordAdapterConnections", expectedAdapterName, false, mock.MatchedBy(compareConnWaitTime)).Once()
 
 	// Run requestBid using an http.Client with a mock handler
-	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, metrics, openrtb_ext.BidderAppnexus)
+	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, metrics, openrtb_ext.BidderAppnexus, nil)
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	_, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
+	_, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
 
 	// Assert no errors
 	assert.Equal(t, 0, len(errs), "bidder.requestBid returned errors %v \n", errs)
@@ -1401,6 +1436,23 @@ func TestTimeoutNotificationOn(t *testing.T) {
 	assert.EqualValues(t, logExpected, logActual)
 }
 
+func TestParseDebugInfoTrue(t *testing.T) {
+	debugInfo := &adapters.DebugInfo{Allow: true}
+	resDebugInfo := parseDebugInfo(debugInfo)
+	assert.True(t, resDebugInfo, "Debug Allow value should be true")
+}
+
+func TestParseDebugInfoFalse(t *testing.T) {
+	debugInfo := &adapters.DebugInfo{Allow: false}
+	resDebugInfo := parseDebugInfo(debugInfo)
+	assert.False(t, resDebugInfo, "Debug Allow value should be false")
+}
+
+func TestParseDebugInfoIsNil(t *testing.T) {
+	resDebugInfo := parseDebugInfo(nil)
+	assert.True(t, resDebugInfo, "Debug Allow value should be true")
+}
+
 func wrapWithBidderInfo(bidder adapters.Bidder) adapters.Bidder {
 	bidderInfo := adapters.BidderInfo{
 		Status: adapters.StatusActive,
diff --git a/exchange/bidder_validate_bids_test.go b/exchange/bidder_validate_bids_test.go
index bf35b2db..e7fc0b04 100644
--- a/exchange/bidder_validate_bids_test.go
+++ b/exchange/bidder_validate_bids_test.go
@@ -42,7 +42,7 @@ func TestAllValidBids(t *testing.T) {
 			},
 		},
 	})
-	seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{})
+	seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true)
 	assert.Len(t, seatBid.bids, 3)
 	assert.Len(t, errs, 0)
 }
@@ -83,7 +83,7 @@ func TestAllBadBids(t *testing.T) {
 			},
 		},
 	})
-	seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{})
+	seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true)
 	assert.Len(t, seatBid.bids, 0)
 	assert.Len(t, errs, 5)
 }
@@ -126,7 +126,7 @@ func TestMixedBids(t *testing.T) {
 			},
 		},
 	})
-	seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{})
+	seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true)
 	assert.Len(t, seatBid.bids, 2)
 	assert.Len(t, errs, 3)
 }
@@ -246,7 +246,7 @@ func TestCurrencyBids(t *testing.T) {
 			Cur: tc.brqCur,
 		}
 
-		seatBid, errs := bidder.requestBid(context.Background(), request, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{})
+		seatBid, errs := bidder.requestBid(context.Background(), request, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true)
 		assert.Len(t, seatBid.bids, expectedValidBids)
 		assert.Len(t, errs, expectedErrs)
 	}
@@ -257,6 +257,6 @@ type mockAdaptedBidder struct {
 	errorResponse []error
 }
 
-func (b *mockAdaptedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) {
+func (b *mockAdaptedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
 	return b.bidResponse, b.errorResponse
 }
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 416875a1..8bdf6c45 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -165,41 +165,66 @@ func TestDebugBehaviour(t *testing.T) {
 	type outTest struct {
 		debugInfoIncluded bool
 	}
+
+	type debugData struct {
+		bidderLevelDebugAllowed  bool
+		accountLevelDebugAllowed bool
+	}
+
 	type aTest struct {
-		desc string
-		in   inTest
-		out  outTest
+		desc      string
+		in        inTest
+		out       outTest
+		debugData debugData
 	}
 	testCases := []aTest{
 		{
-			desc: "test flag equals zero, ext debug flag false, no debug info expected",
-			in:   inTest{test: 0, debug: false},
-			out:  outTest{debugInfoIncluded: false},
+			desc:      "test flag equals zero, ext debug flag false, no debug info expected",
+			in:        inTest{test: 0, debug: false},
+			out:       outTest{debugInfoIncluded: false},
+			debugData: debugData{true, true},
 		},
 		{
-			desc: "test flag equals zero, ext debug flag true, debug info expected",
-			in:   inTest{test: 0, debug: true},
-			out:  outTest{debugInfoIncluded: true},
+			desc:      "test flag equals zero, ext debug flag true, debug info expected",
+			in:        inTest{test: 0, debug: true},
+			out:       outTest{debugInfoIncluded: true},
+			debugData: debugData{true, true},
 		},
 		{
-			desc: "test flag equals 1, ext debug flag false, debug info expected",
-			in:   inTest{test: 1, debug: false},
-			out:  outTest{debugInfoIncluded: true},
+			desc:      "test flag equals 1, ext debug flag false, debug info expected",
+			in:        inTest{test: 1, debug: false},
+			out:       outTest{debugInfoIncluded: true},
+			debugData: debugData{true, true},
 		},
 		{
-			desc: "test flag equals 1, ext debug flag true, debug info expected",
-			in:   inTest{test: 1, debug: true},
-			out:  outTest{debugInfoIncluded: true},
+			desc:      "test flag equals 1, ext debug flag true, debug info expected",
+			in:        inTest{test: 1, debug: true},
+			out:       outTest{debugInfoIncluded: true},
+			debugData: debugData{true, true},
 		},
 		{
-			desc: "test flag not equal to 0 nor 1, ext debug flag false, no debug info expected",
-			in:   inTest{test: 2, debug: false},
-			out:  outTest{debugInfoIncluded: false},
+			desc:      "test flag not equal to 0 nor 1, ext debug flag false, no debug info expected",
+			in:        inTest{test: 2, debug: false},
+			out:       outTest{debugInfoIncluded: false},
+			debugData: debugData{true, true},
 		},
 		{
-			desc: "test flag not equal to 0 nor 1, ext debug flag true, debug info expected",
-			in:   inTest{test: -1, debug: true},
-			out:  outTest{debugInfoIncluded: true},
+			desc:      "test flag not equal to 0 nor 1, ext debug flag true, debug info expected",
+			in:        inTest{test: -1, debug: true},
+			out:       outTest{debugInfoIncluded: true},
+			debugData: debugData{true, true},
+		},
+		{
+			desc:      "test account level debug disabled",
+			in:        inTest{test: -1, debug: true},
+			out:       outTest{debugInfoIncluded: false},
+			debugData: debugData{true, false},
+		},
+		{
+			desc:      "test bidder level debug disabled",
+			in:        inTest{test: -1, debug: true},
+			out:       outTest{debugInfoIncluded: false},
+			debugData: debugData{false, true},
 		},
 	}
 
@@ -239,17 +264,25 @@ func TestDebugBehaviour(t *testing.T) {
 	}
 
 	e := new(exchange)
-	e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{
-		openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus),
-	}
+
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.DummyMetricsEngine{}
 	e.gDPR = gdpr.AlwaysAllow{}
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.categoriesFetcher = categoriesFetcher
 
+	ctx := context.Background()
+
 	// Run tests
 	for _, test := range testCases {
+
+		e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{
+			openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &adapters.DebugInfo{Allow: test.debugData.bidderLevelDebugAllowed}),
+		}
+
+		//request level debug key
+		ctx = context.WithValue(ctx, DebugContextKey, test.in.debug)
+
 		bidRequest.Test = test.in.test
 
 		if test.in.debug {
@@ -260,13 +293,13 @@ func TestDebugBehaviour(t *testing.T) {
 
 		auctionRequest := AuctionRequest{
 			BidRequest: bidRequest,
-			Account:    config.Account{},
+			Account:    config.Account{DebugAllow: test.debugData.accountLevelDebugAllowed},
 			UserSyncs:  &emptyUsersync{},
 			StartTime:  time.Now(),
 		}
 
 		// Run test
-		outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, nil)
+		outBidResponse, err := e.HoldAuction(ctx, auctionRequest, nil)
 
 		// Assert no HoldAuction error
 		assert.NoErrorf(t, err, "%s. ex.HoldAuction returned an error: %v \n", test.desc, err)
@@ -292,8 +325,132 @@ func TestDebugBehaviour(t *testing.T) {
 			if test.in.debug {
 				diffJson(t, test.desc, bidRequest.Ext, actualExt.Debug.ResolvedRequest.Ext)
 			}
+		} else if !test.debugData.bidderLevelDebugAllowed && test.debugData.accountLevelDebugAllowed {
+			assert.Equal(t, len(actualExt.Debug.HttpCalls), 0, "%s. ext.debug.httpcalls array should not be empty", "With bidder level debug disable option http calls should be empty")
+
+		} else {
+			assert.Nil(t, actualExt.Debug, "%s. ext.debug.httpcalls array should not be empty", "With bidder level debug disable option http calls should be empty")
+		}
+	}
+}
+
+func TestTwoBiddersDebugDisabledAndEnabled(t *testing.T) {
+
+	type testCase struct {
+		bidder1DebugEnabled bool
+		bidder2DebugEnabled bool
+	}
+
+	testCases := []testCase{
+		{
+			bidder1DebugEnabled: true, bidder2DebugEnabled: true,
+		},
+		{
+			bidder1DebugEnabled: true, bidder2DebugEnabled: false,
+		},
+		{
+			bidder1DebugEnabled: false, bidder2DebugEnabled: true,
+		},
+		{
+			bidder1DebugEnabled: false, bidder2DebugEnabled: false,
+		},
+	}
+
+	// Set up test
+	noBidServer := func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(204)
+	}
+	server := httptest.NewServer(http.HandlerFunc(noBidServer))
+	defer server.Close()
+
+	categoriesFetcher, err := newCategoryFetcher("./test/category-mapping")
+	if err != nil {
+		t.Errorf("Failed to create a category Fetcher: %v", err)
+	}
+
+	bidderImpl := &goodSingleBidder{
+		httpRequest: &adapters.RequestData{
+			Method:  "POST",
+			Uri:     server.URL,
+			Body:    []byte("{\"key\":\"val\"}"),
+			Headers: http.Header{},
+		},
+		bidResponse: &adapters.BidderResponse{},
+	}
+
+	e := new(exchange)
+	e.cache = &wellBehavedCache{}
+	e.me = &metricsConf.DummyMetricsEngine{}
+	e.gDPR = gdpr.AlwaysAllow{}
+	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
+	e.categoriesFetcher = categoriesFetcher
+
+	debugLog := DebugLog{}
+
+	for _, testCase := range testCases {
+		bidRequest := &openrtb.BidRequest{
+			ID: "some-request-id",
+			Imp: []openrtb.Imp{{
+				ID:     "some-impression-id",
+				Banner: &openrtb.Banner{Format: []openrtb.Format{{W: 300, H: 250}, {W: 300, H: 600}}},
+				Ext:    json.RawMessage(`{"telaria": {"placementId": 1}, "appnexus": {"placementid": 2}}`),
+			}},
+			Site:   &openrtb.Site{Page: "prebid.org", Ext: json.RawMessage(`{"amp":0}`)},
+			Device: &openrtb.Device{UA: "curl/7.54.0", IP: "::1"},
+			AT:     1,
+			TMax:   500,
+		}
+
+		bidRequest.Ext = json.RawMessage(`{"prebid":{"debug":true}}`)
+
+		auctionRequest := AuctionRequest{
+			BidRequest: bidRequest,
+			Account:    config.Account{DebugAllow: true},
+			UserSyncs:  &emptyUsersync{},
+			StartTime:  time.Now(),
+		}
+
+		e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{
+			openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &adapters.DebugInfo{Allow: testCase.bidder1DebugEnabled}),
+			openrtb_ext.BidderTelaria:  adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &adapters.DebugInfo{Allow: testCase.bidder2DebugEnabled}),
+		}
+		// Run test
+		outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, &debugLog)
+		// Assert no HoldAuction err
+		assert.NoErrorf(t, err, "ex.HoldAuction returned an err")
+		assert.NotNilf(t, outBidResponse.Ext, "outBidResponse.Ext should not be nil")
+
+		actualExt := &openrtb_ext.ExtBidResponse{}
+		err = json.Unmarshal(outBidResponse.Ext, actualExt)
+		assert.NoErrorf(t, err, "JSON field unmarshaling err. ")
+
+		assert.NotEmpty(t, actualExt.Prebid, "ext.prebid should not be empty")
+		assert.NotEmpty(t, actualExt.Prebid.AuctionTimestamp, "ext.prebid.auctiontimestamp should not be empty when AuctionRequest.StartTime is set")
+		assert.Equal(t, auctionRequest.StartTime.UnixNano()/1e+6, actualExt.Prebid.AuctionTimestamp, "ext.prebid.auctiontimestamp has incorrect value")
+
+		assert.NotNilf(t, actualExt, "ext.debug field is expected to be included in this outBidResponse.Ext and not be nil")
+
+		// Assert "Debug fields
+		if testCase.bidder1DebugEnabled {
+			assert.Equal(t, server.URL, actualExt.Debug.HttpCalls["appnexus"][0].Uri, "Url for bidder with debug enabled is incorrect")
+			assert.NotNilf(t, actualExt.Debug.HttpCalls["appnexus"][0].RequestBody, "ext.debug.resolvedrequest field is expected to be included in this outBidResponse.Ext and not be nil")
+		}
+		if testCase.bidder2DebugEnabled {
+			assert.Equal(t, server.URL, actualExt.Debug.HttpCalls["telaria"][0].Uri, "Url for bidder with debug enabled is incorrect")
+			assert.NotNilf(t, actualExt.Debug.HttpCalls["telaria"][0].RequestBody, "ext.debug.resolvedrequest field is expected to be included in this outBidResponse.Ext and not be nil")
+		}
+		if !testCase.bidder1DebugEnabled {
+			assert.Nil(t, actualExt.Debug.HttpCalls["appnexus"], "ext.debug.resolvedrequest field is expected to be included in this outBidResponse.Ext and not be nil")
+		}
+		if !testCase.bidder2DebugEnabled {
+			assert.Nil(t, actualExt.Debug.HttpCalls["telaria"], "ext.debug.resolvedrequest field is expected to be included in this outBidResponse.Ext and not be nil")
+		}
+
+		if testCase.bidder1DebugEnabled && testCase.bidder2DebugEnabled {
+			assert.Equal(t, 2, len(actualExt.Debug.HttpCalls), "With bidder level debug enable option for both bidders http calls should have 2 elements")
 		}
 	}
+
 }
 
 func TestReturnCreativeEndToEnd(t *testing.T) {
@@ -434,7 +591,7 @@ func TestReturnCreativeEndToEnd(t *testing.T) {
 
 	e := new(exchange)
 	e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{
-		openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus),
+		openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil),
 	}
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.DummyMetricsEngine{}
@@ -465,7 +622,8 @@ func TestReturnCreativeEndToEnd(t *testing.T) {
 			}
 
 			// Run test
-			outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, nil)
+			debugLog := DebugLog{}
+			outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, &debugLog)
 
 			// Assert return error, if any
 			if testGroup.expectError {
@@ -716,7 +874,7 @@ func TestBidReturnsCreative(t *testing.T) {
 	}
 	e := new(exchange)
 	e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{
-		openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus),
+		openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil),
 	}
 	e.cache = &wellBehavedCache{}
 	e.me = &metricsConf.DummyMetricsEngine{}
@@ -1059,8 +1217,9 @@ func TestRaceIntegration(t *testing.T) {
 		UserSyncs:  &emptyUsersync{},
 	}
 
+	debugLog := DebugLog{}
 	ex := NewExchange(adapters, &wellBehavedCache{}, cfg, &metricsConf.DummyMetricsEngine{}, biddersInfo, gdpr.AlwaysAllow{}, currencyConverter, &nilCategoryFetcher{}).(*exchange)
-	_, err := ex.HoldAuction(context.Background(), auctionRequest, nil)
+	_, err := ex.HoldAuction(context.Background(), auctionRequest, &debugLog)
 	if err != nil {
 		t.Errorf("HoldAuction returned unexpected error: %v", err)
 	}
@@ -1279,8 +1438,8 @@ func TestPanicRecoveryHighLevel(t *testing.T) {
 		Account:    config.Account{},
 		UserSyncs:  &emptyUsersync{},
 	}
-
-	_, err := e.HoldAuction(context.Background(), auctionRequest, nil)
+	debugLog := DebugLog{}
+	_, err := e.HoldAuction(context.Background(), auctionRequest, &debugLog)
 	if err != nil {
 		t.Errorf("HoldAuction returned unexpected error: %v", err)
 	}
@@ -1406,14 +1565,17 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 		Account: config.Account{
 			ID:            "testaccount",
 			EventsEnabled: spec.EventsEnabled,
+			DebugAllow:    true,
 		},
 		UserSyncs: mockIdFetcher(spec.IncomingRequest.Usersyncs),
 	}
 	if spec.StartTime > 0 {
 		auctionRequest.StartTime = time.Unix(0, spec.StartTime*1e+6)
 	}
+	ctx := context.Background()
+	ctx = context.WithValue(ctx, DebugContextKey, true)
 
-	bid, err := ex.HoldAuction(context.Background(), auctionRequest, debugLog)
+	bid, err := ex.HoldAuction(ctx, auctionRequest, debugLog)
 	responseTimes := extractResponseTimes(t, filename, bid)
 	for _, bidderName := range biddersInAuction {
 		if _, ok := responseTimes[bidderName]; !ok {
@@ -2622,7 +2784,7 @@ type validatingBidder struct {
 	mockResponses map[string]bidderResponse
 }
 
-func (b *validatingBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (seatBid *pbsOrtbSeatBid, errs []error) {
+func (b *validatingBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (seatBid *pbsOrtbSeatBid, errs []error) {
 	if expectedRequest, ok := b.expectations[string(name)]; ok {
 		if expectedRequest != nil {
 			if expectedRequest.BidAdjustment != bidAdjustment {
@@ -2801,7 +2963,7 @@ func (e *mockUsersync) LiveSyncCount() int {
 
 type panicingAdapter struct{}
 
-func (panicingAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (posb *pbsOrtbSeatBid, errs []error) {
+func (panicingAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (posb *pbsOrtbSeatBid, errs []error) {
 	panic("Panic! Panic! The world is ending!")
 }
 
diff --git a/exchange/exchangetest/append-bidder-names.json b/exchange/exchangetest/append-bidder-names.json
index 1247b9f0..507aee1a 100644
--- a/exchange/exchangetest/append-bidder-names.json
+++ b/exchange/exchangetest/append-bidder-names.json
@@ -166,9 +166,6 @@
     },
     "ext": {
       "debug": {
-        "httpcalls": {
-          "appnexus": null
-        },
         "resolvedrequest": {
           "id": "some-request-id",
           "imp": [
diff --git a/exchange/exchangetest/debuglog_disabled.json b/exchange/exchangetest/debuglog_disabled.json
index 3dd04f66..e95b556b 100644
--- a/exchange/exchangetest/debuglog_disabled.json
+++ b/exchange/exchangetest/debuglog_disabled.json
@@ -175,9 +175,6 @@
     },
     "ext": {
       "debug": {
-        "httpcalls": {
-          "appnexus": null
-        },
         "resolvedrequest": {
           "id": "some-request-id",
           "imp": [
diff --git a/exchange/exchangetest/debuglog_enabled.json b/exchange/exchangetest/debuglog_enabled.json
index a70013b4..851bda69 100644
--- a/exchange/exchangetest/debuglog_enabled.json
+++ b/exchange/exchangetest/debuglog_enabled.json
@@ -175,9 +175,6 @@
     },
     "ext": {
       "debug": {
-        "httpcalls": {
-          "appnexus": null
-        },
         "resolvedrequest": {
           "id": "some-request-id",
           "imp": [
diff --git a/exchange/exchangetest/request-multi-bidders-debug-info.json b/exchange/exchangetest/request-multi-bidders-debug-info.json
index ec174f75..5061798b 100644
--- a/exchange/exchangetest/request-multi-bidders-debug-info.json
+++ b/exchange/exchangetest/request-multi-bidders-debug-info.json
@@ -65,6 +65,7 @@
           {
             "uri": "appnexusTest.com",
             "requestbody": "appnexusTestRequestBody",
+            "requestheaders": { "header_1" : ["value_11", "value_12"], "header_2" : ["value_21"] },
             "responsebody": "appnexusTestResponseBody",
             "status": 200
           }
@@ -94,6 +95,7 @@
           {
             "uri": "audienceNetworkTest.com",
             "requestbody": "audienceNetworkTestRequestBody",
+            "requestheaders": null,
             "responsebody": "audienceNetworkTestResponseBody",
             "status": 200
           }
@@ -149,6 +151,7 @@
             {
               "uri": "appnexusTest.com",
               "requestbody": "appnexusTestRequestBody",
+              "requestheaders": { "header_1" : ["value_11", "value_12"], "header_2" : ["value_21"] },
               "responsebody": "appnexusTestResponseBody",
               "status": 200
             }
@@ -157,6 +160,7 @@
             {
               "uri": "audienceNetworkTest.com",
               "requestbody": "audienceNetworkTestRequestBody",
+              "requestheaders": null,
               "responsebody": "audienceNetworkTestResponseBody",
               "status": 200
             }
diff --git a/exchange/legacy_test.go b/exchange/legacy_test.go
index fcc8b09f..2cef4fea 100644
--- a/exchange/legacy_test.go
+++ b/exchange/legacy_test.go
@@ -61,7 +61,7 @@ func TestSiteVideo(t *testing.T) {
 
 	exchangeBidder := adaptLegacyAdapter(&mockAdapter)
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	_, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
+	_, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
 	if len(errs) > 0 {
 		t.Errorf("Unexpected error requesting bids: %v", errs)
 	}
@@ -95,7 +95,7 @@ func TestAppBanner(t *testing.T) {
 
 	exchangeBidder := adaptLegacyAdapter(&mockAdapter)
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	_, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
+	_, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
 	if len(errs) > 0 {
 		t.Errorf("Unexpected error requesting bids: %v", errs)
 	}
@@ -141,7 +141,7 @@ func TestBidTransforms(t *testing.T) {
 
 	exchangeBidder := adaptLegacyAdapter(&mockAdapter)
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	seatBid, errs := exchangeBidder.requestBid(context.Background(), newAppOrtbRequest(), openrtb_ext.BidderRubicon, bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
+	seatBid, errs := exchangeBidder.requestBid(context.Background(), newAppOrtbRequest(), openrtb_ext.BidderRubicon, bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
 	if len(errs) != 1 {
 		t.Fatalf("Bad error count. Expected 1, got %d", len(errs))
 	}
@@ -290,7 +290,7 @@ func TestErrorResponse(t *testing.T) {
 
 	exchangeBidder := adaptLegacyAdapter(&mockAdapter)
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	_, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
+	_, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
 	if len(errs) != 1 {
 		t.Fatalf("Bad error count. Expected 1, got %d", len(errs))
 	}
@@ -329,7 +329,7 @@ func TestWithTargeting(t *testing.T) {
 	}
 	exchangeBidder := adaptLegacyAdapter(&mockAdapter)
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
-	bid, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderAudienceNetwork, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
+	bid, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderAudienceNetwork, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
 	if len(errs) != 0 {
 		t.Fatalf("This should not produce errors. Got %v", errs)
 	}
diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go
index 160d7465..2a77c4f7 100644
--- a/exchange/targeting_test.go
+++ b/exchange/targeting_test.go
@@ -115,7 +115,8 @@ func runTargetingAuction(t *testing.T, mockBids map[openrtb_ext.BidderName][]*op
 		UserSyncs:  &emptyUsersync{},
 	}
 
-	bidResp, err := ex.HoldAuction(context.Background(), auctionRequest, nil)
+	debugLog := DebugLog{}
+	bidResp, err := ex.HoldAuction(context.Background(), auctionRequest, &debugLog)
 
 	if err != nil {
 		t.Fatalf("Unexpected errors running auction: %v", err)
@@ -141,7 +142,7 @@ func buildAdapterMap(bids map[openrtb_ext.BidderName][]*openrtb.Bid, mockServerU
 		adapterMap[bidder] = adaptBidder(&mockTargetingBidder{
 			mockServerURL: mockServerURL,
 			bids:          bids,
-		}, client, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
+		}, client, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil)
 	}
 	return adapterMap
 }
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index e679397b..0407b3c5 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -24,11 +24,11 @@ type permissionsMock struct {
 	personalInfoAllowedError error
 }
 
-func (p *permissionsMock) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) {
+func (p *permissionsMock) HostCookiesAllowed(ctx context.Context, gdpr gdpr.Signal, consent string) (bool, error) {
 	return true, nil
 }
 
-func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) {
+func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdpr gdpr.Signal, consent string) (bool, error) {
 	return true, nil
 }
 
diff --git a/gdpr/gdpr_test.go b/gdpr/gdpr_test.go
index 902bf14e..5048cf11 100644
--- a/gdpr/gdpr_test.go
+++ b/gdpr/gdpr_test.go
@@ -48,3 +48,55 @@ func TestNewPermissions(t *testing.T) {
 		assert.IsType(t, tt.wantType, perms, tt.description)
 	}
 }
+
+func TestSignalParse(t *testing.T) {
+	tests := []struct {
+		description string
+		rawSignal   string
+		wantSignal  Signal
+		wantError   bool
+	}{
+		{
+			description: "valid raw signal is 0",
+			rawSignal:   "0",
+			wantSignal:  SignalNo,
+			wantError:   false,
+		},
+		{
+			description: "Valid signal - raw signal is 1",
+			rawSignal:   "1",
+			wantSignal:  SignalYes,
+			wantError:   false,
+		},
+		{
+			description: "Valid signal - raw signal is empty",
+			rawSignal:   "",
+			wantSignal:  SignalAmbiguous,
+			wantError:   false,
+		},
+		{
+			description: "Invalid signal - raw signal is -1",
+			rawSignal:   "-1",
+			wantSignal:  SignalAmbiguous,
+			wantError:   true,
+		},
+		{
+			description: "Invalid signal - raw signal is abc",
+			rawSignal:   "abc",
+			wantSignal:  SignalAmbiguous,
+			wantError:   true,
+		},
+	}
+
+	for _, tt := range tests {
+		signal, err := SignalParse(tt.rawSignal)
+
+		assert.Equal(t, tt.wantSignal, signal, tt.description)
+
+		if tt.wantError {
+			assert.NotNil(t, err, tt.description)
+		} else {
+			assert.Nil(t, err, tt.description)
+		}
+	}
+}
diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go
index a7d4b26a..45d2ba43 100644
--- a/gdpr/impl_test.go
+++ b/gdpr/impl_test.go
@@ -15,7 +15,7 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestNoConsentButAllowByDefault(t *testing.T) {
+func TestDisallowOnEmptyConsent(t *testing.T) {
 	perms := permissionsImpl{
 		cfg: config.GDPR{
 			HostVendorID:        3,
@@ -27,34 +27,27 @@ func TestNoConsentButAllowByDefault(t *testing.T) {
 			tcf2SpecVersion: failedListFetcher,
 		},
 	}
-	allowSync, err := perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, "")
-	assertBoolsEqual(t, true, allowSync)
-	assertNilErr(t, err)
-	allowSync, err = perms.HostCookiesAllowed(context.Background(), "")
-	assertBoolsEqual(t, true, allowSync)
-	assertNilErr(t, err)
-}
-
-func TestNoConsentAndRejectByDefault(t *testing.T) {
-	perms := permissionsImpl{
-		cfg: config.GDPR{
-			HostVendorID:        3,
-			UsersyncIfAmbiguous: false,
-		},
-		vendorIDs: nil,
-		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
-			tcf1SpecVersion: failedListFetcher,
-			tcf2SpecVersion: failedListFetcher,
-		},
-	}
-	allowSync, err := perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, "")
+	allowSync, err := perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, SignalYes, "")
 	assertBoolsEqual(t, false, allowSync)
 	assertNilErr(t, err)
-	allowSync, err = perms.HostCookiesAllowed(context.Background(), "")
+	allowSync, err = perms.HostCookiesAllowed(context.Background(), SignalYes, "")
 	assertBoolsEqual(t, false, allowSync)
 	assertNilErr(t, err)
 }
 
+func TestAllowOnSignalNo(t *testing.T) {
+	perms := permissionsImpl{}
+	emptyConsent := ""
+
+	allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalNo, emptyConsent)
+	assert.Equal(t, true, allowSync)
+	assert.Nil(t, err)
+
+	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, SignalNo, emptyConsent)
+	assert.Equal(t, true, allowSync)
+	assert.Nil(t, err)
+}
+
 func TestAllowedSyncs(t *testing.T) {
 	vendorListData := tcf1MarshalVendorList(tcf1VendorList{
 		VendorListVersion: 1,
@@ -81,11 +74,11 @@ func TestAllowedSyncs(t *testing.T) {
 		},
 	}
 
-	allowSync, err := perms.HostCookiesAllowed(context.Background(), "BON3PCUON3PCUABABBAAABoAAAAAMw")
+	allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "BON3PCUON3PCUABABBAAABoAAAAAMw")
 	assertNilErr(t, err)
 	assertBoolsEqual(t, true, allowSync)
 
-	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, "BON3PCUON3PCUABABBAAABoAAAAAMw")
+	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, SignalYes, "BON3PCUON3PCUABABBAAABoAAAAAMw")
 	assertNilErr(t, err)
 	assertBoolsEqual(t, true, allowSync)
 }
@@ -116,11 +109,11 @@ func TestProhibitedPurposes(t *testing.T) {
 		},
 	}
 
-	allowSync, err := perms.HostCookiesAllowed(context.Background(), "BON3PCUON3PCUABABBAAABAAAAAAMw")
+	allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "BON3PCUON3PCUABABBAAABAAAAAAMw")
 	assertNilErr(t, err)
 	assertBoolsEqual(t, false, allowSync)
 
-	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, "BON3PCUON3PCUABABBAAABAAAAAAMw")
+	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, SignalYes, "BON3PCUON3PCUABABBAAABAAAAAAMw")
 	assertNilErr(t, err)
 	assertBoolsEqual(t, false, allowSync)
 }
@@ -151,11 +144,11 @@ func TestProhibitedVendors(t *testing.T) {
 		},
 	}
 
-	allowSync, err := perms.HostCookiesAllowed(context.Background(), "BOS2bx5OS2bx5ABABBAAABoAAAAAFA")
+	allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "BOS2bx5OS2bx5ABABBAAABoAAAAAFA")
 	assertNilErr(t, err)
 	assertBoolsEqual(t, false, allowSync)
 
-	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, "BOS2bx5OS2bx5ABABBAAABoAAAAAFA")
+	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, SignalYes, "BOS2bx5OS2bx5ABABBAAABoAAAAAFA")
 	assertNilErr(t, err)
 	assertBoolsEqual(t, false, allowSync)
 }
@@ -171,7 +164,7 @@ func TestMalformedConsent(t *testing.T) {
 		},
 	}
 
-	sync, err := perms.HostCookiesAllowed(context.Background(), "BON")
+	sync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "BON")
 	assertErr(t, err, true)
 	assertBoolsEqual(t, false, sync)
 }
@@ -618,11 +611,11 @@ func TestAllowSyncTCF2(t *testing.T) {
 	}
 
 	// COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 6, 8
-	allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
 	assert.NoErrorf(t, err, "Error processing HostCookiesAllowed")
 	assert.EqualValuesf(t, true, allowSync, "HostCookiesAllowed failure")
 
-	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
 	assert.NoErrorf(t, err, "Error processing BidderSyncAllowed")
 	assert.EqualValuesf(t, true, allowSync, "BidderSyncAllowed failure")
 }
@@ -648,11 +641,11 @@ func TestProhibitedPurposeSyncTCF2(t *testing.T) {
 	perms.cfg.HostVendorID = 8
 
 	// COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consents to purposes for vendors 2, 6, 8
-	allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
 	assert.NoErrorf(t, err, "Error processing HostCookiesAllowed")
 	assert.EqualValuesf(t, false, allowSync, "HostCookiesAllowed failure")
 
-	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
 	assert.NoErrorf(t, err, "Error processing BidderSyncAllowed")
 	assert.EqualValuesf(t, false, allowSync, "BidderSyncAllowed failure")
 }
@@ -677,12 +670,12 @@ func TestProhibitedVendorSyncTCF2(t *testing.T) {
 	perms.cfg.HostVendorID = 10
 
 	// COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consents to purposes for vendors 2, 6, 8
-	allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
 	assert.NoErrorf(t, err, "Error processing HostCookiesAllowed")
 	assert.EqualValuesf(t, false, allowSync, "HostCookiesAllowed failure")
 
 	// Permission disallowed due to consent string not including vendor 10.
-	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderOpenx, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderOpenx, SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
 	assert.NoErrorf(t, err, "Error processing BidderSyncAllowed")
 	assert.EqualValuesf(t, false, allowSync, "BidderSyncAllowed failure")
 }
@@ -750,3 +743,61 @@ func assertStringsEqual(t *testing.T, expected string, actual string) {
 		t.Errorf("Expected %s, got %s", expected, actual)
 	}
 }
+
+func TestNormalizeGDPR(t *testing.T) {
+	tests := []struct {
+		description         string
+		userSyncIfAmbiguous bool
+		giveSignal          Signal
+		wantSignal          Signal
+	}{
+		{
+			description:         "Don't normalize - Signal No and userSyncIfAmbiguous false",
+			userSyncIfAmbiguous: false,
+			giveSignal:          SignalNo,
+			wantSignal:          SignalNo,
+		},
+		{
+			description:         "Don't normalize - Signal No and userSyncIfAmbiguous true",
+			userSyncIfAmbiguous: true,
+			giveSignal:          SignalNo,
+			wantSignal:          SignalNo,
+		},
+		{
+			description:         "Don't normalize - Signal Yes and userSyncIfAmbiguous false",
+			userSyncIfAmbiguous: false,
+			giveSignal:          SignalYes,
+			wantSignal:          SignalYes,
+		},
+		{
+			description:         "Don't normalize - Signal Yes and userSyncIfAmbiguous true",
+			userSyncIfAmbiguous: true,
+			giveSignal:          SignalYes,
+			wantSignal:          SignalYes,
+		},
+		{
+			description:         "Normalize - Signal Ambiguous and userSyncIfAmbiguous false",
+			userSyncIfAmbiguous: false,
+			giveSignal:          SignalAmbiguous,
+			wantSignal:          SignalYes,
+		},
+		{
+			description:         "Normalize - Signal Ambiguous and userSyncIfAmbiguous true",
+			userSyncIfAmbiguous: true,
+			giveSignal:          SignalAmbiguous,
+			wantSignal:          SignalNo,
+		},
+	}
+
+	for _, tt := range tests {
+		perms := permissionsImpl{
+			cfg: config.GDPR{
+				UsersyncIfAmbiguous: tt.userSyncIfAmbiguous,
+			},
+		}
+
+		normalizedSignal := perms.normalizeGDPR(tt.giveSignal)
+
+		assert.Equal(t, tt.wantSignal, normalizedSignal, tt.description)
+	}
+}
diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go
index 62c6a5f9..77f3f294 100644
--- a/gdpr/vendorlist-fetching_test.go
+++ b/gdpr/vendorlist-fetching_test.go
@@ -28,63 +28,24 @@ func TestTCF1FetcherInitialLoad(t *testing.T) {
 
 	testCases := []test{
 		{
-			description: "Fetch - No Fallback - Vendor List 1",
+			description: "Fallback - Vendor List 1",
 			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: false,
-				vendorListVersion:  1,
-			},
-			expected: vendorList1Expected,
-		},
-		{
-			description: "Fetch - No Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: false,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
-		},
-		{
-			description: "Fetch - Fallback - Vendor List 1",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: true,
-				vendorListVersion:  1,
-			},
-			expected: vendorList1Expected,
-		},
-		{
-			description: "Fetch - Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: true,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
-		},
-		{
-			description: "No Fetch - Fallback - Vendor List 1",
-			setup: testSetup{
-				enableTCF1Fetch:    false,
 				enableTCF1Fallback: true,
 				vendorListVersion:  1,
 			},
 			expected: vendorListFallbackExpected,
 		},
 		{
-			description: "No Fetch - Fallback - Vendor List 2",
+			description: "Fallback - Vendor List 2",
 			setup: testSetup{
-				enableTCF1Fetch:    false,
 				enableTCF1Fallback: true,
 				vendorListVersion:  2,
 			},
 			expected: vendorListFallbackExpected,
 		},
 		{
-			description: "No Fetch - No Fallback - Vendor List 1",
+			description: "No Fallback - Vendor List 1",
 			setup: testSetup{
-				enableTCF1Fetch:    false,
 				enableTCF1Fallback: false,
 				vendorListVersion:  1,
 			},
@@ -93,9 +54,8 @@ func TestTCF1FetcherInitialLoad(t *testing.T) {
 			},
 		},
 		{
-			description: "No Fetch - No Fallback - Vendor List 2",
+			description: "No Fallback - Vendor List 2",
 			setup: testSetup{
-				enableTCF1Fetch:    false,
 				enableTCF1Fallback: false,
 				vendorListVersion:  2,
 			},
@@ -125,72 +85,32 @@ func TestTCF2FetcherInitialLoad(t *testing.T) {
 
 	testCases := []test{
 		{
-			description: "Fetch - No Fallback - Vendor List 1",
+			description: "Fallback - Vendor List 1",
 			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: false,
-				vendorListVersion:  1,
-			},
-			expected: vendorList1Expected,
-		},
-		{
-			description: "Fetch - No Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: false,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
-		},
-		{
-			description: "Fetch - Fallback - Vendor List 1",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: true,
-				vendorListVersion:  1,
-			},
-			expected: vendorList1Expected,
-		},
-		{
-			description: "Fetch - Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: true,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
-		},
-		{
-			description: "No Fetch - Fallback - Vendor List 1",
-			setup: testSetup{
-				enableTCF1Fetch:    false,
 				enableTCF1Fallback: true,
 				vendorListVersion:  1,
 			},
 			expected: vendorList1Expected,
 		},
 		{
-			description: "No Fetch - Fallback - Vendor List 2",
+			description: "Fallback - Vendor List 2",
 			setup: testSetup{
-				enableTCF1Fetch:    false,
 				enableTCF1Fallback: true,
 				vendorListVersion:  2,
 			},
 			expected: vendorList2Expected,
 		},
 		{
-			description: "No Fetch - No Fallback - Vendor List 1",
+			description: "No Fallback - Vendor List 1",
 			setup: testSetup{
-				enableTCF1Fetch:    false,
 				enableTCF1Fallback: false,
 				vendorListVersion:  1,
 			},
 			expected: vendorList1Expected,
 		},
 		{
-			description: "No Fetch - No Fallback - Vendor List 2",
+			description: "No Fallback - Vendor List 2",
 			setup: testSetup{
-				enableTCF1Fetch:    false,
 				enableTCF1Fallback: false,
 				vendorListVersion:  2,
 			},
@@ -203,65 +123,6 @@ func TestTCF2FetcherInitialLoad(t *testing.T) {
 	}
 }
 
-func TestTCF1FetcherDynamicLoadListExists(t *testing.T) {
-	// Loads the first vendor list during initialization by setting the latest vendor list version to 1.
-	// All other vendor lists will be dynamically loaded.
-
-	server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{
-		vendorListLatestVersion: 1,
-		vendorLists: map[int]string{
-			1: tcf1VendorList1,
-			2: tcf1VendorList2,
-		},
-	})))
-	defer server.Close()
-
-	testCases := []test{
-		{
-			description: "Fetch - No Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: false,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
-		},
-		{
-			description: "Fetch - Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: true,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
-		},
-		{
-			description: "No Fetch - Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fetch:    false,
-				enableTCF1Fallback: true,
-				vendorListVersion:  2,
-			},
-			expected: vendorListFallbackExpected,
-		},
-		{
-			description: "No Fetch - No Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fetch:    false,
-				enableTCF1Fallback: false,
-				vendorListVersion:  2,
-			},
-			expected: testExpected{
-				errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes",
-			},
-		},
-	}
-
-	for _, test := range testCases {
-		runTest(t, test, tcf1SpecVersion, server)
-	}
-}
-
 func TestTCF2FetcherDynamicLoadListExists(t *testing.T) {
 	// Loads the first vendor list during initialization by setting the latest vendor list version to 1.
 	// All other vendor lists will be dynamically loaded.
@@ -278,36 +139,16 @@ func TestTCF2FetcherDynamicLoadListExists(t *testing.T) {
 
 	testCases := []test{
 		{
-			description: "Fetch - No Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: false,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
-		},
-		{
-			description: "Fetch - Fallback - Vendor List 2",
+			description: "Fallback - Vendor List 2",
 			setup: testSetup{
-				enableTCF1Fetch:    true,
 				enableTCF1Fallback: true,
 				vendorListVersion:  2,
 			},
 			expected: vendorList2Expected,
 		},
 		{
-			description: "No Fetch - Fallback - Vendor List 2",
+			description: "No Fallback - Vendor List 2",
 			setup: testSetup{
-				enableTCF1Fetch:    false,
-				enableTCF1Fallback: true,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
-		},
-		{
-			description: "No Fetch - No Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fetch:    false,
 				enableTCF1Fallback: false,
 				vendorListVersion:  2,
 			},
@@ -320,66 +161,6 @@ func TestTCF2FetcherDynamicLoadListExists(t *testing.T) {
 	}
 }
 
-func TestTCF1FetcherDynamicLoadListDoesntExist(t *testing.T) {
-	// Loads the first vendor list during initialization by setting the latest vendor list version to 1.
-	// All other vendor list load attempts will be done dynamically.
-
-	server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{
-		vendorListLatestVersion: 1,
-		vendorLists: map[int]string{
-			1: tcf1VendorList1,
-		},
-	})))
-	defer server.Close()
-
-	testCases := []test{
-		{
-			description: "Fetch - No Fallback - Vendor Doesn't Exist",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: false,
-				vendorListVersion:  2,
-			},
-			expected: testExpected{
-				errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes",
-			},
-		},
-		{
-			description: "Fetch - Fallback - Vendor Doesn't Exist",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: true,
-				vendorListVersion:  2,
-			},
-			expected: vendorListFallbackExpected,
-		},
-		{
-			description: "No Fetch - Fallback - Vendor Doesn't Exist",
-			setup: testSetup{
-				enableTCF1Fetch:    false,
-				enableTCF1Fallback: true,
-				vendorListVersion:  2,
-			},
-			expected: vendorListFallbackExpected,
-		},
-		{
-			description: "No Fetch - No Fallback - Vendor Doesn't Exist",
-			setup: testSetup{
-				enableTCF1Fetch:    false,
-				enableTCF1Fallback: false,
-				vendorListVersion:  2,
-			},
-			expected: testExpected{
-				errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes",
-			},
-		},
-	}
-
-	for _, test := range testCases {
-		runTest(t, test, 1, server)
-	}
-}
-
 func TestTCF2FetcherDynamicLoadListDoesntExist(t *testing.T) {
 	// Loads the first vendor list during initialization by setting the latest vendor list version to 1.
 	// All other vendor list load attempts will be done dynamically.
@@ -395,31 +176,8 @@ func TestTCF2FetcherDynamicLoadListDoesntExist(t *testing.T) {
 
 	testCases := []test{
 		{
-			description: "Fetch - No Fallback - Vendor Doesn't Exist",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: false,
-				vendorListVersion:  2,
-			},
-			expected: testExpected{
-				errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes",
-			},
-		},
-		{
-			description: "Fetch - Fallback - Vendor Doesn't Exist",
-			setup: testSetup{
-				enableTCF1Fetch:    true,
-				enableTCF1Fallback: true,
-				vendorListVersion:  2,
-			},
-			expected: testExpected{
-				errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes",
-			},
-		},
-		{
-			description: "No Fetch - Fallback - Vendor Doesn't Exist",
+			description: "Fallback - Vendor Doesn't Exist",
 			setup: testSetup{
-				enableTCF1Fetch:    false,
 				enableTCF1Fallback: true,
 				vendorListVersion:  2,
 			},
@@ -428,9 +186,8 @@ func TestTCF2FetcherDynamicLoadListDoesntExist(t *testing.T) {
 			},
 		},
 		{
-			description: "No Fetch - No Fallback - Vendor Doesn't Exist",
+			description: "No Fallback - Vendor Doesn't Exist",
 			setup: testSetup{
-				enableTCF1Fetch:    false,
 				enableTCF1Fallback: false,
 				vendorListVersion:  2,
 			},
@@ -445,38 +202,6 @@ func TestTCF2FetcherDynamicLoadListDoesntExist(t *testing.T) {
 	}
 }
 
-func TestTCF1FetcherThrottling(t *testing.T) {
-	server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{
-		vendorListLatestVersion: 1,
-		vendorLists: map[int]string{
-			1: tcf1MarshalVendorList(tcf1VendorList{
-				VendorListVersion: 1,
-				Vendors:           []tcf1Vendor{{ID: 12, Purposes: []int{1}}},
-			}),
-			2: tcf1MarshalVendorList(tcf1VendorList{
-				VendorListVersion: 2,
-				Vendors:           []tcf1Vendor{{ID: 12, Purposes: []int{1, 2}}},
-			}),
-			3: tcf1MarshalVendorList(tcf1VendorList{
-				VendorListVersion: 3,
-				Vendors:           []tcf1Vendor{{ID: 12, Purposes: []int{1, 2, 3}}},
-			}),
-		},
-	})))
-	defer server.Close()
-
-	fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf1SpecVersion)
-
-	// Dynamically Load List 2 Successfully
-	_, errList1 := fetcher(context.Background(), 2)
-	assert.NoError(t, errList1)
-
-	// Fail To Load List 3 Due To Rate Limiting
-	// - The request is rate limited after dynamically list 2.
-	_, errList2 := fetcher(context.Background(), 3)
-	assert.EqualError(t, errList2, "gdpr vendor list version 3 does not exist, or has not been loaded yet. Try again in a few minutes")
-}
-
 func TestTCF2FetcherThrottling(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{
 		vendorListLatestVersion: 1,
@@ -509,22 +234,6 @@ func TestTCF2FetcherThrottling(t *testing.T) {
 	assert.EqualError(t, errList2, "gdpr vendor list version 3 does not exist, or has not been loaded yet. Try again in a few minutes")
 }
 
-func TestTCF1MalformedVendorlist(t *testing.T) {
-	server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{
-		vendorListLatestVersion: 1,
-		vendorLists: map[int]string{
-			1: "malformed",
-		},
-	})))
-	defer server.Close()
-
-	fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf1SpecVersion)
-	_, err := fetcher(context.Background(), 1)
-
-	// Fetching should fail since vendor list could not be unmarshalled.
-	assert.Error(t, err)
-}
-
 func TestTCF2MalformedVendorlist(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{
 		vendorListLatestVersion: 1,
@@ -541,18 +250,6 @@ func TestTCF2MalformedVendorlist(t *testing.T) {
 	assert.Error(t, err)
 }
 
-func TestTCF1ServerUrlInvalid(t *testing.T) {
-	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
-	server.Close()
-
-	invalidURLGenerator := func(uint16, uint8) string { return " http://invalid-url-has-leading-whitespace" }
-
-	fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), invalidURLGenerator, tcf1SpecVersion)
-	_, err := fetcher(context.Background(), 1)
-
-	assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes")
-}
-
 func TestTCF2ServerUrlInvalid(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
 	server.Close()
@@ -565,16 +262,6 @@ func TestTCF2ServerUrlInvalid(t *testing.T) {
 	assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes")
 }
 
-func TestTCF1ServerUnavailable(t *testing.T) {
-	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
-	server.Close()
-
-	fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf1SpecVersion)
-	_, err := fetcher(context.Background(), 1)
-
-	assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes")
-}
-
 func TestTCF2ServerUnavailable(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
 	server.Close()
@@ -740,7 +427,6 @@ type test struct {
 }
 
 type testSetup struct {
-	enableTCF1Fetch    bool
 	enableTCF1Fallback bool
 	vendorListVersion  uint16
 }
@@ -754,7 +440,6 @@ type testExpected struct {
 
 func runTest(t *testing.T, test test, tcfSpecVersion uint8, server *httptest.Server) {
 	config := testConfig()
-	config.TCF1.FetchGVL = test.setup.enableTCF1Fetch
 	if test.setup.enableTCF1Fallback {
 		config.TCF1.FallbackGVLPath = "../static/tcf1/fallback_gvl.json"
 	}
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index bca8623d..73f0e886 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -101,6 +101,7 @@ func TestNewSyncerMap(t *testing.T) {
 		openrtb_ext.BidderAdot:         true,
 		openrtb_ext.BidderAdprime:      true,
 		openrtb_ext.BidderApplogy:      true,
+		openrtb_ext.BidderDecenterAds:  true,
 		openrtb_ext.BidderInMobi:       true,
 		openrtb_ext.BidderKidoz:        true,
 		openrtb_ext.BidderKubient:      true,