Repository: prebid/prebid-server
Pull request #1663 (2021-01-20 18:43:57)

Updating pulsepoint adapter
Removed the old logic, and updated the adapter to use the latest interface given for bidder adapters.


Patch:
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/emx_digital/emx_digital.go b/adapters/emx_digital/emx_digital.go
index a219481c..e87f0681 100644
--- a/adapters/emx_digital/emx_digital.go
+++ b/adapters/emx_digital/emx_digital.go
@@ -152,8 +152,10 @@ func buildImpVideo(imp *openrtb.Imp) error {
 		}
 	}
 
-	if imp.Video.Protocols != nil {
-		imp.Video.Protocols = cleanProtocol(imp.Video.Protocols)
+	if len(imp.Video.Protocols) > 0 {
+		videoCopy := *imp.Video
+		videoCopy.Protocols = cleanProtocol(imp.Video.Protocols)
+		imp.Video = &videoCopy
 	}
 
 	return 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/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/config.go b/config/config.go
index d1186688..2a99e278 100755
--- a/config/config.go
+++ b/config/config.go
@@ -216,6 +216,9 @@ func (cfg *GDPR) validate(errs []error) []error {
 	if cfg.AMPException == true {
 		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")
+	}
 	return errs
 }
 
@@ -234,7 +237,7 @@ func (t *GDPRTimeouts) ActiveTimeout() time.Duration {
 
 // TCF1 defines the TCF1 specific configurations for GDPR
 type TCF1 struct {
-	FetchGVL        bool   `mapstructure:"fetch_gvl"`
+	FetchGVL        bool   `mapstructure:"fetch_gvl"` // Deprecated: In a future version TCF1 will always use the fallback GVL
 	FallbackGVLPath string `mapstructure:"fallback_gvl_path"`
 }
 
@@ -598,7 +601,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")
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 31342a4c..8da57d6e 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -65,6 +65,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"
@@ -166,6 +167,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..4d92fe9f 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"
 )
@@ -58,7 +57,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 +98,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..6e84c260 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -310,17 +310,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/gdpr.go b/exchange/gdpr.go
index f464cac2..e861f948 100644
--- a/exchange/gdpr.go
+++ b/exchange/gdpr.go
@@ -8,24 +8,22 @@ import (
 )
 
 // ExtractGDPR will pull the gdpr flag from an openrtb request
-func extractGDPR(bidRequest *openrtb.BidRequest) gdpr.Signal {
+func extractGDPR(bidRequest *openrtb.BidRequest) (gdpr.Signal, error) {
 	var re regsExt
 	var err error
-	if bidRequest.Regs != nil {
+	if bidRequest.Regs != nil && bidRequest.Regs.Ext != nil {
 		err = json.Unmarshal(bidRequest.Regs.Ext, &re)
 	}
 	if re.GDPR == nil || err != nil {
-		return gdpr.SignalAmbiguous
-	} else {
-		return gdpr.Signal(*re.GDPR)
+		return gdpr.SignalAmbiguous, err
 	}
+	return gdpr.Signal(*re.GDPR), nil
 }
 
 // ExtractConsent will pull the consent string from an openrtb request
-func extractConsent(bidRequest *openrtb.BidRequest) (consent string) {
+func extractConsent(bidRequest *openrtb.BidRequest) (consent string, err error) {
 	var ue userExt
-	var err error
-	if bidRequest.User != nil {
+	if bidRequest.User != nil && bidRequest.User.Ext != nil {
 		err = json.Unmarshal(bidRequest.User.Ext, &ue)
 	}
 	if err != nil {
diff --git a/exchange/utils.go b/exchange/utils.go
index af4dfb05..8d4c0fac 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -75,14 +75,19 @@ func cleanOpenRTBRequests(ctx context.Context,
 		return
 	}
 
-	gdprSignal := extractGDPR(req.BidRequest)
-	consent := extractConsent(req.BidRequest)
+	gdprSignal, err := extractGDPR(req.BidRequest)
+	if err != nil {
+		errs = append(errs, err)
+	}
+	consent, err := extractConsent(req.BidRequest)
+	if err != nil {
+		errs = append(errs, err)
+	}
 	gdprEnforced := gdprSignal == gdpr.SignalYes || (gdprSignal == gdpr.SignalAmbiguous && !usersyncIfAmbiguous)
 
 	ccpaEnforcer, err := extractCCPA(req.BidRequest, privacyConfig, &req.Account, aliases, integrationTypeMap[req.LegacyLabels.RType])
 	if err != nil {
 		errs = append(errs, err)
-		return
 	}
 
 	lmtEnforcer := extractLMT(req.BidRequest, privacyConfig)
diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go
index eea7fa5a..bfc238e1 100644
--- a/gdpr/gdpr.go
+++ b/gdpr/gdpr.go
@@ -34,18 +34,25 @@ const (
 
 // NewPermissions gets an instance of the Permissions for use elsewhere in the project.
 func NewPermissions(ctx context.Context, cfg config.GDPR, vendorIDs map[openrtb_ext.BidderName]uint16, client *http.Client) Permissions {
-	// If the host doesn't buy into the IAB GDPR consent framework, then save some cycles and let all syncs happen.
-	if cfg.HostVendorID == 0 {
-		return AlwaysAllow{}
+	if !cfg.Enabled {
+		return &AlwaysAllow{}
 	}
 
-	return &permissionsImpl{
+	permissionsImpl := &permissionsImpl{
 		cfg:       cfg,
 		vendorIDs: vendorIDs,
 		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
 			tcf1SpecVersion: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker, tcf1SpecVersion),
 			tcf2SpecVersion: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker, tcf2SpecVersion)},
 	}
+
+	if cfg.HostVendorID == 0 {
+		return &AllowHostCookies{
+			permissionsImpl: permissionsImpl,
+		}
+	}
+
+	return permissionsImpl
 }
 
 // An ErrorMalformedConsent will be returned by the Permissions interface if
diff --git a/gdpr/impl.go b/gdpr/impl.go
index e1822ece..c7998251 100644
--- a/gdpr/impl.go
+++ b/gdpr/impl.go
@@ -223,6 +223,16 @@ func (p *permissionsImpl) parseVendor(ctx context.Context, vendorID uint16, cons
 	return
 }
 
+// AllowHostCookies represents a GDPR permissions policy with host cookie syncing always allowed
+type AllowHostCookies struct {
+	*permissionsImpl
+}
+
+// HostCookiesAllowed always returns true
+func (p *AllowHostCookies) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) {
+	return true, nil
+}
+
 // Exporting to allow for easy test setups
 type AlwaysAllow struct{}
 
diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go
index 66a3f4ad..de6da7b4 100644
--- a/gdpr/vendorlist-fetching.go
+++ b/gdpr/vendorlist-fetching.go
@@ -95,14 +95,14 @@ func preloadCache(ctx context.Context, client *http.Client, urlMaker func(uint16
 func vendorListURLMaker(vendorListVersion uint16, tcfSpecVersion uint8) string {
 	if tcfSpecVersion == tcf2SpecVersion {
 		if vendorListVersion == 0 {
-			return "https://vendorlist.consensu.org/v2/vendor-list.json"
+			return "https://vendor-list.consensu.org/v2/vendor-list.json"
 		}
-		return "https://vendorlist.consensu.org/v2/archives/vendor-list-v" + strconv.Itoa(int(vendorListVersion)) + ".json"
+		return "https://vendor-list.consensu.org/v2/archives/vendor-list-v" + strconv.Itoa(int(vendorListVersion)) + ".json"
 	}
 	if vendorListVersion == 0 {
-		return "https://vendorlist.consensu.org/vendorlist.json"
+		return "https://vendor-list.consensu.org/vendorlist.json"
 	}
-	return "https://vendorlist.consensu.org/v-" + strconv.Itoa(int(vendorListVersion)) + "/vendorlist.json"
+	return "https://vendor-list.consensu.org/v-" + strconv.Itoa(int(vendorListVersion)) + "/vendorlist.json"
 }
 
 // newOccasionalSaver returns a wrapped version of saveOne() which only activates every few minutes.
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/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/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"]
 }
diff --git a/usersync/usersync.go b/usersync/usersync.go
index 2b313a02..236acbe7 100644
--- a/usersync/usersync.go
+++ b/usersync/usersync.go
@@ -17,7 +17,7 @@ type Usersyncer interface {
 
 	// GDPRVendorID returns the ID in the IAB Global Vendor List which refers to this Bidder.
 	//
-	// The Global Vendor list can be found here: https://vendorlist.consensu.org/vendorlist.json
+	// The Global Vendor list can be found here: https://vendor-list.consensu.org/vendorlist.json
 	// Bidders can register for the list here: https://register.consensu.org/
 	//
 	// If you're not on the list, this should return 0. If cookie sync requests have GDPR consent info,


Test patch:
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/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/endpoints/auction_test.go b/endpoints/auction_test.go
index 6a237e39..db26a84b 100644
--- a/endpoints/auction_test.go
+++ b/endpoints/auction_test.go
@@ -349,7 +349,7 @@ func TestCacheVideoOnly(t *testing.T) {
 		t.Fatal(err.Error())
 	}
 	syncers := usersyncers.NewSyncerMap(cfg)
-	gdprPerms := gdpr.NewPermissions(nil, config.GDPR{
+	gdprPerms := gdpr.NewPermissions(context.Background(), config.GDPR{
 		HostVendorID: 0,
 	}, nil, nil)
 	prebid_cache_client.InitPrebidCache(server.URL)
diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go
index d7059648..20402e1e 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"
@@ -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 {
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index 8266daaa..93aaf241 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -886,6 +886,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 +897,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 +916,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 +925,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 +947,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) {
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/gdpr_test.go b/exchange/gdpr_test.go
index f7906104..35193909 100644
--- a/exchange/gdpr_test.go
+++ b/exchange/gdpr_test.go
@@ -14,6 +14,7 @@ func TestExtractGDPR(t *testing.T) {
 		description string
 		giveRegs    *openrtb.Regs
 		wantGDPR    gdpr.Signal
+		wantError   bool
 	}{
 		{
 			description: "Regs Ext GDPR = 0",
@@ -44,6 +45,7 @@ func TestExtractGDPR(t *testing.T) {
 			description: "JSON unmarshal error",
 			giveRegs:    &openrtb.Regs{Ext: json.RawMessage(`{"`)},
 			wantGDPR:    gdpr.SignalAmbiguous,
+			wantError:   true,
 		},
 	}
 
@@ -52,8 +54,14 @@ func TestExtractGDPR(t *testing.T) {
 			Regs: tt.giveRegs,
 		}
 
-		result := extractGDPR(&bidReq)
+		result, err := extractGDPR(&bidReq)
 		assert.Equal(t, tt.wantGDPR, result, tt.description)
+
+		if tt.wantError {
+			assert.NotNil(t, err, tt.description)
+		} else {
+			assert.Nil(t, err, tt.description)
+		}
 	}
 }
 
@@ -62,6 +70,7 @@ func TestExtractConsent(t *testing.T) {
 		description string
 		giveUser    *openrtb.User
 		wantConsent string
+		wantError   bool
 	}{
 		{
 			description: "User Ext Consent is not empty",
@@ -87,6 +96,7 @@ func TestExtractConsent(t *testing.T) {
 			description: "JSON unmarshal error",
 			giveUser:    &openrtb.User{Ext: json.RawMessage(`{`)},
 			wantConsent: "",
+			wantError:   true,
 		},
 	}
 
@@ -95,7 +105,13 @@ func TestExtractConsent(t *testing.T) {
 			User: tt.giveUser,
 		}
 
-		result := extractConsent(&bidReq)
+		result, err := extractConsent(&bidReq)
 		assert.Equal(t, tt.wantConsent, result, tt.description)
+
+		if tt.wantError {
+			assert.NotNil(t, err, tt.description)
+		} else {
+			assert.Nil(t, err, tt.description)
+		}
 	}
 }
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 6dab5b00..e679397b 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -1060,6 +1060,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 		permissionsError    error
 		userSyncIfAmbiguous bool
 		expectPrivacyLabels metrics.PrivacyLabels
+		expectError         bool
 	}{
 		{
 			description:        "Enforce - TCF Invalid",
@@ -1109,6 +1110,19 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 				GDPRTCFVersion: "",
 			},
 		},
+		{
+			description:        "Enforce - TCF 1; GDPR signal extraction error",
+			gdprAccountEnabled: &trueValue,
+			gdprHostEnabled:    true,
+			gdpr:               "0{",
+			gdprConsent:        "BONV8oqONXwgmADACHENAO7pqzAAppY",
+			gdprScrub:          true,
+			expectPrivacyLabels: metrics.PrivacyLabels{
+				GDPREnforced:   true,
+				GDPRTCFVersion: metrics.TCFVersionV1,
+			},
+			expectError: true,
+		},
 		{
 			description:        "Enforce - TCF 1; account GDPR enabled, host GDPR setting disregarded",
 			gdprAccountEnabled: &trueValue,
@@ -1236,7 +1250,12 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 			privacyConfig)
 		result := results[0]
 
-		assert.Nil(t, errs)
+		if test.expectError {
+			assert.NotNil(t, errs)
+		} else {
+			assert.Nil(t, errs)
+		}
+
 		if test.gdprScrub {
 			assert.Equal(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID")
 			assert.Equal(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5")
diff --git a/gdpr/gdpr_test.go b/gdpr/gdpr_test.go
new file mode 100644
index 00000000..902bf14e
--- /dev/null
+++ b/gdpr/gdpr_test.go
@@ -0,0 +1,50 @@
+package gdpr
+
+import (
+	"context"
+	"net/http"
+	"testing"
+
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewPermissions(t *testing.T) {
+	tests := []struct {
+		description  string
+		gdprEnabled  bool
+		hostVendorID int
+		wantType     Permissions
+	}{
+		{
+			gdprEnabled:  false,
+			hostVendorID: 32,
+			wantType:     &AlwaysAllow{},
+		},
+		{
+			gdprEnabled:  true,
+			hostVendorID: 0,
+			wantType:     &AllowHostCookies{},
+		},
+		{
+			gdprEnabled:  true,
+			hostVendorID: 32,
+			wantType:     &permissionsImpl{},
+		},
+	}
+
+	for _, tt := range tests {
+
+		config := config.GDPR{
+			Enabled:      tt.gdprEnabled,
+			HostVendorID: tt.hostVendorID,
+		}
+		vendorIDs := map[openrtb_ext.BidderName]uint16{}
+
+		perms := NewPermissions(context.Background(), config, vendorIDs, &http.Client{})
+
+		assert.IsType(t, tt.wantType, perms, tt.description)
+	}
+}
diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go
index e5ad8793..62c6a5f9 100644
--- a/gdpr/vendorlist-fetching_test.go
+++ b/gdpr/vendorlist-fetching_test.go
@@ -596,25 +596,25 @@ func TestVendorListURLMaker(t *testing.T) {
 			description:       "TCF1 - Latest",
 			tcfSpecVersion:    1,
 			vendorListVersion: 0, // Forces latest version.
-			expectedURL:       "https://vendorlist.consensu.org/vendorlist.json",
+			expectedURL:       "https://vendor-list.consensu.org/vendorlist.json",
 		},
 		{
 			description:       "TCF1 - Specific",
 			tcfSpecVersion:    1,
 			vendorListVersion: 42,
-			expectedURL:       "https://vendorlist.consensu.org/v-42/vendorlist.json",
+			expectedURL:       "https://vendor-list.consensu.org/v-42/vendorlist.json",
 		},
 		{
 			description:       "TCF2 - Latest",
 			tcfSpecVersion:    2,
 			vendorListVersion: 0, // Forces latest version.
-			expectedURL:       "https://vendorlist.consensu.org/v2/vendor-list.json",
+			expectedURL:       "https://vendor-list.consensu.org/v2/vendor-list.json",
 		},
 		{
 			description:       "TCF2 - Specific",
 			tcfSpecVersion:    2,
 			vendorListVersion: 42,
-			expectedURL:       "https://vendorlist.consensu.org/v2/archives/vendor-list-v42.json",
+			expectedURL:       "https://vendor-list.consensu.org/v2/archives/vendor-list-v42.json",
 		},
 	}
 
@@ -710,7 +710,7 @@ type serverSettings struct {
 // If the "version" query param doesn't exist, it returns a 400.
 //
 // If the "version" query param points to a version which doesn't exist, it returns a 403.
-// Don't ask why... that's just what the official page is doing. See https://vendorlist.consensu.org/v-9999/vendorlist.json
+// Don't ask why... that's just what the official page is doing. See https://vendor-list.consensu.org/v-9999/vendorlist.json
 func mockServer(settings serverSettings) func(http.ResponseWriter, *http.Request) {
 	return func(w http.ResponseWriter, req *http.Request) {
 		vendorListVersion := req.URL.Query().Get("version")