Repository: prebid/prebid-server
Pull request #1994 (2021-09-13 11:28:31)

New Adapter: yahoossp (Rebrand yssp to yahoossp keeping the yssp alias)
We were told by legal that we'll go further with `yahoossp` name. The verizonmedia adapter will be removed at later stage.


Patch:
diff --git a/adapters/iqzone/iqzone.go b/adapters/iqzone/iqzone.go
index 190466b3..79dc04d8 100644
--- a/adapters/iqzone/iqzone.go
+++ b/adapters/iqzone/iqzone.go
@@ -23,19 +23,71 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters
 	return bidder, nil
 }
 
-func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
-	requestJSON, err := json.Marshal(request)
-	if err != nil {
-		return nil, []error{err}
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var errs []error
+	var err error
+	var adapterRequests []*adapters.RequestData
+
+	reqCopy := *request
+	for _, imp := range request.Imp {
+		reqCopy.Imp = []openrtb2.Imp{imp}
+
+		var bidderExt adapters.ExtImpBidder
+		var iqzoneExt openrtb_ext.ImpExtIQZone
+
+		if err = json.Unmarshal(reqCopy.Imp[0].Ext, &bidderExt); err != nil {
+			return nil, append(errs, err)
+		}
+		if err = json.Unmarshal(bidderExt.Bidder, &iqzoneExt); err != nil {
+			return nil, append(errs, err)
+		}
+
+		finalyImpExt := reqCopy.Imp[0].Ext
+		if iqzoneExt.PlacementID != "" {
+			finalyImpExt, _ = json.Marshal(map[string]interface{}{
+				"bidder": map[string]interface{}{
+					"placementId": iqzoneExt.PlacementID,
+					"type":        "publisher",
+				},
+			})
+		} else if iqzoneExt.EndpointID != "" {
+			finalyImpExt, _ = json.Marshal(map[string]interface{}{
+				"bidder": map[string]interface{}{
+					"endpointId": iqzoneExt.EndpointID,
+					"type":       "network",
+				},
+			})
+		}
+
+		reqCopy.Imp[0].Ext = finalyImpExt
+
+		adapterReq, errors := a.makeRequest(&reqCopy)
+		if adapterReq != nil {
+			adapterRequests = append(adapterRequests, adapterReq)
+		}
+		errs = append(errs, errors...)
 	}
+	return adapterRequests, errs
+}
+
+func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) {
+	var errs []error
 
-	requestData := &adapters.RequestData{
-		Method: "POST",
-		Uri:    a.endpoint,
-		Body:   requestJSON,
+	reqJSON, err := json.Marshal(request)
+	if err != nil {
+		errs = append(errs, err)
+		return nil, errs
 	}
 
-	return []*adapters.RequestData{requestData}, nil
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	return &adapters.RequestData{
+		Method:  "POST",
+		Uri:     a.endpoint,
+		Body:    reqJSON,
+		Headers: headers,
+	}, errs
 }
 
 func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
@@ -75,20 +127,16 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R
 }
 
 func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
-	var mediaType openrtb_ext.BidType = ""
 	for _, imp := range imps {
 		if imp.ID == impID {
 			if imp.Banner != nil {
-				mediaType = openrtb_ext.BidTypeBanner
-				return mediaType, nil
+				return openrtb_ext.BidTypeBanner, nil
 			}
 			if imp.Banner == nil && imp.Video != nil {
-				mediaType = openrtb_ext.BidTypeVideo
-				return mediaType, nil
+				return openrtb_ext.BidTypeVideo, nil
 			}
 			if imp.Banner == nil && imp.Video == nil && imp.Native != nil {
-				mediaType = openrtb_ext.BidTypeNative
-				return mediaType, nil
+				return openrtb_ext.BidTypeNative, nil
 			}
 		}
 	}
diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go
index 1878c41f..80c62df1 100644
--- a/adapters/rubicon/rubicon.go
+++ b/adapters/rubicon/rubicon.go
@@ -5,6 +5,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"github.com/buger/jsonparser"
 	"io/ioutil"
 	"net/http"
 	"net/url"
@@ -30,7 +31,6 @@ type RubiconAdapter struct {
 	XAPIPassword string
 }
 
-// used for cookies and such
 func (a *RubiconAdapter) Name() string {
 	return "rubicon"
 }
@@ -73,16 +73,17 @@ type rubiconImpExtRPTrack struct {
 	MintVersion string `json:"mint_version"`
 }
 
+type rubiconImpExt struct {
+	RP   rubiconImpExtRP `json:"rp,omitempty"`
+	GPID string          `json:"gpid,omitempty"`
+}
+
 type rubiconImpExtRP struct {
 	ZoneID int                  `json:"zone_id"`
 	Target json.RawMessage      `json:"target,omitempty"`
 	Track  rubiconImpExtRPTrack `json:"track"`
 }
 
-type rubiconImpExt struct {
-	RP rubiconImpExtRP `json:"rp"`
-}
-
 type rubiconUserExtRP struct {
 	Target json.RawMessage `json:"target,omitempty"`
 }
@@ -102,6 +103,7 @@ type rubiconUserExt struct {
 	TpID        []rubiconExtUserTpID     `json:"tpid,omitempty"`
 	RP          rubiconUserExtRP         `json:"rp"`
 	LiverampIdl string                   `json:"liveramp_idl,omitempty"`
+	Data        json.RawMessage          `json:"data,omitempty"`
 }
 
 type rubiconSiteExtRP struct {
@@ -131,18 +133,6 @@ type rubiconBannerExt struct {
 	RP rubiconBannerExtRP `json:"rp"`
 }
 
-type ExtImpContextData struct {
-	AdSlot string `json:"adslot,omitempty"`
-}
-
-type ExtImpContext struct {
-	Data ExtImpContextData `json:"data,omitempty"`
-}
-
-type ExtImpWithContext struct {
-	Context ExtImpContext `json:"context,omitempty"` // First Party Data context
-}
-
 // ***** Video Extension *****
 type rubiconVideoParams struct {
 	Language     string `json:"language,omitempty"`
@@ -701,11 +691,10 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada
 	requestImpCopy := request.Imp
 
 	rubiconRequest := *request
-	for i := 0; i < numRequests; i++ {
-		thisImp := requestImpCopy[i]
+	for _, imp := range requestImpCopy {
 
 		var bidderExt adapters.ExtImpBidder
-		if err = json.Unmarshal(thisImp.Ext, &bidderExt); err != nil {
+		if err = json.Unmarshal(imp.Ext, &bidderExt); err != nil {
 			errs = append(errs, &errortypes.BadInput{
 				Message: err.Error(),
 			})
@@ -720,36 +709,15 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada
 			continue
 		}
 
-		target := rubiconExt.Inventory
-		if rubiconExt.Inventory != nil {
-			rubiconExtInventory := make(map[string]interface{})
-			if err := json.Unmarshal(rubiconExt.Inventory, &rubiconExtInventory); err != nil {
-				errs = append(errs, &errortypes.BadInput{
-					Message: err.Error(),
-				})
-				continue
-			}
-
-			var extImpWithContext ExtImpWithContext
-			if err := json.Unmarshal(thisImp.Ext, &extImpWithContext); err != nil {
-				errs = append(errs, &errortypes.BadInput{
-					Message: err.Error(),
-				})
-				continue
-			}
-
-			// Copy imp[].ext.context.data.adslot is copied to imp[].ext.rp.target.dfp_ad_unit_code,
-			// but with any leading slash dropped
-			adSlot := extImpWithContext.Context.Data.AdSlot
-			if adSlot != "" {
-				rubiconExtInventory["dfp_ad_unit_code"] = strings.TrimLeft(adSlot, "/")
-
-				target, err = json.Marshal(&rubiconExtInventory)
-				if err != nil {
-					errs = append(errs, err)
-					continue
-				}
-			}
+		target, err := updateImpRpTargetWithFpdAttributes(rubiconExt, imp, request.Site, request.App)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+		adSlot, err := getAdSlot(imp)
+		if err != nil {
+			errs = append(errs, err)
+			continue
 		}
 
 		impExt := rubiconImpExt{
@@ -758,35 +726,36 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada
 				Target: target,
 				Track:  rubiconImpExtRPTrack{Mint: "", MintVersion: ""},
 			},
+			GPID: adSlot,
 		}
-		thisImp.Ext, err = json.Marshal(&impExt)
+		imp.Ext, err = json.Marshal(&impExt)
 		if err != nil {
 			errs = append(errs, err)
 			continue
 		}
 
-		resolvedBidFloor, err := resolveBidFloor(thisImp.BidFloor, thisImp.BidFloorCur, reqInfo)
+		resolvedBidFloor, err := resolveBidFloor(imp.BidFloor, imp.BidFloorCur, reqInfo)
 		if err != nil {
 			errs = append(errs, &errortypes.BadInput{
 				Message: fmt.Sprintf("Unable to convert provided bid floor currency from %s to USD",
-					thisImp.BidFloorCur),
+					imp.BidFloorCur),
 			})
 			continue
 		}
 
 		if resolvedBidFloor > 0 {
-			thisImp.BidFloorCur = "USD"
-			thisImp.BidFloor = resolvedBidFloor
+			imp.BidFloorCur = "USD"
+			imp.BidFloor = resolvedBidFloor
 		}
 
 		if request.User != nil {
 			userCopy := *request.User
-
-			target, err := updateExtWithIabAttribute(rubiconExt.Visitor, userCopy.Data, []int{4})
+			target, err := updateUserRpTargetWithFpdAttributes(rubiconExt.Visitor, userCopy)
 			if err != nil {
 				errs = append(errs, err)
 				continue
 			}
+
 			userExtRP := rubiconUserExt{RP: rubiconUserExtRP{Target: target}}
 
 			if request.User.Ext != nil {
@@ -822,6 +791,10 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada
 				errs = append(errs, err)
 				continue
 			}
+			userCopy.Geo = nil
+			userCopy.Yob = 0
+			userCopy.Gender = ""
+
 			rubiconRequest.User = &userCopy
 		}
 
@@ -832,13 +805,13 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada
 			rubiconRequest.Device = &deviceCopy
 		}
 
-		isVideo := isVideo(thisImp)
+		isVideo := isVideo(imp)
 		if isVideo {
-			videoCopy := *thisImp.Video
+			videoCopy := *imp.Video
 
 			videoSizeId := rubiconExt.Video.VideoSizeID
 			if videoSizeId == 0 {
-				resolvedSizeId, err := resolveVideoSizeId(thisImp.Video.Placement, thisImp.Instl, thisImp.ID)
+				resolvedSizeId, err := resolveVideoSizeId(imp.Video.Placement, imp.Instl, imp.ID)
 				if err != nil {
 					errs = append(errs, err)
 					continue
@@ -853,23 +826,23 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada
 			}
 			videoExt := rubiconVideoExt{Skip: rubiconExt.Video.Skip, SkipDelay: rubiconExt.Video.SkipDelay, VideoType: videoType, RP: rubiconVideoExtRP{SizeID: videoSizeId}}
 			videoCopy.Ext, err = json.Marshal(&videoExt)
-			thisImp.Video = &videoCopy
-			thisImp.Banner = nil
+			imp.Video = &videoCopy
+			imp.Banner = nil
 		} else {
-			primarySizeID, altSizeIDs, err := parseRubiconSizes(thisImp.Banner.Format)
+			primarySizeID, altSizeIDs, err := parseRubiconSizes(imp.Banner.Format)
 			if err != nil {
 				errs = append(errs, err)
 				continue
 			}
 			bannerExt := rubiconBannerExt{RP: rubiconBannerExtRP{SizeID: primarySizeID, AltSizeIDs: altSizeIDs, MIME: "text/html"}}
-			bannerCopy := *thisImp.Banner
+			bannerCopy := *imp.Banner
 			bannerCopy.Ext, err = json.Marshal(&bannerExt)
 			if err != nil {
 				errs = append(errs, err)
 				continue
 			}
-			thisImp.Banner = &bannerCopy
-			thisImp.Video = nil
+			imp.Banner = &bannerCopy
+			imp.Video = nil
 		}
 
 		pubExt := rubiconPubExt{RP: rubiconPubExtRP{AccountID: rubiconExt.AccountId}}
@@ -878,12 +851,16 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada
 			siteCopy := *request.Site
 			siteExtRP := rubiconSiteExt{RP: rubiconSiteExtRP{SiteID: rubiconExt.SiteId}}
 			if siteCopy.Content != nil {
-				target, err := updateExtWithIabAttribute(nil, siteCopy.Content.Data, []int{1, 2})
-				if err != nil {
-					errs = append(errs, err)
-					continue
+				siteTarget := make(map[string]interface{})
+				updateExtWithIabAttribute(siteTarget, siteCopy.Content.Data, []int{1, 2, 5, 6})
+				if len(siteTarget) > 0 {
+					updatedSiteTarget, err := json.Marshal(siteTarget)
+					if err != nil {
+						errs = append(errs, err)
+						continue
+					}
+					siteExtRP.RP.Target = updatedSiteTarget
 				}
-				siteExtRP.RP.Target = target
 			}
 
 			siteCopy.Ext, err = json.Marshal(&siteExtRP)
@@ -910,7 +887,7 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada
 			}
 		}
 
-		rubiconRequest.Imp = []openrtb2.Imp{thisImp}
+		rubiconRequest.Imp = []openrtb2.Imp{imp}
 		rubiconRequest.Cur = nil
 		rubiconRequest.Ext = nil
 
@@ -941,27 +918,272 @@ func resolveBidFloor(bidFloor float64, bidFloorCur string, reqInfo *adapters.Ext
 	return bidFloor, nil
 }
 
-func updateExtWithIabAttribute(target json.RawMessage, data []openrtb2.Data, segTaxes []int) (json.RawMessage, error) {
+func updateImpRpTargetWithFpdAttributes(extImp openrtb_ext.ExtImpRubicon, imp openrtb2.Imp,
+	site *openrtb2.Site, app *openrtb2.App) (json.RawMessage, error) {
+	existingTarget, _, _, err := jsonparser.Get(imp.Ext, "rp", "target")
+	if isNotKeyPathError(err) {
+		return nil, err
+	}
+	target, err := rawJSONToMap(existingTarget)
+	if err != nil {
+		return nil, err
+	}
+	err = populateFirstPartyDataAttributes(extImp.Inventory, target)
+	if err != nil {
+		return nil, err
+	}
+
+	if site != nil {
+		siteExtData, _, _, err := jsonparser.Get(site.Ext, "data")
+		if isNotKeyPathError(err) {
+			return nil, err
+		}
+		err = populateFirstPartyDataAttributes(siteExtData, target)
+		if err != nil {
+			return nil, err
+		}
+		if len(site.SectionCat) > 0 {
+			addStringArrayAttribute(site.SectionCat, target, "sectioncat")
+		}
+		if len(site.PageCat) > 0 {
+			addStringArrayAttribute(site.PageCat, target, "pagecat")
+		}
+		if site.Page != "" {
+			addStringAttribute(site.Page, target, "page")
+		}
+		if site.Ref != "" {
+			addStringAttribute(site.Ref, target, "ref")
+		}
+		if site.Search != "" {
+			addStringAttribute(site.Search, target, "search")
+		}
+	} else {
+		appExtData, _, _, err := jsonparser.Get(app.Ext, "data")
+		if isNotKeyPathError(err) {
+			return nil, err
+		}
+		err = populateFirstPartyDataAttributes(appExtData, target)
+		if err != nil {
+			return nil, err
+		}
+		if len(app.SectionCat) > 0 {
+			addStringArrayAttribute(app.SectionCat, target, "sectioncat")
+		}
+		if len(app.PageCat) > 0 {
+			addStringArrayAttribute(app.PageCat, target, "pagecat")
+		}
+	}
+
+	impExtContextAttributes, _, _, err := jsonparser.Get(imp.Ext, "context", "data")
+	if isNotKeyPathError(err) {
+		return nil, err
+	}
+
+	if len(impExtContextAttributes) > 0 {
+		err = populateFirstPartyDataAttributes(impExtContextAttributes, target)
+		if err != nil {
+			return nil, err
+		}
+	} else if impExtDataAttributes, _, _, err := jsonparser.Get(imp.Ext, "data"); err == nil && len(impExtDataAttributes) > 0 {
+		err = populateFirstPartyDataAttributes(impExtDataAttributes, target)
+		if err != nil {
+			return nil, err
+		}
+	}
+	if isNotKeyPathError(err) {
+		return nil, err
+	}
+
+	if len(extImp.Keywords) > 0 {
+		addStringArrayAttribute(extImp.Keywords, target, "keywords")
+	}
+	updatedTarget, err := json.Marshal(target)
+	if err != nil {
+		return nil, err
+	}
+	return updatedTarget, nil
+}
+
+func isNotKeyPathError(err error) bool {
+	return err != nil && err != jsonparser.KeyPathNotFoundError
+}
+
+func addStringAttribute(attribute string, target map[string]interface{}, attributeName string) {
+	target[attributeName] = [1]string{attribute}
+}
+
+func addStringArrayAttribute(attribute []string, target map[string]interface{}, attributeName string) {
+	target[attributeName] = attribute
+}
+
+func getAdSlot(imp openrtb2.Imp) (string, error) {
+	var adSlot string
+	var parsingError error
+	jsonparser.EachKey(imp.Ext, func(idx int, value []byte, vt jsonparser.ValueType, err error) {
+		switch idx {
+		case 0:
+			adServerContextName, err := jsonparser.GetString(value, "name")
+			if isNotKeyPathError(err) {
+				parsingError = err
+				return
+			}
+			if adServerContextName == "gam" {
+				contextAdSlot, err := jsonparser.GetString(value, "adslot")
+				if isNotKeyPathError(err) {
+					parsingError = err
+					return
+				}
+				adSlot = contextAdSlot
+			}
+		}
+	}, []string{"context", "data", "adserver"})
+
+	if parsingError != nil {
+		return "", parsingError
+	}
+
+	if adSlot != "" {
+		return adSlot, nil
+	}
+
+	jsonparser.EachKey(imp.Ext, func(idx int, value []byte, vt jsonparser.ValueType, err error) {
+		switch idx {
+		case 0:
+			adServerDataName, err := jsonparser.GetString(value, "name")
+			if isNotKeyPathError(err) {
+				parsingError = err
+				return
+			}
+			if adServerDataName == "gam" {
+				dataAdSlot, err := jsonparser.GetString(value, "adslot")
+				if isNotKeyPathError(err) {
+					parsingError = err
+					return
+				}
+				adSlot = dataAdSlot
+			}
+		}
+	}, []string{"data", "adserver"})
+
+	if parsingError != nil {
+		return "", parsingError
+	}
+
+	return adSlot, nil
+}
+
+func updateUserRpTargetWithFpdAttributes(visitor json.RawMessage, user openrtb2.User) (json.RawMessage, error) {
+	existingTarget, _, _, err := jsonparser.Get(user.Ext, "rp", "target")
+	if isNotKeyPathError(err) {
+		return nil, err
+	}
+	target, err := rawJSONToMap(existingTarget)
+	if err != nil {
+		return nil, err
+	}
+	err = populateFirstPartyDataAttributes(visitor, target)
+	if err != nil {
+		return nil, err
+	}
+	userExtData, _, _, err := jsonparser.Get(user.Ext, "data")
+	if isNotKeyPathError(err) {
+		return nil, err
+	}
+	err = populateFirstPartyDataAttributes(userExtData, target)
+	if err != nil {
+		return nil, err
+	}
+	updateExtWithIabAttribute(target, user.Data, []int{4})
+
+	updatedTarget, err := json.Marshal(target)
+	if err != nil {
+		return nil, err
+	}
+	return updatedTarget, nil
+}
+
+func updateExtWithIabAttribute(target map[string]interface{}, data []openrtb2.Data, segTaxes []int) {
 	var segmentIdsToCopy = getSegmentIdsToCopy(data, segTaxes)
 	if len(segmentIdsToCopy) == 0 {
-		return target, nil
+		return
+	}
+
+	target["iab"] = segmentIdsToCopy
+}
+
+func populateFirstPartyDataAttributes(source json.RawMessage, target map[string]interface{}) error {
+	sourceAsMap, err := rawJSONToMap(source)
+	if err != nil {
+		return err
+	}
+
+	for key, val := range sourceAsMap {
+		switch typedValue := val.(type) {
+		case string:
+			target[key] = [1]string{typedValue}
+		case float64:
+			if typedValue == float64(int(typedValue)) {
+				target[key] = [1]string{strconv.Itoa(int(typedValue))}
+			}
+		case bool:
+			target[key] = [1]string{strconv.FormatBool(typedValue)}
+		case []interface{}:
+			if isStringArray(typedValue) {
+				target[key] = typedValue
+			}
+			if isBoolArray(typedValue) {
+				target[key] = convertToStringArray(typedValue)
+			}
+		}
+	}
+	return nil
+}
+
+func isStringArray(array []interface{}) bool {
+	for _, val := range array {
+		if _, ok := val.(string); !ok {
+			return false
+		}
+	}
+
+	return true
+}
+
+func isBoolArray(array []interface{}) bool {
+	for _, val := range array {
+		if _, ok := val.(bool); !ok {
+			return false
+		}
 	}
 
-	extRPTarget := make(map[string]interface{})
+	return true
+}
 
-	if target != nil {
-		if err := json.Unmarshal(target, &extRPTarget); err != nil {
-			return nil, &errortypes.BadInput{Message: err.Error()}
+func convertToStringArray(arr []interface{}) []string {
+	var stringArray []string
+	for _, val := range arr {
+		if boolVal, ok := val.(bool); ok {
+			stringArray = append(stringArray, strconv.FormatBool(boolVal))
 		}
 	}
 
-	extRPTarget["iab"] = segmentIdsToCopy
+	return stringArray
+}
+
+func rawJSONToMap(message json.RawMessage) (map[string]interface{}, error) {
+	if message == nil {
+		return make(map[string]interface{}), nil
+	}
 
-	jsonTarget, err := json.Marshal(&extRPTarget)
+	return mapFromRawJSON(message)
+}
+func mapFromRawJSON(message json.RawMessage) (map[string]interface{}, error) {
+	targetAsMap := make(map[string]interface{})
+	err := json.Unmarshal(message, &targetAsMap)
 	if err != nil {
-		return nil, &errortypes.BadInput{Message: err.Error()}
+		return nil, err
 	}
-	return jsonTarget, nil
+	return targetAsMap, nil
 }
 
 func getSegmentIdsToCopy(data []openrtb2.Data, segTaxValues []int) []string {
diff --git a/adapters/yssp/yssp.go b/adapters/yahoossp/yahoossp.go
similarity index 93%
rename from adapters/yssp/yssp.go
rename to adapters/yahoossp/yahoossp.go
index a73398ca..652d57f9 100644
--- a/adapters/yssp/yssp.go
+++ b/adapters/yahoossp/yahoossp.go
@@ -1,4 +1,4 @@
-package yssp
+package yahoossp
 
 import (
 	"encoding/json"
@@ -41,8 +41,8 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E
 			continue
 		}
 
-		var ysspExt openrtb_ext.ExtImpYSSP
-		err = json.Unmarshal(bidderExt.Bidder, &ysspExt)
+		var yahoosspExt openrtb_ext.ExtImpYahooSSP
+		err = json.Unmarshal(bidderExt.Bidder, &yahoosspExt)
 		if err != nil {
 			err = &errortypes.BadInput{
 				Message: fmt.Sprintf("imp #%d: %s", idx, err.Error()),
@@ -64,7 +64,7 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E
 			reqCopy.App = &appCopy
 		}
 
-		if err := changeRequestForBidService(&reqCopy, &ysspExt); err != nil {
+		if err := changeRequestForBidService(&reqCopy, &yahoosspExt); err != nil {
 			errors = append(errors, err)
 			continue
 		}
@@ -147,7 +147,7 @@ func getImpInfo(impId string, imps []openrtb2.Imp) (bool, openrtb_ext.BidType) {
 	return exists, mediaType
 }
 
-func changeRequestForBidService(request *openrtb2.BidRequest, extension *openrtb_ext.ExtImpYSSP) error {
+func changeRequestForBidService(request *openrtb2.BidRequest, extension *openrtb_ext.ExtImpYahooSSP) error {
 	/* Always override the tag ID and (site ID or app ID) of the request */
 	request.Imp[0].TagID = extension.Pos
 	if request.Site != nil {
@@ -180,7 +180,7 @@ func changeRequestForBidService(request *openrtb2.BidRequest, extension *openrtb
 	return nil
 }
 
-// Builder builds a new instance of the YSSP adapter for the given bidder with the given config.
+// Builder builds a new instance of the YahooSSP adapter for the given bidder with the given config.
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
 	bidder := &adapter{
 		URI: config.Endpoint,
diff --git a/adapters/yieldlab/yieldlab.go b/adapters/yieldlab/yieldlab.go
index ee9170c2..66f12c8b 100644
--- a/adapters/yieldlab/yieldlab.go
+++ b/adapters/yieldlab/yieldlab.go
@@ -212,7 +212,15 @@ func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa
 		Bids:     []*adapters.TypedBid{},
 	}
 
-	for i, bid := range bids {
+	adslotToImpMap := make(map[string]*openrtb2.Imp)
+	for i := 0; i < len(internalRequest.Imp); i++ {
+		adslotID := a.extractAdslotID(internalRequest.Imp[i])
+		if internalRequest.Imp[i].Video != nil || internalRequest.Imp[i].Banner != nil {
+			adslotToImpMap[adslotID] = &internalRequest.Imp[i]
+		}
+	}
+
+	for _, bid := range bids {
 		width, height, err := splitSize(bid.Adsize)
 		if err != nil {
 			return nil, []error{err}
@@ -225,33 +233,37 @@ func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa
 			}
 		}
 
-		var bidType openrtb_ext.BidType
-		responseBid := &openrtb2.Bid{
-			ID:     strconv.FormatUint(bid.ID, 10),
-			Price:  float64(bid.Price) / 100,
-			ImpID:  internalRequest.Imp[i].ID,
-			CrID:   a.makeCreativeID(req, bid),
-			DealID: strconv.FormatUint(bid.Pid, 10),
-			W:      int64(width),
-			H:      int64(height),
-		}
+		if imp, exists := adslotToImpMap[strconv.FormatUint(bid.ID, 10)]; !exists {
+			continue
+		} else {
+			var bidType openrtb_ext.BidType
+			responseBid := &openrtb2.Bid{
+				ID:     strconv.FormatUint(bid.ID, 10),
+				Price:  float64(bid.Price) / 100,
+				ImpID:  imp.ID,
+				CrID:   a.makeCreativeID(req, bid),
+				DealID: strconv.FormatUint(bid.Pid, 10),
+				W:      int64(width),
+				H:      int64(height),
+			}
 
-		if internalRequest.Imp[i].Video != nil {
-			bidType = openrtb_ext.BidTypeVideo
-			responseBid.NURL = a.makeAdSourceURL(internalRequest, req, bid)
+			if imp.Video != nil {
+				bidType = openrtb_ext.BidTypeVideo
+				responseBid.NURL = a.makeAdSourceURL(internalRequest, req, bid)
 
-		} else if internalRequest.Imp[i].Banner != nil {
-			bidType = openrtb_ext.BidTypeBanner
-			responseBid.AdM = a.makeBannerAdSource(internalRequest, req, bid)
-		} else {
-			// Yieldlab adapter currently doesn't support Audio and Native ads
-			continue
-		}
+			} else if imp.Banner != nil {
+				bidType = openrtb_ext.BidTypeBanner
+				responseBid.AdM = a.makeBannerAdSource(internalRequest, req, bid)
+			} else {
+				// Yieldlab adapter currently doesn't support Audio and Native ads
+				continue
+			}
 
-		bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
-			BidType: bidType,
-			Bid:     responseBid,
-		})
+			bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
+				BidType: bidType,
+				Bid:     responseBid,
+			})
+		}
 	}
 
 	return bidderResponse, nil
@@ -259,6 +271,7 @@ func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa
 
 func (a *YieldlabAdapter) findBidReq(adslotID uint64, params []*openrtb_ext.ExtImpYieldlab) *openrtb_ext.ExtImpYieldlab {
 	slotIdStr := strconv.FormatUint(adslotID, 10)
+
 	for _, p := range params {
 		if p.AdslotID == slotIdStr {
 			return p
@@ -268,6 +281,14 @@ func (a *YieldlabAdapter) findBidReq(adslotID uint64, params []*openrtb_ext.ExtI
 	return nil
 }
 
+func (a *YieldlabAdapter) extractAdslotID(internalRequestImp openrtb2.Imp) string {
+	bidderExt := new(adapters.ExtImpBidder)
+	json.Unmarshal(internalRequestImp.Ext, bidderExt)
+	yieldlabExt := new(openrtb_ext.ExtImpYieldlab)
+	json.Unmarshal(bidderExt.Bidder, yieldlabExt)
+	return yieldlabExt.AdslotID
+}
+
 func (a *YieldlabAdapter) makeBannerAdSource(req *openrtb2.BidRequest, ext *openrtb_ext.ExtImpYieldlab, res *bidResponse) string {
 	return fmt.Sprintf(adSourceBanner, a.makeAdSourceURL(req, ext, res))
 }
diff --git a/config/config.go b/config/config.go
index 0286744d..75203b9f 100644
--- a/config/config.go
+++ b/config/config.go
@@ -83,6 +83,8 @@ type Configuration struct {
 	AutoGenSourceTID bool `mapstructure:"auto_gen_source_tid"`
 	//When true, new bid id will be generated in seatbid[].bid[].ext.prebid.bidid and used in event urls instead
 	GenerateBidID bool `mapstructure:"generate_bid_id"`
+	// GenerateRequestID overrides the bidrequest.id in an AMP Request or an App Stored Request with a generated UUID if set to true. The default is false.
+	GenerateRequestID bool `mapstructure:"generate_request_id"`
 }
 
 const MIN_COOKIE_SIZE_BYTES = 500
@@ -509,9 +511,9 @@ func New(v *viper.Viper) (*Configuration, error) {
 		c.GDPR.NonStandardPublisherMap[c.GDPR.NonStandardPublishers[i]] = s
 	}
 
-	c.GDPR.EEACountriesMap = make(map[string]struct{})
-	for i := 0; i < len(c.GDPR.EEACountriesMap); i++ {
-		c.GDPR.NonStandardPublisherMap[c.GDPR.EEACountries[i]] = s
+	c.GDPR.EEACountriesMap = make(map[string]struct{}, len(c.GDPR.EEACountries))
+	for _, v := range c.GDPR.EEACountries {
+		c.GDPR.EEACountriesMap[v] = s
 	}
 
 	// To look for a purpose's vendor exceptions in O(1) time, for each purpose we fill this hash table located in the
@@ -867,6 +869,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.viewdeos.endpoint", "http://ghb.sync.viewdeos.com/pbs/ortb")
 	v.SetDefault("adapters.visx.endpoint", "https://t.visx.net/s2s_bid?wrapperType=s2s_prebid_standard:0.1.0")
 	v.SetDefault("adapters.vrtcal.endpoint", "http://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804")
+	v.SetDefault("adapters.yahoossp.disabled", true)
 	v.SetDefault("adapters.yeahmobi.endpoint", "https://{{.Host}}/prebid/bid")
 	v.SetDefault("adapters.yieldlab.endpoint", "https://ad.yieldlab.net/yp/")
 	v.SetDefault("adapters.yieldmo.endpoint", "https://ads.yieldmo.com/exchange/prebid-server")
@@ -944,6 +947,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("certificates_file", "")
 	v.SetDefault("auto_gen_source_tid", true)
 	v.SetDefault("generate_bid_id", false)
+	v.SetDefault("generate_request_id", false)
 
 	v.SetDefault("request_timeout_headers.request_time_in_queue", "")
 	v.SetDefault("request_timeout_headers.request_timeout_in_queue", "")
diff --git a/endpoints/info/bidders.go b/endpoints/info/bidders.go
index 2bd925ce..54a9104e 100644
--- a/endpoints/info/bidders.go
+++ b/endpoints/info/bidders.go
@@ -4,28 +4,62 @@ import (
 	"encoding/json"
 	"net/http"
 	"sort"
+	"strings"
 
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
 	"github.com/prebid/prebid-server/config"
 )
 
+var invalidEnabledOnly = []byte(`Invalid value for 'enabledonly' query param, must be of boolean type`)
+
 // NewBiddersEndpoint builds a handler for the /info/bidders endpoint.
 func NewBiddersEndpoint(bidders config.BidderInfos, aliases map[string]string) httprouter.Handle {
-	response, err := prepareBiddersResponse(bidders, aliases)
+	responseAll, err := prepareBiddersResponseAll(bidders, aliases)
+	if err != nil {
+		glog.Fatalf("error creating /info/bidders endpoint all bidders response: %v", err)
+	}
+
+	responseEnabledOnly, err := prepareBiddersResponseEnabledOnly(bidders, aliases)
 	if err != nil {
-		glog.Fatalf("error creating /info/bidders endpoint response: %v", err)
+		glog.Fatalf("error creating /info/bidders endpoint enabled only response: %v", err)
 	}
 
-	return func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
-		w.Header().Set("Content-Type", "application/json")
-		if _, err := w.Write(response); err != nil {
-			glog.Errorf("error writing response to /info/bidders: %v", err)
+	return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+		var writeErr error
+		switch readEnabledOnly(r) {
+		case "true":
+			w.Header().Set("Content-Type", "application/json")
+			_, writeErr = w.Write(responseEnabledOnly)
+		case "false":
+			w.Header().Set("Content-Type", "application/json")
+			_, writeErr = w.Write(responseAll)
+		default:
+			w.WriteHeader(http.StatusBadRequest)
+			_, writeErr = w.Write(invalidEnabledOnly)
+		}
+
+		if writeErr != nil {
+			glog.Errorf("error writing response to /info/bidders: %v", writeErr)
 		}
 	}
 }
 
-func prepareBiddersResponse(bidders config.BidderInfos, aliases map[string]string) ([]byte, error) {
+func readEnabledOnly(r *http.Request) string {
+	q := r.URL.Query()
+
+	v, exists := q["enabledonly"]
+
+	if !exists || len(v) == 0 {
+		// if the enabledOnly query parameter is not specified, default to false to match
+		// previous behavior of returning all adapters regardless of their enabled status.
+		return "false"
+	}
+
+	return strings.ToLower(v[0])
+}
+
+func prepareBiddersResponseAll(bidders config.BidderInfos, aliases map[string]string) ([]byte, error) {
 	bidderNames := make([]string, 0, len(bidders)+len(aliases))
 
 	for name := range bidders {
@@ -40,3 +74,23 @@ func prepareBiddersResponse(bidders config.BidderInfos, aliases map[string]strin
 
 	return json.Marshal(bidderNames)
 }
+
+func prepareBiddersResponseEnabledOnly(bidders config.BidderInfos, aliases map[string]string) ([]byte, error) {
+	bidderNames := make([]string, 0, len(bidders)+len(aliases))
+
+	for name, info := range bidders {
+		if info.Enabled {
+			bidderNames = append(bidderNames, name)
+		}
+	}
+
+	for name, bidder := range aliases {
+		if info, ok := bidders[bidder]; ok && info.Enabled {
+			bidderNames = append(bidderNames, name)
+		}
+	}
+
+	sort.Strings(bidderNames)
+
+	return json.Marshal(bidderNames)
+}
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 7a0afb0d..159e8a15 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -31,6 +31,7 @@ import (
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
 	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/prebid/prebid-server/util/uuidutil"
 )
 
 const defaultAmpRequestTimeoutMillis = 900
@@ -45,6 +46,7 @@ type AmpResponse struct {
 // NewAmpEndpoint modifies the OpenRTB endpoint to handle AMP requests. This will basically modify the parsing
 // of the request, and the return value, using the OpenRTB machinery to handle everything in between.
 func NewAmpEndpoint(
+	uuidGenerator uuidutil.UUIDGenerator,
 	ex exchange.Exchange,
 	validator openrtb_ext.BidderParamValidator,
 	requestsById stored_requests.Fetcher,
@@ -69,6 +71,7 @@ func NewAmpEndpoint(
 	}
 
 	return httprouter.Handle((&endpointDeps{
+		uuidGenerator,
 		ex,
 		validator,
 		requestsById,
@@ -345,6 +348,15 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req
 		return
 	}
 
+	if deps.cfg.GenerateRequestID {
+		newBidRequestId, err := deps.uuidGenerator.Generate()
+		if err != nil {
+			errs = []error{err}
+			return
+		}
+		req.ID = newBidRequestId
+	}
+
 	if ampParams.Debug {
 		req.Test = 1
 	}
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 1d879443..579d6687 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -14,7 +14,7 @@ import (
 	"time"
 
 	"github.com/buger/jsonparser"
-	"github.com/evanphx/json-patch"
+	jsonpatch "github.com/evanphx/json-patch"
 	"github.com/gofrs/uuid"
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
@@ -36,6 +36,7 @@ import (
 	"github.com/prebid/prebid-server/usersync"
 	"github.com/prebid/prebid-server/util/httputil"
 	"github.com/prebid/prebid-server/util/iputil"
+	"github.com/prebid/prebid-server/util/uuidutil"
 	"golang.org/x/net/publicsuffix"
 	"golang.org/x/text/currency"
 )
@@ -49,6 +50,7 @@ var (
 )
 
 func NewEndpoint(
+	uuidGenerator uuidutil.UUIDGenerator,
 	ex exchange.Exchange,
 	validator openrtb_ext.BidderParamValidator,
 	requestsById stored_requests.Fetcher,
@@ -72,6 +74,7 @@ func NewEndpoint(
 	}
 
 	return httprouter.Handle((&endpointDeps{
+		uuidGenerator,
 		ex,
 		validator,
 		requestsById,
@@ -90,6 +93,7 @@ func NewEndpoint(
 }
 
 type endpointDeps struct {
+	uuidGenerator             uuidutil.UUIDGenerator
 	ex                        exchange.Exchange
 	paramsValidator           openrtb_ext.BidderParamValidator
 	storedReqFetcher          stored_requests.Fetcher
@@ -1354,21 +1358,34 @@ func (deps *endpointDeps) processStoredRequests(ctx context.Context, requestJson
 
 	// Apply the Stored BidRequest, if it exists
 	resolvedRequest := requestJson
-	if hasStoredBidRequest {
-		resolvedRequest, err = jsonpatch.MergePatch(storedRequests[storedBidRequestId], requestJson)
+
+	if deps.cfg.GenerateRequestID {
+		isAppRequest, err := checkIfAppRequest(requestJson)
 		if err != nil {
-			hasErr, Err := getJsonSyntaxError(requestJson)
-			if hasErr {
-				err = fmt.Errorf("Invalid JSON in Incoming Request: %s", Err)
-			} else {
-				hasErr, Err = getJsonSyntaxError(storedRequests[storedBidRequestId])
-				if hasErr {
-					err = fmt.Errorf("Invalid JSON in Stored Request with ID %s: %s", storedBidRequestId, Err)
-					err = fmt.Errorf("ext.prebid.storedrequest.id refers to Stored Request %s which contains Invalid JSON: %s", storedBidRequestId, Err)
-				}
-			}
 			return nil, nil, []error{err}
 		}
+		if isAppRequest && hasStoredBidRequest {
+			uuidPatch, err := generateUuidForBidRequest(deps.uuidGenerator)
+			if err != nil {
+				return nil, nil, []error{err}
+			}
+			uuidPatch, err = jsonpatch.MergePatch(storedRequests[storedBidRequestId], uuidPatch)
+			if err != nil {
+				errL := storedRequestErrorChecker(requestJson, storedRequests, storedBidRequestId)
+				return nil, nil, errL
+			}
+			resolvedRequest, err = jsonpatch.MergePatch(requestJson, uuidPatch)
+			if err != nil {
+				errL := storedRequestErrorChecker(requestJson, storedRequests, storedBidRequestId)
+				return nil, nil, errL
+			}
+		}
+	} else if hasStoredBidRequest {
+		resolvedRequest, err = jsonpatch.MergePatch(storedRequests[storedBidRequestId], requestJson)
+		if err != nil {
+			errL := storedRequestErrorChecker(requestJson, storedRequests, storedBidRequestId)
+			return nil, nil, errL
+		}
 	}
 
 	// Apply default aliases, if they are provided
@@ -1471,7 +1488,8 @@ type ImpExtPrebidData struct {
 // (e.g. malformed json, id not a string, etc).
 func getStoredRequestId(data []byte) (string, bool, error) {
 	// These keys must be kept in sync with openrtb_ext.ExtStoredRequest
-	value, dataType, _, err := jsonparser.Get(data, "ext", openrtb_ext.PrebidExtKey, "storedrequest", "id")
+	storedRequestId, dataType, _, err := jsonparser.Get(data, "ext", openrtb_ext.PrebidExtKey, "storedrequest", "id")
+
 	if dataType == jsonparser.NotExist {
 		return "", false, nil
 	}
@@ -1481,8 +1499,7 @@ func getStoredRequestId(data []byte) (string, bool, error) {
 	if dataType != jsonparser.String {
 		return "", true, errors.New("ext.prebid.storedrequest.id must be a string")
 	}
-
-	return string(value), true, nil
+	return string(storedRequestId), true, nil
 }
 
 // setIPImplicitly sets the IP address on bidReq, if it's not explicitly defined and we can figure it out.
@@ -1584,3 +1601,35 @@ func getAccountID(pub *openrtb2.Publisher) string {
 	}
 	return metrics.PublisherUnknown
 }
+
+func storedRequestErrorChecker(requestJson []byte, storedRequests map[string]json.RawMessage, storedBidRequestId string) []error {
+	if hasErr, syntaxErr := getJsonSyntaxError(requestJson); hasErr {
+		return []error{fmt.Errorf("Invalid JSON in Incoming Request: %s", syntaxErr)}
+	}
+	if hasErr, syntaxErr := getJsonSyntaxError(storedRequests[storedBidRequestId]); hasErr {
+		return []error{fmt.Errorf("ext.prebid.storedrequest.id refers to Stored Request %s which contains Invalid JSON: %s", storedBidRequestId, syntaxErr)}
+	}
+	return nil
+}
+
+func generateUuidForBidRequest(uuidGenerator uuidutil.UUIDGenerator) ([]byte, error) {
+	newBidRequestID, err := uuidGenerator.Generate()
+	if err != nil {
+		return nil, err
+	}
+	return []byte(`{"id":"` + newBidRequestID + `"}`), nil
+}
+
+func checkIfAppRequest(request []byte) (bool, error) {
+	requestApp, dataType, _, err := jsonparser.Get(request, "app")
+	if dataType == jsonparser.NotExist {
+		return false, nil
+	}
+	if err != nil {
+		return false, err
+	}
+	if requestApp != nil {
+		return true, nil
+	}
+	return false, nil
+}
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index c7e3dfbd..56c67c4d 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -20,6 +20,7 @@ import (
 	"github.com/mxmCherry/openrtb/v15/openrtb2"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/util/iputil"
+	"github.com/prebid/prebid-server/util/uuidutil"
 
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
@@ -37,6 +38,7 @@ import (
 var defaultRequestTimeout int64 = 5000
 
 func NewVideoEndpoint(
+	uuidGenerator uuidutil.UUIDGenerator,
 	ex exchange.Exchange,
 	validator openrtb_ext.BidderParamValidator,
 	requestsById stored_requests.Fetcher,
@@ -65,6 +67,7 @@ func NewVideoEndpoint(
 	videoEndpointRegexp := regexp.MustCompile(`[<>]`)
 
 	return httprouter.Handle((&endpointDeps{
+		uuidGenerator,
 		ex,
 		validator,
 		requestsById,
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 630f18b6..c796df06 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -120,11 +120,11 @@ import (
 	"github.com/prebid/prebid-server/adapters/valueimpression"
 	"github.com/prebid/prebid-server/adapters/visx"
 	"github.com/prebid/prebid-server/adapters/vrtcal"
+	"github.com/prebid/prebid-server/adapters/yahoossp"
 	"github.com/prebid/prebid-server/adapters/yeahmobi"
 	"github.com/prebid/prebid-server/adapters/yieldlab"
 	"github.com/prebid/prebid-server/adapters/yieldmo"
 	"github.com/prebid/prebid-server/adapters/yieldone"
-	"github.com/prebid/prebid-server/adapters/yssp"
 	"github.com/prebid/prebid-server/adapters/zeroclickfraud"
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
@@ -252,15 +252,16 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderUnicorn:           unicorn.Builder,
 		openrtb_ext.BidderUnruly:            unruly.Builder,
 		openrtb_ext.BidderValueImpression:   valueimpression.Builder,
-		openrtb_ext.BidderVerizonMedia:      yssp.Builder,
+		openrtb_ext.BidderVerizonMedia:      yahoossp.Builder,
 		openrtb_ext.BidderViewdeos:          adtelligent.Builder,
 		openrtb_ext.BidderVisx:              visx.Builder,
 		openrtb_ext.BidderVrtcal:            vrtcal.Builder,
+		openrtb_ext.BidderYahooSSP:          yahoossp.Builder,
 		openrtb_ext.BidderYeahmobi:          yeahmobi.Builder,
 		openrtb_ext.BidderYieldlab:          yieldlab.Builder,
 		openrtb_ext.BidderYieldmo:           yieldmo.Builder,
 		openrtb_ext.BidderYieldone:          yieldone.Builder,
-		openrtb_ext.BidderYSSP:              yssp.Builder,
+		openrtb_ext.BidderYSSP:              yahoossp.Builder,
 		openrtb_ext.BidderZeroClickFraud:    zeroclickfraud.Builder,
 	}
 }
diff --git a/exchange/bidder.go b/exchange/bidder.go
index 28be854c..0bdaa648 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -180,24 +180,20 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb2.B
 		// If this is a test bid, capture debugging info from the requests.
 		// Write debug data to ext in case if:
 		// - headerDebugAllowed (debug override header specified correct) - it overrides all other debug restrictions
-		// - debugContextKey (url param) in true
 		// - account debug is allowed
 		// - bidder debug is allowed
 		if headerDebugAllowed {
 			seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
 		} else {
-			debugInfo := ctx.Value(DebugContextKey)
-			if debugInfo != nil && debugInfo.(bool) {
-				if accountDebugAllowed {
-					if bidder.config.DebugInfo.Allow {
-						seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
-					} else {
-						debugDisabledWarning := errortypes.Warning{
-							WarningCode: errortypes.BidderLevelDebugDisabledWarningCode,
-							Message:     "debug turned off for bidder",
-						}
-						errs = append(errs, &debugDisabledWarning)
+			if accountDebugAllowed {
+				if bidder.config.DebugInfo.Allow {
+					seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
+				} else {
+					debugDisabledWarning := errortypes.Warning{
+						WarningCode: errortypes.BidderLevelDebugDisabledWarningCode,
+						Message:     "debug turned off for bidder",
 					}
+					errs = append(errs, &debugDisabledWarning)
 				}
 			}
 		}
diff --git a/exchange/exchange.go b/exchange/exchange.go
index c612a35b..8b4b49e7 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -24,6 +24,7 @@ import (
 	"github.com/prebid/prebid-server/prebid_cache_client"
 	"github.com/prebid/prebid-server/stored_requests"
 	"github.com/prebid/prebid-server/usersync"
+	"github.com/prebid/prebid-server/util/maputil"
 
 	"github.com/buger/jsonparser"
 	"github.com/gofrs/uuid"
@@ -31,10 +32,6 @@ import (
 	"github.com/mxmCherry/openrtb/v15/openrtb2"
 )
 
-type ContextKey string
-
-const DebugContextKey = ContextKey("debugInfo")
-
 type extCacheInstructions struct {
 	cacheBids, cacheVAST, returnCreative bool
 }
@@ -187,18 +184,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 		_, targData.cacheHost, targData.cachePath = e.cache.GetExtCacheData()
 	}
 
-	if debugLog == nil {
-		debugLog = &DebugLog{Enabled: false, DebugEnabledOrOverridden: false}
-	}
-
-	requestDebugInfo := getDebugInfo(r.BidRequest, requestExt)
-
-	debugInfo := debugLog.DebugEnabledOrOverridden || (requestDebugInfo && r.Account.DebugAllow)
-	debugLog.Enabled = debugLog.DebugEnabledOrOverridden || r.Account.DebugAllow
-
-	if debugInfo {
-		ctx = e.makeDebugContext(ctx, debugInfo)
-	}
+	responseDebugAllow, accountDebugAllow, debugLog := getDebugInfo(r.BidRequest, requestExt, r.Account.DebugAllow, debugLog)
 
 	bidAdjustmentFactors := getExtBidAdjustmentFactors(requestExt)
 
@@ -223,7 +209,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 	// Get currency rates conversions for the auction
 	conversions := e.getAuctionCurrencyRates(requestExt.Prebid.CurrencyConversions)
 
-	adapterBids, adapterExtra, anyBidsReturned := e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, r.Account.DebugAllow, r.GlobalPrivacyControlHeader, debugLog.DebugOverride)
+	adapterBids, adapterExtra, anyBidsReturned := e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, accountDebugAllow, r.GlobalPrivacyControlHeader, debugLog.DebugOverride)
 
 	var auc *auction
 	var cacheErrs []error
@@ -267,7 +253,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 				errs = append(errs, dealErrs...)
 			}
 
-			bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, debugInfo, errs)
+			bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, errs)
 			if debugLog.DebugEnabledOrOverridden {
 				if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil {
 					debugLog.Data.Response = string(bidRespExtBytes)
@@ -285,9 +271,9 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 			targData.setTargeting(auc, r.BidRequest.App != nil, bidCategory)
 
 		}
-		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, debugInfo, errs)
+		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, errs)
 	} else {
-		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, debugInfo, errs)
+		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, errs)
 
 		if debugLog.DebugEnabledOrOverridden {
 
@@ -300,7 +286,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 		}
 	}
 
-	if !r.Account.DebugAllow && requestDebugInfo && !debugLog.DebugOverride {
+	if !accountDebugAllow && !debugLog.DebugOverride {
 		accountDebugDisabledWarning := openrtb_ext.ExtBidderMessage{
 			Code:    errortypes.AccountLevelDebugDisabledWarningCode,
 			Message: "debug turned off for account",
@@ -410,11 +396,6 @@ func updateHbPbCatDur(bid *pbsOrtbBid, dealTier openrtb_ext.DealTier, bidCategor
 	}
 }
 
-func (e *exchange) makeDebugContext(ctx context.Context, debugInfo bool) (debugCtx context.Context) {
-	debugCtx = context.WithValue(ctx, DebugContextKey, debugInfo)
-	return
-}
-
 func (e *exchange) makeAuctionContext(ctx context.Context, needsCache bool) (auctionCtx context.Context, cancel context.CancelFunc) {
 	auctionCtx = ctx
 	cancel = func() {}
@@ -966,9 +947,6 @@ func (e *exchange) makeBid(bids []*pbsOrtbBid, auc *auction, returnCreative bool
 }
 
 func makeBidExtJSON(ext json.RawMessage, prebid *openrtb_ext.ExtBidPrebid, impExtInfoMap map[string]ImpExtInfo, impId string) (json.RawMessage, error) {
-	// update existing bid.ext with prebid section
-	// if bid.ext.prebid already exists, it will be overwritten.
-	// if echoVideoAttrs set to true stored video attributes will be added to bid.ext.storedrequestattributes
 	var extMap map[string]interface{}
 
 	if len(ext) != 0 {
@@ -979,10 +957,22 @@ func makeBidExtJSON(ext json.RawMessage, prebid *openrtb_ext.ExtBidPrebid, impEx
 		extMap = make(map[string]interface{})
 	}
 
+	// ext.prebid
+	if prebid.Meta == nil && maputil.HasElement(extMap, "prebid", "meta") {
+		metaContainer := struct {
+			Prebid struct {
+				Meta openrtb_ext.ExtBidPrebidMeta `json:"meta"`
+			} `json:"prebid"`
+		}{}
+		if err := json.Unmarshal(ext, &metaContainer); err != nil {
+			return nil, fmt.Errorf("error validaing response from server, %s", err)
+		}
+		prebid.Meta = &metaContainer.Prebid.Meta
+	}
 	extMap[openrtb_ext.PrebidExtKey] = prebid
 
+	// ext.storedrequestattributes
 	if impExtInfo, ok := impExtInfoMap[impId]; ok && impExtInfo.EchoVideoAttrs {
-
 		videoData, _, _, err := jsonparser.Get(impExtInfo.StoredImp, "video")
 		if err != nil && err != jsonparser.KeyPathNotFoundError {
 			return nil, err
diff --git a/exchange/utils.go b/exchange/utils.go
index 1277cffd..59cc7f1e 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -690,7 +690,30 @@ func getExtTargetData(requestExt *openrtb_ext.ExtRequest, cacheInstructions *ext
 	return targData
 }
 
-func getDebugInfo(bidRequest *openrtb2.BidRequest, requestExt *openrtb_ext.ExtRequest) bool {
+// getDebugInfo returns the boolean flags that allow for debug information in bidResponse.Ext, the SeatBid.httpcalls slice, and
+// also sets the debugLog information
+func getDebugInfo(bidRequest *openrtb2.BidRequest, requestExt *openrtb_ext.ExtRequest, accountDebugFlag bool, debugLog *DebugLog) (bool, bool, *DebugLog) {
+	requestDebugAllow := parseRequestDebugValues(bidRequest, requestExt)
+	debugLog = setDebugLogValues(accountDebugFlag, debugLog)
+
+	responseDebugAllow := (requestDebugAllow && accountDebugFlag) || debugLog.DebugEnabledOrOverridden
+	accountDebugAllow := (requestDebugAllow && accountDebugFlag) || (debugLog.DebugEnabledOrOverridden && accountDebugFlag)
+
+	return responseDebugAllow, accountDebugAllow, debugLog
+}
+
+// setDebugLogValues initializes the DebugLog if nil. It also sets the value of the debugInfo flag
+// used in HoldAuction
+func setDebugLogValues(accountDebugFlag bool, debugLog *DebugLog) *DebugLog {
+	if debugLog == nil {
+		debugLog = &DebugLog{}
+	}
+
+	debugLog.Enabled = debugLog.DebugEnabledOrOverridden || accountDebugFlag
+	return debugLog
+}
+
+func parseRequestDebugValues(bidRequest *openrtb2.BidRequest, requestExt *openrtb_ext.ExtRequest) bool {
 	return (bidRequest != nil && bidRequest.Test == 1) || (requestExt != nil && requestExt.Prebid.Debug)
 }
 
diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go
index ce6e25fd..7c73fb0d 100644
--- a/openrtb_ext/bid.go
+++ b/openrtb_ext/bid.go
@@ -1,6 +1,7 @@
 package openrtb_ext
 
 import (
+	"encoding/json"
 	"fmt"
 )
 
@@ -38,18 +39,20 @@ type ExtBidPrebidCacheBids struct {
 
 // ExtBidPrebidMeta defines the contract for bidresponse.seatbid.bid[i].ext.prebid.meta
 type ExtBidPrebidMeta struct {
-	AdvertiserDomains    []string `json:"advertiserDomains,omitempty"` // or advertiserDomain?
-	AdvertiserID         int      `json:"advertiserId,omitempty"`
-	AdvertiserName       string   `json:"advertiserName,omitempty"`
-	AgencyID             int      `json:"agencyId,omitempty"`
-	AgencyName           string   `json:"agencyName,omitempty"`
-	BrandID              int      `json:"brandId,omitempty"`
-	BrandName            string   `json:"brandName,omitempty"`
-	MediaType            string   `json:"mediaType,omitempty"`
-	NetworkID            int      `json:"networkId,omitempty"`
-	NetworkName          string   `json:"networkName,omitempty"`
-	PrimaryCategoryID    string   `json:"primaryCatId,omitempty"`
-	SecondaryCategoryIDs []string `json:"secondaryCatIds,omitempty"`
+	AdvertiserDomains    []string        `json:"advertiserDomains,omitempty"`
+	AdvertiserID         int             `json:"advertiserId,omitempty"`
+	AdvertiserName       string          `json:"advertiserName,omitempty"`
+	AgencyID             int             `json:"agencyId,omitempty"`
+	AgencyName           string          `json:"agencyName,omitempty"`
+	BrandID              int             `json:"brandId,omitempty"`
+	BrandName            string          `json:"brandName,omitempty"`
+	DemandSource         string          `json:"demandSource,omitempty"`
+	DChain               json.RawMessage `json:"dchain,omitempty"`
+	MediaType            string          `json:"mediaType,omitempty"`
+	NetworkID            int             `json:"networkId,omitempty"`
+	NetworkName          string          `json:"networkName,omitempty"`
+	PrimaryCategoryID    string          `json:"primaryCatId,omitempty"`
+	SecondaryCategoryIDs []string        `json:"secondaryCatIds,omitempty"`
 }
 
 // ExtBidPrebidVideo defines the contract for bidresponse.seatbid.bid[i].ext.prebid.video
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 88f816c2..69ed1f42 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -195,6 +195,7 @@ const (
 	BidderVisx              BidderName = "visx"
 	BidderViewdeos          BidderName = "viewdeos"
 	BidderVrtcal            BidderName = "vrtcal"
+	BidderYahooSSP          BidderName = "yahoossp"
 	BidderYeahmobi          BidderName = "yeahmobi"
 	BidderYieldlab          BidderName = "yieldlab"
 	BidderYieldmo           BidderName = "yieldmo"
@@ -328,6 +329,7 @@ func CoreBidderNames() []BidderName {
 		BidderViewdeos,
 		BidderVisx,
 		BidderVrtcal,
+		BidderYahooSSP,
 		BidderYeahmobi,
 		BidderYieldlab,
 		BidderYieldmo,
diff --git a/openrtb_ext/imp_iqzone.go b/openrtb_ext/imp_iqzone.go
index 67a03376..e885625c 100644
--- a/openrtb_ext/imp_iqzone.go
+++ b/openrtb_ext/imp_iqzone.go
@@ -2,4 +2,5 @@ package openrtb_ext
 
 type ImpExtIQZone struct {
 	PlacementID string `json:"placementId"`
+	EndpointID  string `json:"endpointId"`
 }
diff --git a/openrtb_ext/imp_rubicon.go b/openrtb_ext/imp_rubicon.go
index ee43d965..f9b15ed3 100644
--- a/openrtb_ext/imp_rubicon.go
+++ b/openrtb_ext/imp_rubicon.go
@@ -10,6 +10,7 @@ type ExtImpRubicon struct {
 	SiteId    int                `json:"siteId"`
 	ZoneId    int                `json:"zoneId"`
 	Inventory json.RawMessage    `json:"inventory,omitempty"`
+	Keywords  []string           `json:"keywords,omitempty"`
 	Visitor   json.RawMessage    `json:"visitor,omitempty"`
 	Video     rubiconVideoParams `json:"video"`
 	Debug     impExtRubiconDebug `json:"debug,omitempty"`
diff --git a/openrtb_ext/imp_yahoossp.go b/openrtb_ext/imp_yahoossp.go
new file mode 100644
index 00000000..e3183c86
--- /dev/null
+++ b/openrtb_ext/imp_yahoossp.go
@@ -0,0 +1,7 @@
+package openrtb_ext
+
+// ExtImpYahooSSP defines the contract for bidrequest.imp[i].ext.yahoossp
+type ExtImpYahooSSP struct {
+	Dcn string `json:"dcn"`
+	Pos string `json:"pos"`
+}
diff --git a/openrtb_ext/imp_yssp.go b/openrtb_ext/imp_yssp.go
deleted file mode 100644
index 054836ec..00000000
--- a/openrtb_ext/imp_yssp.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package openrtb_ext
-
-// ExtImpYSSP defines the contract for bidrequest.imp[i].ext.yssp
-type ExtImpYSSP struct {
-	Dcn string `json:"dcn"`
-	Pos string `json:"pos"`
-}
diff --git a/router/router.go b/router/router.go
index 446d4280..17145b5a 100644
--- a/router/router.go
+++ b/router/router.go
@@ -12,6 +12,13 @@ import (
 	"strings"
 	"time"
 
+	"github.com/prebid/prebid-server/currency"
+	"github.com/prebid/prebid-server/endpoints/events"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/util/uuidutil"
+
+	"github.com/prebid/prebid-server/metrics"
+
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/adapters/adform"
 	"github.com/prebid/prebid-server/adapters/appnexus"
@@ -27,15 +34,11 @@ import (
 	"github.com/prebid/prebid-server/cache/filecache"
 	"github.com/prebid/prebid-server/cache/postgrescache"
 	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/currency"
 	"github.com/prebid/prebid-server/endpoints"
-	"github.com/prebid/prebid-server/endpoints/events"
 	infoEndpoints "github.com/prebid/prebid-server/endpoints/info"
 	"github.com/prebid/prebid-server/endpoints/openrtb2"
-	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/exchange"
 	"github.com/prebid/prebid-server/gdpr"
-	"github.com/prebid/prebid-server/metrics"
 	metricsConf "github.com/prebid/prebid-server/metrics/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/pbs"
@@ -276,18 +279,18 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	}
 
 	theExchange := exchange.NewExchange(adapters, cacheClient, cfg, syncersByBidder, r.MetricsEngine, bidderInfos, gdprPerms, rateConvertor, categoriesFetcher)
-
-	openrtbEndpoint, err := openrtb2.NewEndpoint(theExchange, paramsValidator, fetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders)
+	var uuidGenerator uuidutil.UUIDRandomGenerator
+	openrtbEndpoint, err := openrtb2.NewEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders)
 	if err != nil {
 		glog.Fatalf("Failed to create the openrtb2 endpoint handler. %v", err)
 	}
 
-	ampEndpoint, err := openrtb2.NewAmpEndpoint(theExchange, paramsValidator, ampFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders)
+	ampEndpoint, err := openrtb2.NewAmpEndpoint(uuidGenerator, theExchange, paramsValidator, ampFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders)
 	if err != nil {
 		glog.Fatalf("Failed to create the amp endpoint handler. %v", err)
 	}
 
-	videoEndpoint, err := openrtb2.NewVideoEndpoint(theExchange, paramsValidator, fetcher, videoFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, cacheClient)
+	videoEndpoint, err := openrtb2.NewVideoEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, videoFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, cacheClient)
 	if err != nil {
 		glog.Fatalf("Failed to create the video endpoint handler. %v", err)
 	}
@@ -337,7 +340,9 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 
 func applyBidderInfoConfigOverrides(bidderInfos config.BidderInfos, adaptersCfg map[string]config.Adapter) error {
 	for bidderName, bidderInfo := range bidderInfos {
-		if adapterCfg, exists := adaptersCfg[bidderName]; exists {
+		// bidder name from bidderInfos is case-sensitive, but bidder name from adaptersCfg
+		// is always expressed as lower case. need to adapt for the difference here.
+		if adapterCfg, exists := adaptersCfg[strings.ToLower(bidderName)]; exists {
 			bidderInfo.Syncer = adapterCfg.Syncer.Override(bidderInfo.Syncer)
 
 			// validate and try to apply the legacy usersync_url configuration in attempt to provide
diff --git a/static/bidder-info/adot.yaml b/static/bidder-info/adot.yaml
index 529ff3ae..6a7aca06 100644
--- a/static/bidder-info/adot.yaml
+++ b/static/bidder-info/adot.yaml
@@ -12,3 +12,7 @@ capabilities:
       - banner
       - video
       - native
+userSync:
+  redirect:
+    url: "https://sync.adotmob.com/cookie/pbs?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}"
+    userMacro: "{amob_user_id}"
\ No newline at end of file
diff --git a/static/bidder-info/yahoossp.yaml b/static/bidder-info/yahoossp.yaml
new file mode 100644
index 00000000..06d0cb18
--- /dev/null
+++ b/static/bidder-info/yahoossp.yaml
@@ -0,0 +1,15 @@
+maintainer:
+  email: "hb-fe-tech@oath.com"
+gvlVendorID: 25
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+  site:
+    mediaTypes:
+      - banner
+userSync:
+  # yahoossp supports user syncing, but requires configuration by the host. contact this
+  # bidder directly at the email address in this file to ask about enabling user sync.
+  supports:
+    - redirect
\ No newline at end of file
diff --git a/static/bidder-params/iqzone.json b/static/bidder-params/iqzone.json
index 38e98c53..5113dfaf 100644
--- a/static/bidder-params/iqzone.json
+++ b/static/bidder-params/iqzone.json
@@ -1,15 +1,21 @@
 {
     "$schema": "http://json-schema.org/draft-04/schema#",
-    "title": "IQZone Adapter Params",
-    "description": "A schema which validates params accepted by the IQZone adapter",
+    "title": "IQzone Adapter Params",
+    "description": "A schema which validates params accepted by the IQzone adapter",
     "type": "object",
   
     "properties": {
       "placementId": {
         "type": "string",
         "description": "Placement ID"
+      },
+      "endpointId": {
+        "type": "string",
+        "description": "Endpoint ID"
       }
     },
-  
-    "required": ["placementId"]
+    "oneOf": [
+      { "required": ["placementId"] },
+      { "required": ["endpointId"] }
+    ]
   }
\ No newline at end of file
diff --git a/static/bidder-params/yahoossp.json b/static/bidder-params/yahoossp.json
new file mode 100644
index 00000000..9d971149
--- /dev/null
+++ b/static/bidder-params/yahoossp.json
@@ -0,0 +1,19 @@
+{
+	"$schema": "http://json-schema.org/draft-04/schema#",
+	"title": "YahooSSP Adapter Params",
+	"description": "A schema which validates params accepted by the YahooSSP adapter",
+	"type": "object",
+	"properties": {
+		"dcn": {
+			"type": "string",
+			"minLength": 1,
+			"description": "Site ID provided by One Mobile"
+		},
+		"pos": {
+			"type": "string",
+			"minLength": 1,
+			"description": "Placement ID"
+		}
+	},
+	"required": ["dcn", "pos"]
+}
diff --git a/util/maputil/maputil.go b/util/maputil/maputil.go
index 595e9321..49df0772 100644
--- a/util/maputil/maputil.go
+++ b/util/maputil/maputil.go
@@ -20,7 +20,7 @@ func ReadEmbeddedSlice(m map[string]interface{}, k string) ([]interface{}, bool)
 	return nil, false
 }
 
-// ReadEmbeddedString reads element k from the map m as a string
+// ReadEmbeddedString reads element k from the map m as a string.
 func ReadEmbeddedString(m map[string]interface{}, k string) (string, bool) {
 	if v, ok := m[k]; ok {
 		vCasted, ok := v.(string)
@@ -28,3 +28,23 @@ func ReadEmbeddedString(m map[string]interface{}, k string) (string, bool) {
 	}
 	return "", false
 }
+
+// HasElement returns true if nested element k exists.
+func HasElement(m map[string]interface{}, k ...string) bool {
+	exists := false
+	kLastIndex := len(k) - 1
+
+	for i, k := range k {
+		isLastKey := i == kLastIndex
+
+		if isLastKey {
+			_, exists = m[k]
+		} else {
+			if m, exists = ReadEmbeddedMap(m, k); !exists {
+				break
+			}
+		}
+	}
+
+	return exists
+}
diff --git a/util/uuidutil/uuidutil.go b/util/uuidutil/uuidutil.go
new file mode 100644
index 00000000..6e4848f6
--- /dev/null
+++ b/util/uuidutil/uuidutil.go
@@ -0,0 +1,19 @@
+package uuidutil
+
+import (
+	"github.com/gofrs/uuid"
+)
+
+type UUIDGenerator interface {
+	Generate() (string, error)
+}
+
+type UUIDRandomGenerator struct{}
+
+func (UUIDRandomGenerator) Generate() (string, error) {
+	id, err := uuid.NewV4()
+	if err != nil {
+		return "", err
+	}
+	return id.String(), nil
+}


Test patch:
diff --git a/adapters/iqzone/iqzonetest/exemplary/endpointId.json b/adapters/iqzone/iqzonetest/exemplary/endpointId.json
new file mode 100644
index 00000000..79887443
--- /dev/null
+++ b/adapters/iqzone/iqzonetest/exemplary/endpointId.json
@@ -0,0 +1,133 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "iPad"
+        },
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "tagid": "test",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "endpointId": "test"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://smartssp-us-east.iqzone.com/pserver",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "tagid": "test",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "endpointId": "test",
+                                    "type": "network"
+                                }
+                            }
+                        }
+                    ],
+                    "app": {
+                        "id": "1",
+                        "bundle": "com.wls.testwlsapplication"
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "iPad"
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://smartssp-us-east.iqzone.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "w": 300,
+                                    "h": 250,
+                                    "ext": {
+                                        "prebid": {
+                                            "type": "banner"
+                                        }
+                                    }
+                                }
+                            ],
+                            "seat": "iqzone"
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://smartssp-us-east.iqzone.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "prebid": {
+                                "type": "banner"
+                            }
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/iqzone/iqzonetest/exemplary/simple-banner.json b/adapters/iqzone/iqzonetest/exemplary/simple-banner.json
index f816191e..12f97e1b 100644
--- a/adapters/iqzone/iqzonetest/exemplary/simple-banner.json
+++ b/adapters/iqzone/iqzonetest/exemplary/simple-banner.json
@@ -57,7 +57,8 @@
                             },
                             "ext": {
                                 "bidder": {
-                                    "placementId": "test"
+                                    "placementId": "test",
+                                    "type": "publisher"
                                 }
                             }
                         }
diff --git a/adapters/iqzone/iqzonetest/exemplary/simple-native.json b/adapters/iqzone/iqzonetest/exemplary/simple-native.json
index 819e14bf..967a846d 100644
--- a/adapters/iqzone/iqzonetest/exemplary/simple-native.json
+++ b/adapters/iqzone/iqzonetest/exemplary/simple-native.json
@@ -41,7 +41,8 @@
                             },
                             "ext": {
                                 "bidder": {
-                                    "placementId": "test"
+                                    "placementId": "test",
+                                    "type": "publisher"
                                 }
                             }
                         }
diff --git a/adapters/iqzone/iqzonetest/exemplary/simple-video.json b/adapters/iqzone/iqzonetest/exemplary/simple-video.json
index 16e6f4c7..7e2b7971 100644
--- a/adapters/iqzone/iqzonetest/exemplary/simple-video.json
+++ b/adapters/iqzone/iqzonetest/exemplary/simple-video.json
@@ -63,7 +63,8 @@
                             },
                             "ext": {
                                 "bidder": {
-                                    "placementId": "test"
+                                    "placementId": "test",
+                                    "type": "publisher"
                                 }
                             }
                         }
diff --git a/adapters/iqzone/iqzonetest/exemplary/simple-web-banner.json b/adapters/iqzone/iqzonetest/exemplary/simple-web-banner.json
index 968c0305..4d69ef41 100644
--- a/adapters/iqzone/iqzonetest/exemplary/simple-web-banner.json
+++ b/adapters/iqzone/iqzonetest/exemplary/simple-web-banner.json
@@ -57,7 +57,8 @@
                             },
                             "ext": {
                                 "bidder": {
-                                    "placementId": "test"
+                                    "placementId": "test",
+                                    "type": "publisher"
                                 }
                             }
                         }
diff --git a/adapters/iqzone/iqzonetest/supplemental/bad_media_type.json b/adapters/iqzone/iqzonetest/supplemental/bad_media_type.json
index 3d192310..4dddd568 100644
--- a/adapters/iqzone/iqzonetest/supplemental/bad_media_type.json
+++ b/adapters/iqzone/iqzonetest/supplemental/bad_media_type.json
@@ -30,7 +30,8 @@
                         "id": "test-imp-id",
                         "ext": {
                             "bidder": {
-                                "placementId": "test"
+                                "placementId": "test",
+                                "type": "publisher"
                             }
                         }
                     }
diff --git a/adapters/iqzone/iqzonetest/supplemental/bad_response.json b/adapters/iqzone/iqzonetest/supplemental/bad_response.json
index ad420705..74c603b4 100644
--- a/adapters/iqzone/iqzonetest/supplemental/bad_response.json
+++ b/adapters/iqzone/iqzonetest/supplemental/bad_response.json
@@ -54,7 +54,8 @@
                         },
                         "ext": {
                             "bidder": {
-                                "placementId": "test"
+                                "placementId": "test",
+                                "type": "publisher"
                             }
                         }
                     }
diff --git a/adapters/iqzone/iqzonetest/supplemental/status-204.json b/adapters/iqzone/iqzonetest/supplemental/status-204.json
index 7c2f21c0..8ef22bda 100644
--- a/adapters/iqzone/iqzonetest/supplemental/status-204.json
+++ b/adapters/iqzone/iqzonetest/supplemental/status-204.json
@@ -54,7 +54,8 @@
                         },
                         "ext": {
                             "bidder": {
-                                "placementId": "test"
+                                "placementId": "test",
+                                "type": "publisher"
                             }
                         }
                     }
diff --git a/adapters/iqzone/iqzonetest/supplemental/status-not-200.json b/adapters/iqzone/iqzonetest/supplemental/status-not-200.json
index 22160ac7..2cb4ba98 100644
--- a/adapters/iqzone/iqzonetest/supplemental/status-not-200.json
+++ b/adapters/iqzone/iqzonetest/supplemental/status-not-200.json
@@ -18,7 +18,7 @@
                 },
                 "ext": {
                     "bidder": {
-                        "TagID": "test"
+                        "placementId": "test"
                     }
                 }
             }
@@ -54,7 +54,8 @@
                         },
                         "ext": {
                             "bidder": {
-                                "TagID": "test"
+                                "placementId": "test",
+                                "type": "publisher"
                             }
                         }
                     }
diff --git a/adapters/iqzone/params_test.go b/adapters/iqzone/params_test.go
index deb4af2e..c06f5e7f 100644
--- a/adapters/iqzone/params_test.go
+++ b/adapters/iqzone/params_test.go
@@ -36,10 +36,12 @@ func TestInvalidParams(t *testing.T) {
 var validParams = []string{
 	`{"placementId": "test"}`,
 	`{"placementId": "1"}`,
+	`{"endpointId": "test"}`,
+	`{"endpointId": "1"}`,
 }
 
 var invalidParams = []string{
 	`{"placementId": 42}`,
-	`{}`,
-	`{"someOtherParam": "value"}`,
+	`{"endpointId": 42}`,
+	`{"placementId": "1", "endpointId": "1"}`,
 }
diff --git a/adapters/rubicon/rubicon_test.go b/adapters/rubicon/rubicon_test.go
index 2d4c962b..9bfa04fa 100644
--- a/adapters/rubicon/rubicon_test.go
+++ b/adapters/rubicon/rubicon_test.go
@@ -35,6 +35,12 @@ type rubiAppendTrackerUrlTestScenario struct {
 	expected string
 }
 
+type rubiPopulateFpdAttributesScenario struct {
+	source json.RawMessage
+	target map[string]interface{}
+	result map[string]interface{}
+}
+
 type rubiSetNetworkIdTestScenario struct {
 	bidExt            *openrtb_ext.ExtBidPrebid
 	buyer             string
@@ -1277,7 +1283,10 @@ func TestOpenRTBRequestWithImpAndAdSlotIncluded(t *testing.T) {
 				},
 				"context": {
 					"data": {
-						"adslot": "///test-adslot"
+                        "adserver": {
+                             "adslot": "/test-adslot",
+                             "name": "gam"
+                        }
 					}
 				}
 			}`),
@@ -1302,14 +1311,52 @@ func TestOpenRTBRequestWithImpAndAdSlotIncluded(t *testing.T) {
 	if err := json.Unmarshal(rubiconReq.Imp[0].Ext, &rpImpExt); err != nil {
 		t.Fatal("Error unmarshalling imp.ext")
 	}
+	assert.Equal(t, rpImpExt.GPID, "/test-adslot")
+}
 
-	rubiconExtInventory := make(map[string]interface{})
-	if err := json.Unmarshal(rpImpExt.RP.Target, &rubiconExtInventory); err != nil {
-		t.Fatal("Error unmarshalling imp.ext.rp.target")
+func TestOpenRTBFirstPartyDataPopulating(t *testing.T) {
+	testScenarios := []rubiPopulateFpdAttributesScenario{
+		{
+			source: json.RawMessage(`{"sourceKey": ["sourceValue", "sourceValue2"]}`),
+			target: map[string]interface{}{"targetKey": []interface{}{"targetValue"}},
+			result: map[string]interface{}{"targetKey": []interface{}{"targetValue"}, "sourceKey": []interface{}{"sourceValue", "sourceValue2"}},
+		},
+		{
+			source: json.RawMessage(`{"sourceKey": ["sourceValue", "sourceValue2"]}`),
+			target: make(map[string]interface{}),
+			result: map[string]interface{}{"sourceKey": []interface{}{"sourceValue", "sourceValue2"}},
+		},
+		{
+			source: json.RawMessage(`{"sourceKey": "sourceValue"}`),
+			target: make(map[string]interface{}),
+			result: map[string]interface{}{"sourceKey": [1]string{"sourceValue"}},
+		},
+		{
+			source: json.RawMessage(`{"sourceKey": true, "sourceKey2": [true, false, true]}`),
+			target: make(map[string]interface{}),
+			result: map[string]interface{}{"sourceKey": [1]string{"true"}, "sourceKey2": []string{"true", "false", "true"}},
+		},
+		{
+			source: json.RawMessage(`{"sourceKey": 1, "sourceKey2": [1, 2, 3]}`),
+			target: make(map[string]interface{}),
+			result: map[string]interface{}{"sourceKey": [1]string{"1"}},
+		},
+		{
+			source: json.RawMessage(`{"sourceKey": 1, "sourceKey2": 3.23}`),
+			target: make(map[string]interface{}),
+			result: map[string]interface{}{"sourceKey": [1]string{"1"}},
+		},
+		{
+			source: json.RawMessage(`{"sourceKey": {}}`),
+			target: make(map[string]interface{}),
+			result: make(map[string]interface{}),
+		},
 	}
 
-	assert.Equal(t, "test-adslot", rubiconExtInventory["dfp_ad_unit_code"],
-		"Unexpected dfp_ad_unit_code: %s", rubiconExtInventory["dfp_ad_unit_code"])
+	for _, scenario := range testScenarios {
+		populateFirstPartyDataAttributes(scenario.source, scenario.target)
+		assert.Equal(t, scenario.result, scenario.target)
+	}
 }
 
 func TestOpenRTBRequestWithBadvOverflowed(t *testing.T) {
diff --git a/adapters/rubicon/rubicontest/exemplary/app-imp-fpd.json b/adapters/rubicon/rubicontest/exemplary/app-imp-fpd.json
new file mode 100644
index 00000000..eec396f8
--- /dev/null
+++ b/adapters/rubicon/rubicontest/exemplary/app-imp-fpd.json
@@ -0,0 +1,411 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    },
+    "app": {
+      "pagecat": [
+        "val1",
+        "val2"
+      ],
+      "sectioncat": [
+        "sectionCat1",
+        "sectionCat2"
+      ],
+      "ext": {
+        "data": {
+          "attr1": "val1",
+          "attr2": [
+            "val21",
+            "val22"
+          ],
+          "attr3": true,
+          "attr4": [
+            true,
+            false,
+            true,
+            true
+          ],
+          "attr5": 3,
+          "attr6": [
+            1,
+            2,
+            3
+          ],
+          "attr7": 1.23
+        }
+      }
+    },
+    "user": {
+      "yob": 2000,
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "gender": "f",
+      "data": [
+        {
+          "ext": {
+            "segtax": 4
+          },
+          "segment": [
+            {
+              "id": "idToCopy"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": "someValue"
+          },
+          "segment": [
+            {
+              "id": "shouldNotBeCopied"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": "4"
+          },
+          "segment": [
+            {
+              "id": "shouldNotBeCopied2"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": 4
+          },
+          "segment": [
+            {
+              "id": "idToCopy2"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": [
+              4
+            ]
+          },
+          "segment": [
+            {
+              "id": "shouldNotBeCopied3"
+            }
+          ]
+        }
+      ],
+      "ext": {
+        "data": {
+          "attr1": "val1",
+          "attr2": [
+            "val21",
+            "val22"
+          ],
+          "attr3": true,
+          "attr4": [
+            true,
+            false,
+            true,
+            true
+          ],
+          "attr5": 3,
+          "attr6": [
+            1,
+            2,
+            3
+          ],
+          "attr7": 1.23
+        }
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "instl": 1,
+        "video": {
+          "placement": 3,
+          "mimes": [
+            "video/mp4"
+          ],
+          "protocols": [
+            2,
+            5
+          ],
+          "w": 1024,
+          "h": 576
+        },
+        "ext": {
+          "context": {
+            "data": {
+              "adserver": {
+                "name": "gam",
+                "adslot": "someAdSlot"
+              },
+              "dataAttr1": "dataVal1",
+              "dataAttr2": "dataVal2"
+            }
+          },
+          "bidder": {
+            "video": {
+            },
+            "accountId": 1001,
+            "siteId": 113932,
+            "zoneId": 535510
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "uri?tk_xint=pbs-test-tracker",
+        "body": {
+          "id": "test-request-id",
+          "device": {
+            "ext": {
+              "rp": {
+                "pixelratio": 0
+              }
+            },
+            "ip": "123.123.123.123",
+            "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+          },
+          "app": {
+            "pagecat": [
+              "val1",
+              "val2"
+            ],
+            "sectioncat": [
+              "sectionCat1",
+              "sectionCat2"
+            ],
+            "ext": {
+              "rp": {
+                "site_id": 113932
+              }
+            },
+            "publisher": {
+              "ext": {
+                "rp": {
+                  "account_id": 1001
+                }
+              }
+            }
+          },
+          "user": {
+            "data": [
+              {
+                "ext": {
+                  "segtax": 4
+                },
+                "segment": [
+                  {
+                    "id": "idToCopy"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": "someValue"
+                },
+                "segment": [
+                  {
+                    "id": "shouldNotBeCopied"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": "4"
+                },
+                "segment": [
+                  {
+                    "id": "shouldNotBeCopied2"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": 4
+                },
+                "segment": [
+                  {
+                    "id": "idToCopy2"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": [
+                    4
+                  ]
+                },
+                "segment": [
+                  {
+                    "id": "shouldNotBeCopied3"
+                  }
+                ]
+              }
+            ],
+            "ext": {
+              "rp": {
+                "target": {
+                  "attr1": [
+                    "val1"
+                  ],
+                  "attr2": [
+                    "val21",
+                    "val22"
+                  ],
+                  "attr3": [
+                    "true"
+                  ],
+                  "attr4": [
+                    "true",
+                    "false",
+                    "true",
+                    "true"
+                  ],
+                  "attr5": [
+                    "3"
+                  ],
+                  "iab": [
+                    "idToCopy",
+                    "idToCopy2"
+                  ]
+                }
+              }
+            }
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "instl": 1,
+              "video": {
+                "placement": 3,
+                "ext": {
+                  "rp": {
+                    "size_id": 203
+                  }
+                },
+                "mimes": [
+                  "video/mp4"
+                ],
+                "protocols": [
+                  2,
+                  5
+                ],
+                "w": 1024,
+                "h": 576
+              },
+              "ext": {
+                "gpid": "someAdSlot",
+                "rp": {
+                  "target": {
+                    "dataAttr1": [
+                      "dataVal1"
+                    ],
+                    "dataAttr2": [
+                      "dataVal2"
+                    ],
+                    "attr1": [
+                      "val1"
+                    ],
+                    "attr2": [
+                      "val21",
+                      "val22"
+                    ],
+                    "attr3": [
+                      "true"
+                    ],
+                    "attr4": [
+                      "true",
+                      "false",
+                      "true",
+                      "true"
+                    ],
+                    "attr5": [
+                      "3"
+                    ],
+                    "pagecat": [
+                      "val1",
+                      "val2"
+                    ],
+                    "sectioncat": [
+                      "sectionCat1",
+                      "sectionCat2"
+                    ]
+                  },
+                  "track": {
+                    "mint": "",
+                    "mint_version": ""
+                  },
+                  "zone_id": 535510
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test_bid_id",
+                  "impid": "test-imp-id",
+                  "price": 0.27543,
+                  "adm": "some-test-ad",
+                  "cid": "test_cid",
+                  "crid": "test_crid",
+                  "dealid": "test_dealid",
+                  "ext": {
+                    "prebid": {
+                      "type": "video"
+                    }
+                  }
+                }
+              ],
+              "seat": "adman"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "some-test-ad",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/rubicon/rubicontest/exemplary/simple-video.json b/adapters/rubicon/rubicontest/exemplary/simple-video.json
index 7e5274f2..592f9fb2 100644
--- a/adapters/rubicon/rubicontest/exemplary/simple-video.json
+++ b/adapters/rubicon/rubicontest/exemplary/simple-video.json
@@ -10,6 +10,10 @@
       "bundle": "com.wls.testwlsapplication"
     },
     "site": {
+      "pagecat": [
+        "val1",
+        "val2"
+      ],
       "content": {
         "data": [
           {
@@ -49,6 +53,13 @@
       }
     },
     "user": {
+      "yob": 2000,
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "gender": "f",
       "data": [
         {
           "ext": {
@@ -148,6 +159,10 @@
             "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
           },
           "site": {
+            "pagecat": [
+              "val1",
+              "val2"
+            ],
             "content": {
               "data": [
                 {
@@ -298,6 +313,12 @@
               },
               "ext": {
                 "rp": {
+                  "target": {
+                    "pagecat": [
+                      "val1",
+                      "val2"
+                    ]
+                  },
                   "track": {
                     "mint": "",
                     "mint_version": ""
diff --git a/adapters/rubicon/rubicontest/exemplary/site-imp-fpd.json b/adapters/rubicon/rubicontest/exemplary/site-imp-fpd.json
new file mode 100644
index 00000000..56f5ce61
--- /dev/null
+++ b/adapters/rubicon/rubicontest/exemplary/site-imp-fpd.json
@@ -0,0 +1,542 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    },
+    "site": {
+      "pagecat": [
+        "val1",
+        "val2"
+      ],
+      "sectioncat": [
+        "sectionCat1",
+        "sectionCat2"
+      ],
+      "page": "somePage",
+      "ref": "someRef",
+      "search": "someSearch",
+      "ext": {
+        "data": {
+          "attr1": "val1",
+          "attr2": [
+            "val21",
+            "val22"
+          ],
+          "attr3": true,
+          "attr4": [
+            true,
+            false,
+            true,
+            true
+          ],
+          "attr5": 3,
+          "attr6": [
+            1,
+            2,
+            3
+          ],
+          "attr7": 1.23
+        }
+      },
+      "content": {
+        "data": [
+          {
+            "ext": {
+              "segtax": 1
+            },
+            "segment": [
+              {
+                "id": "idToCopy"
+              }
+            ]
+          },
+          {
+            "ext": {
+              "segtax": "someValue"
+            },
+            "segment": [
+              {
+                "id": "shouldNotBeCopied"
+              }
+            ]
+          },
+          {
+            "ext": {
+              "segtax": "2"
+            },
+            "segment": [
+              {
+                "id": "shouldNotBeCopied2"
+              }
+            ]
+          },
+          {
+            "ext": {
+              "segtax": 2
+            },
+            "segment": [
+              {
+                "id": "idToCopy2"
+              }
+            ]
+          },
+          {
+            "ext": {
+              "segtax": 5
+            },
+            "segment": [
+              {
+                "id": "idToCopy3"
+              }
+            ]
+          },
+          {
+            "ext": {
+              "segtax": 6
+            },
+            "segment": [
+              {
+                "id": "idToCopy4"
+              }
+            ]
+          },
+          {
+            "ext": {
+              "segtax": [
+                2
+              ]
+            },
+            "segment": [
+              {
+                "id": "shouldNotBeCopied3"
+              }
+            ]
+          }
+        ]
+      }
+    },
+    "user": {
+      "yob": 2000,
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "gender": "f",
+      "data": [
+        {
+          "ext": {
+            "segtax": 4
+          },
+          "segment": [
+            {
+              "id": "idToCopy"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": "someValue"
+          },
+          "segment": [
+            {
+              "id": "shouldNotBeCopied"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": "4"
+          },
+          "segment": [
+            {
+              "id": "shouldNotBeCopied2"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": 4
+          },
+          "segment": [
+            {
+              "id": "idToCopy2"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": [
+              4
+            ]
+          },
+          "segment": [
+            {
+              "id": "shouldNotBeCopied3"
+            }
+          ]
+        }
+      ]
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "instl": 1,
+        "video": {
+          "placement": 3,
+          "mimes": [
+            "video/mp4"
+          ],
+          "protocols": [
+            2,
+            5
+          ],
+          "w": 1024,
+          "h": 576
+        },
+        "ext": {
+          "data": {
+            "adserver": {
+              "name": "gam",
+              "adslot": "someAdSlot"
+            },
+            "dataAttr1": "dataVal1",
+            "dataAttr2": "dataVal2"
+          },
+          "bidder": {
+            "video": {
+            },
+            "accountId": 1001,
+            "siteId": 113932,
+            "zoneId": 535510
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "uri?tk_xint=pbs-test-tracker",
+        "body": {
+          "id": "test-request-id",
+          "device": {
+            "ext": {
+              "rp": {
+                "pixelratio": 0
+              }
+            },
+            "ip": "123.123.123.123",
+            "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+          },
+          "site": {
+            "pagecat": [
+              "val1",
+              "val2"
+            ],
+            "sectioncat": [
+              "sectionCat1",
+              "sectionCat2"
+            ],
+            "page": "somePage",
+            "ref": "someRef",
+            "search": "someSearch",
+            "content": {
+              "data": [
+                {
+                  "ext": {
+                    "segtax": 1
+                  },
+                  "segment": [
+                    {
+                      "id": "idToCopy"
+                    }
+                  ]
+                },
+                {
+                  "ext": {
+                    "segtax": "someValue"
+                  },
+                  "segment": [
+                    {
+                      "id": "shouldNotBeCopied"
+                    }
+                  ]
+                },
+                {
+                  "ext": {
+                    "segtax": "2"
+                  },
+                  "segment": [
+                    {
+                      "id": "shouldNotBeCopied2"
+                    }
+                  ]
+                },
+                {
+                  "ext": {
+                    "segtax": 2
+                  },
+                  "segment": [
+                    {
+                      "id": "idToCopy2"
+                    }
+                  ]
+                },
+                {
+                  "ext": {
+                    "segtax": 5
+                  },
+                  "segment": [
+                    {
+                      "id": "idToCopy3"
+                    }
+                  ]
+                },
+                {
+                  "ext": {
+                    "segtax": 6
+                  },
+                  "segment": [
+                    {
+                      "id": "idToCopy4"
+                    }
+                  ]
+                },
+                {
+                  "ext": {
+                    "segtax": [
+                      2
+                    ]
+                  },
+                  "segment": [
+                    {
+                      "id": "shouldNotBeCopied3"
+                    }
+                  ]
+                }
+              ]
+            },
+            "ext": {
+              "rp": {
+                "site_id": 113932,
+                "target": {
+                  "iab": [
+                    "idToCopy",
+                    "idToCopy2",
+                    "idToCopy3",
+                    "idToCopy4"
+                  ]
+                }
+              }
+            },
+            "publisher": {
+              "ext": {
+                "rp": {
+                  "account_id": 1001
+                }
+              }
+            }
+          },
+          "user": {
+            "data": [
+              {
+                "ext": {
+                  "segtax": 4
+                },
+                "segment": [
+                  {
+                    "id": "idToCopy"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": "someValue"
+                },
+                "segment": [
+                  {
+                    "id": "shouldNotBeCopied"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": "4"
+                },
+                "segment": [
+                  {
+                    "id": "shouldNotBeCopied2"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": 4
+                },
+                "segment": [
+                  {
+                    "id": "idToCopy2"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": [
+                    4
+                  ]
+                },
+                "segment": [
+                  {
+                    "id": "shouldNotBeCopied3"
+                  }
+                ]
+              }
+            ],
+            "ext": {
+              "rp": {
+                "target": {
+                  "iab": [
+                    "idToCopy",
+                    "idToCopy2"
+                  ]
+                }
+              }
+            }
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "instl": 1,
+              "video": {
+                "placement": 3,
+                "ext": {
+                  "rp": {
+                    "size_id": 203
+                  }
+                },
+                "mimes": [
+                  "video/mp4"
+                ],
+                "protocols": [
+                  2,
+                  5
+                ],
+                "w": 1024,
+                "h": 576
+              },
+              "ext": {
+                "gpid": "someAdSlot",
+                "rp": {
+                  "target": {
+                    "dataAttr1": [
+                      "dataVal1"
+                    ],
+                    "dataAttr2": [
+                      "dataVal2"
+                    ],
+                    "attr1": [
+                      "val1"
+                    ],
+                    "attr2": [
+                      "val21",
+                      "val22"
+                    ],
+                    "attr3": [
+                      "true"
+                    ],
+                    "attr4": [
+                      "true",
+                      "false",
+                      "true",
+                      "true"
+                    ],
+                    "attr5": [
+                      "3"
+                    ],
+                    "page": [
+                      "somePage"
+                    ],
+                    "ref": [
+                      "someRef"
+                    ],
+                    "search": [
+                      "someSearch"
+                    ],
+                    "pagecat": [
+                      "val1",
+                      "val2"
+                    ],
+                    "sectioncat": [
+                      "sectionCat1",
+                      "sectionCat2"
+                    ]
+                  },
+                  "track": {
+                    "mint": "",
+                    "mint_version": ""
+                  },
+                  "zone_id": 535510
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test_bid_id",
+                  "impid": "test-imp-id",
+                  "price": 0.27543,
+                  "adm": "some-test-ad",
+                  "cid": "test_cid",
+                  "crid": "test_crid",
+                  "dealid": "test_dealid",
+                  "ext": {
+                    "prebid": {
+                      "type": "video"
+                    }
+                  }
+                }
+              ],
+              "seat": "adman"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "some-test-ad",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/rubicon/rubicontest/exemplary/user-fpd.json b/adapters/rubicon/rubicontest/exemplary/user-fpd.json
new file mode 100644
index 00000000..b2be736f
--- /dev/null
+++ b/adapters/rubicon/rubicontest/exemplary/user-fpd.json
@@ -0,0 +1,344 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    },
+    "site": {
+      "page": "somePage",
+      "ref": "someRef",
+      "search": "someSearch"
+    },
+    "user": {
+      "yob": 2000,
+      "geo": {
+        "country": "USA",
+        "lat": 47.627500,
+        "lon": -122.346200
+      },
+      "gender": "f",
+      "data": [
+        {
+          "ext": {
+            "segtax": 4
+          },
+          "segment": [
+            {
+              "id": "idToCopy"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": "someValue"
+          },
+          "segment": [
+            {
+              "id": "shouldNotBeCopied"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": "4"
+          },
+          "segment": [
+            {
+              "id": "shouldNotBeCopied2"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": 4
+          },
+          "segment": [
+            {
+              "id": "idToCopy2"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "segtax": [
+              4
+            ]
+          },
+          "segment": [
+            {
+              "id": "shouldNotBeCopied3"
+            }
+          ]
+        }
+      ],
+      "keywords": "someKeywords",
+      "ext": {
+        "rp": {
+          "target": {
+            "someKey": "someValue"
+          }
+        },
+        "data": {
+          "dataKey1": "dataValue1",
+          "dataKey2": [
+            "dataValue2",
+            "dataValue3"
+          ],
+          "dataKey3": true
+        }
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "instl": 1,
+        "video": {
+          "placement": 3,
+          "mimes": [
+            "video/mp4"
+          ],
+          "protocols": [
+            2,
+            5
+          ],
+          "w": 1024,
+          "h": 576
+        },
+        "ext": {
+          "data": {
+            "adserver": {
+              "name": "gam",
+              "adslot": "someAdSlot"
+            },
+            "dataAttr1": "dataVal1",
+            "dataAttr2": "dataVal2"
+          },
+          "bidder": {
+            "video": {
+            },
+            "accountId": 1001,
+            "siteId": 113932,
+            "zoneId": 535510
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "uri?tk_xint=pbs-test-tracker",
+        "body": {
+          "id": "test-request-id",
+          "device": {
+            "ext": {
+              "rp": {
+                "pixelratio": 0
+              }
+            },
+            "ip": "123.123.123.123",
+            "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+          },
+          "site": {
+            "page": "somePage",
+            "ref": "someRef",
+            "search": "someSearch",
+            "ext": {
+              "rp": {
+                "site_id": 113932
+              }
+            },
+            "publisher": {
+              "ext": {
+                "rp": {
+                  "account_id": 1001
+                }
+              }
+            }
+          },
+          "user": {
+            "data": [
+              {
+                "ext": {
+                  "segtax": 4
+                },
+                "segment": [
+                  {
+                    "id": "idToCopy"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": "someValue"
+                },
+                "segment": [
+                  {
+                    "id": "shouldNotBeCopied"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": "4"
+                },
+                "segment": [
+                  {
+                    "id": "shouldNotBeCopied2"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": 4
+                },
+                "segment": [
+                  {
+                    "id": "idToCopy2"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "segtax": [
+                    4
+                  ]
+                },
+                "segment": [
+                  {
+                    "id": "shouldNotBeCopied3"
+                  }
+                ]
+              }
+            ],
+            "ext": {
+              "rp": {
+                "target": {
+                  "someKey": "someValue",
+                  "dataKey1": [
+                    "dataValue1"
+                  ],
+                  "dataKey2": [
+                    "dataValue2",
+                    "dataValue3"
+                  ],
+                  "dataKey3": [
+                    "true"
+                  ],
+                  "iab": [
+                    "idToCopy",
+                    "idToCopy2"
+                  ]
+                }
+              }
+            },
+            "keywords": "someKeywords"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "instl": 1,
+              "video": {
+                "placement": 3,
+                "ext": {
+                  "rp": {
+                    "size_id": 203
+                  }
+                },
+                "mimes": [
+                  "video/mp4"
+                ],
+                "protocols": [
+                  2,
+                  5
+                ],
+                "w": 1024,
+                "h": 576
+              },
+              "ext": {
+                "gpid": "someAdSlot",
+                "rp": {
+                  "target": {
+                    "dataAttr1": [
+                      "dataVal1"
+                    ],
+                    "dataAttr2": [
+                      "dataVal2"
+                    ],
+                    "page": [
+                      "somePage"
+                    ],
+                    "ref": [
+                      "someRef"
+                    ],
+                    "search": [
+                      "someSearch"
+                    ]
+                  },
+                  "track": {
+                    "mint": "",
+                    "mint_version": ""
+                  },
+                  "zone_id": 535510
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test_bid_id",
+                  "impid": "test-imp-id",
+                  "price": 0.27543,
+                  "adm": "some-test-ad",
+                  "cid": "test_cid",
+                  "crid": "test_crid",
+                  "dealid": "test_dealid",
+                  "ext": {
+                    "prebid": {
+                      "type": "video"
+                    }
+                  }
+                }
+              ],
+              "seat": "adman"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "some-test-ad",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/rubicon/rubicontest/supplemental/no-site-content-data.json b/adapters/rubicon/rubicontest/supplemental/no-site-content-data.json
index 0be214da..a1e13815 100644
--- a/adapters/rubicon/rubicontest/supplemental/no-site-content-data.json
+++ b/adapters/rubicon/rubicontest/supplemental/no-site-content-data.json
@@ -10,6 +10,10 @@
       "bundle": "com.wls.testwlsapplication"
     },
     "site": {
+      "pagecat": [
+        "val1",
+        "val2"
+      ],
       "content": {
       }
     },
@@ -113,6 +117,10 @@
             "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
           },
           "site": {
+            "pagecat": [
+              "val1",
+              "val2"
+            ],
             "content": {
             },
             "ext": {
@@ -221,6 +229,12 @@
               },
               "ext": {
                 "rp": {
+                  "target": {
+                    "pagecat": [
+                      "val1",
+                      "val2"
+                    ]
+                  },
                   "track": {
                     "mint": "",
                     "mint_version": ""
diff --git a/adapters/rubicon/rubicontest/supplemental/no-site-content.json b/adapters/rubicon/rubicontest/supplemental/no-site-content.json
index 2e830a2d..6630799b 100644
--- a/adapters/rubicon/rubicontest/supplemental/no-site-content.json
+++ b/adapters/rubicon/rubicontest/supplemental/no-site-content.json
@@ -10,6 +10,10 @@
       "bundle": "com.wls.testwlsapplication"
     },
     "site": {
+      "pagecat": [
+        "val1",
+        "val2"
+      ]
     },
     "user": {
       "data": [
@@ -111,6 +115,10 @@
             "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
           },
           "site": {
+            "pagecat": [
+              "val1",
+              "val2"
+            ],
             "ext": {
               "rp": {
                 "site_id": 113932
@@ -217,6 +225,12 @@
               },
               "ext": {
                 "rp": {
+                  "target": {
+                    "pagecat": [
+                      "val1",
+                      "val2"
+                    ]
+                  },
                   "track": {
                     "mint": "",
                     "mint_version": ""
diff --git a/adapters/yssp/params_test.go b/adapters/yahoossp/params_test.go
similarity index 61%
rename from adapters/yssp/params_test.go
rename to adapters/yahoossp/params_test.go
index 66146377..47d8d268 100644
--- a/adapters/yssp/params_test.go
+++ b/adapters/yahoossp/params_test.go
@@ -1,4 +1,4 @@
-package yssp
+package yahoossp
 
 import (
 	"encoding/json"
@@ -7,11 +7,11 @@ import (
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
-// This file actually intends to test static/bidder-params/yssp.json
+// This file actually intends to test static/bidder-params/yahoossp.json
 //
-// These also validate the format of the external API: request.imp[i].ext.yssp
+// These also validate the format of the external API: request.imp[i].ext.yahoossp
 
-// TestValidParams makes sure that the yssp schema accepts all imp.ext fields which we intend to support.
+// TestValidParams makes sure that the yahoossp schema accepts all imp.ext fields which we intend to support.
 func TestValidParams(t *testing.T) {
 	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
 	if err != nil {
@@ -19,13 +19,13 @@ func TestValidParams(t *testing.T) {
 	}
 
 	for _, validParam := range validParams {
-		if err := validator.Validate(openrtb_ext.BidderYSSP, json.RawMessage(validParam)); err != nil {
-			t.Errorf("Schema rejected yssp params: %s", validParam)
+		if err := validator.Validate(openrtb_ext.BidderYahooSSP, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected yahoossp params: %s", validParam)
 		}
 	}
 }
 
-// TestInvalidParams makes sure that the yssp schema rejects all the imp.ext fields we don't support.
+// TestInvalidParams makes sure that the yahoossp 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 {
@@ -33,7 +33,7 @@ func TestInvalidParams(t *testing.T) {
 	}
 
 	for _, invalidParam := range invalidParams {
-		if err := validator.Validate(openrtb_ext.BidderYSSP, json.RawMessage(invalidParam)); err == nil {
+		if err := validator.Validate(openrtb_ext.BidderYahooSSP, json.RawMessage(invalidParam)); err == nil {
 			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
 		}
 	}
diff --git a/adapters/yssp/yssp_test.go b/adapters/yahoossp/yahoossp_test.go
similarity index 56%
rename from adapters/yssp/yssp_test.go
rename to adapters/yahoossp/yahoossp_test.go
index ee99d672..214457c0 100644
--- a/adapters/yssp/yssp_test.go
+++ b/adapters/yahoossp/yahoossp_test.go
@@ -1,4 +1,4 @@
-package yssp
+package yahoossp
 
 import (
 	"testing"
@@ -9,8 +9,8 @@ import (
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
-func TestYSSPBidderEndpointConfig(t *testing.T) {
-	bidder, buildErr := Builder(openrtb_ext.BidderYSSP, config.Adapter{
+func TestYahooSSPBidderEndpointConfig(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderYahooSSP, config.Adapter{
 		Endpoint: "http://localhost/bid",
 	})
 
@@ -18,17 +18,17 @@ func TestYSSPBidderEndpointConfig(t *testing.T) {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
 	}
 
-	bidderYSSP := bidder.(*adapter)
+	bidderYahooSSP := bidder.(*adapter)
 
-	assert.Equal(t, "http://localhost/bid", bidderYSSP.URI)
+	assert.Equal(t, "http://localhost/bid", bidderYahooSSP.URI)
 }
 
 func TestJsonSamples(t *testing.T) {
-	bidder, buildErr := Builder(openrtb_ext.BidderYSSP, config.Adapter{})
+	bidder, buildErr := Builder(openrtb_ext.BidderYahooSSP, config.Adapter{})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
 	}
 
-	adapterstest.RunJSONBidderTest(t, "yssptest", bidder)
+	adapterstest.RunJSONBidderTest(t, "yahoossptest", bidder)
 }
diff --git a/adapters/yssp/yssptest/exemplary/simple-app-banner.json b/adapters/yahoossp/yahoossptest/exemplary/simple-app-banner.json
similarity index 98%
rename from adapters/yssp/yssptest/exemplary/simple-app-banner.json
rename to adapters/yahoossp/yahoossptest/exemplary/simple-app-banner.json
index a28913f8..319e22e3 100644
--- a/adapters/yssp/yssptest/exemplary/simple-app-banner.json
+++ b/adapters/yahoossp/yahoossptest/exemplary/simple-app-banner.json
@@ -77,7 +77,7 @@
           "id": "test-request-id",
           "seatbid": [
             {
-              "seat": "yssp",
+              "seat": "yahoossp",
               "bid": [{
                 "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
                 "impid": "test-imp-id",
diff --git a/adapters/yssp/yssptest/exemplary/simple-banner.json b/adapters/yahoossp/yahoossptest/exemplary/simple-banner.json
similarity index 98%
rename from adapters/yssp/yssptest/exemplary/simple-banner.json
rename to adapters/yahoossp/yahoossptest/exemplary/simple-banner.json
index 4a71473b..344bbe9a 100644
--- a/adapters/yssp/yssptest/exemplary/simple-banner.json
+++ b/adapters/yahoossp/yahoossptest/exemplary/simple-banner.json
@@ -77,7 +77,7 @@
           "id": "test-request-id",
           "seatbid": [
             {
-              "seat": "yssp",
+              "seat": "yahoossp",
               "bid": [{
                 "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
                 "impid": "test-imp-id",
diff --git a/adapters/yssp/yssptest/supplemental/empty-banner-format.json b/adapters/yahoossp/yahoossptest/supplemental/empty-banner-format.json
similarity index 100%
rename from adapters/yssp/yssptest/supplemental/empty-banner-format.json
rename to adapters/yahoossp/yahoossptest/supplemental/empty-banner-format.json
diff --git a/adapters/yssp/yssptest/supplemental/invalid-banner-width.json b/adapters/yahoossp/yahoossptest/supplemental/invalid-banner-width.json
similarity index 100%
rename from adapters/yssp/yssptest/supplemental/invalid-banner-width.json
rename to adapters/yahoossp/yahoossptest/supplemental/invalid-banner-width.json
diff --git a/adapters/yssp/yssptest/supplemental/non-banner-bids-ignored.json b/adapters/yahoossp/yahoossptest/supplemental/non-banner-bids-ignored.json
similarity index 98%
rename from adapters/yssp/yssptest/supplemental/non-banner-bids-ignored.json
rename to adapters/yahoossp/yahoossptest/supplemental/non-banner-bids-ignored.json
index 94772a4b..6f40e2a9 100644
--- a/adapters/yssp/yssptest/supplemental/non-banner-bids-ignored.json
+++ b/adapters/yahoossp/yahoossptest/supplemental/non-banner-bids-ignored.json
@@ -74,7 +74,7 @@
           "id": "test-request-id",
           "seatbid": [
             {
-              "seat": "yssp",
+              "seat": "yahoossp",
               "bid": [{
                 "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
                 "impid": "test-imp-id",
diff --git a/adapters/yssp/yssptest/supplemental/required-nobidder-info.json b/adapters/yahoossp/yahoossptest/supplemental/required-nobidder-info.json
similarity index 100%
rename from adapters/yssp/yssptest/supplemental/required-nobidder-info.json
rename to adapters/yahoossp/yahoossptest/supplemental/required-nobidder-info.json
diff --git a/adapters/yssp/yssptest/supplemental/server-error.json b/adapters/yahoossp/yahoossptest/supplemental/server-error.json
similarity index 100%
rename from adapters/yssp/yssptest/supplemental/server-error.json
rename to adapters/yahoossp/yahoossptest/supplemental/server-error.json
diff --git a/adapters/yssp/yssptest/supplemental/server-response-wrong-impid.json b/adapters/yahoossp/yahoossptest/supplemental/server-response-wrong-impid.json
similarity index 98%
rename from adapters/yssp/yssptest/supplemental/server-response-wrong-impid.json
rename to adapters/yahoossp/yahoossptest/supplemental/server-response-wrong-impid.json
index 1e893127..5b33f3af 100644
--- a/adapters/yssp/yssptest/supplemental/server-response-wrong-impid.json
+++ b/adapters/yahoossp/yahoossptest/supplemental/server-response-wrong-impid.json
@@ -76,7 +76,7 @@
           "id": "test-request-id",
           "seatbid": [
             {
-              "seat": "yssp",
+              "seat": "yahoossp",
               "bid": [{
                 "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
                 "impid": "wrong",
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/multiple_impressions.json b/adapters/yieldlab/yieldlabtest/exemplary/multiple_impressions.json
new file mode 100644
index 00000000..c68748e3
--- /dev/null
+++ b/adapters/yieldlab/yieldlabtest/exemplary/multiple_impressions.json
@@ -0,0 +1,156 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 728,
+              "h": 90
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adslotId": "12345",
+            "supplyId": "123456789",
+            "adSize": "728x90",
+            "targeting": {
+              "key1": "value1",
+              "key2": "value2"
+            },
+            "extId": "abc"
+          }
+        }
+      },
+      {
+        "id": "test-imp-id2",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adslotId": "67890",
+            "supplyId": "123456789",
+            "adSize": "300x250",
+            "targeting": {
+              "key1": "value1",
+              "key2": "value2"
+            },
+            "extId": "abc"
+          }
+        }
+      }
+    ],
+    "user": {
+      "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c"
+    },
+    "device": {
+      "ifa": "hello-ads",
+      "devicetype": 4,
+      "connectiontype": 6,
+      "geo": {
+        "lat": 51.499488,
+        "lon": -0.128953
+      },
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
+      "ip": "169.254.13.37",
+      "h": 1098,
+      "w": 814
+    },
+    "site": {
+      "id": "fake-site-id",
+      "publisher": {
+        "id": "1"
+      },
+      "page": "http://localhost:9090/gdpr.html"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Cookie": [
+            "id=34a53e82-0dc3-4815-8b7e-b725ede0361c"
+          ],
+          "Referer": [
+            "http://localhost:9090/gdpr.html"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "169.254.13.37"
+          ]
+        },
+        "uri": "https://ad.yieldlab.net/testing/12345,67890?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "id": 12345,
+            "price": 201,
+            "advertiser": "yieldlab",
+            "adsize": "728x90",
+            "pid": 1234,
+            "did": 5678,
+            "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5"
+          },
+          {
+            "id": 67890,
+            "price": 131,
+            "advertiser": "yieldlab",
+            "adsize": "300x250",
+            "pid": 1234,
+            "did": 5678,
+            "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5"
+          }
+        ]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "adm": "<script src=\"https://ad.yieldlab.net/d/12345/123456789/728x90?id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing\"></script>",
+            "crid": "12345123433",
+            "dealid": "1234",
+            "id": "12345",
+            "impid": "test-imp-id",
+            "price": 2.01,
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        },
+        {
+          "bid": {
+            "adm": "<script src=\"https://ad.yieldlab.net/d/67890/123456789/300x250?id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing\"></script>",
+            "crid": "67890123433",
+            "dealid": "1234",
+            "id": "67890",
+            "impid": "test-imp-id2",
+            "price": 1.31,
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/yssp/yssptest/params/race/banner.json b/adapters/yssp/yssptest/params/race/banner.json
deleted file mode 100644
index 739ec3c0..00000000
--- a/adapters/yssp/yssptest/params/race/banner.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "dcn": "2345",
-  "pos": "header"
-}
diff --git a/config/config_test.go b/config/config_test.go
index b22b384d..03ad8846 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -228,7 +228,8 @@ var fullConfig = []byte(`
 gdpr:
   host_vendor_id: 15
   default_value: "1"
-  non_standard_publishers: ["siteID","fake-site-id","appID","agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA"]
+  non_standard_publishers: ["pub1", "pub2"]
+  eea_countries: ["eea1", "eea2"]
   tcf2:
     purpose1:
       enforce_vendors: false
@@ -440,19 +441,12 @@ func TestFullConfig(t *testing.T) {
 	cmpStrings(t, "gdpr.default_value", cfg.GDPR.DefaultValue, "1")
 
 	//Assert the NonStandardPublishers was correctly unmarshalled
-	cmpStrings(t, "gdpr.non_standard_publishers", cfg.GDPR.NonStandardPublishers[0], "siteID")
-	cmpStrings(t, "gdpr.non_standard_publishers", cfg.GDPR.NonStandardPublishers[1], "fake-site-id")
-	cmpStrings(t, "gdpr.non_standard_publishers", cfg.GDPR.NonStandardPublishers[2], "appID")
-	cmpStrings(t, "gdpr.non_standard_publishers", cfg.GDPR.NonStandardPublishers[3], "agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA")
-
-	//Assert the NonStandardPublisherMap hash table was built correctly
-	var found bool
-	for i := 0; i < len(cfg.GDPR.NonStandardPublishers); i++ {
-		_, found = cfg.GDPR.NonStandardPublisherMap[cfg.GDPR.NonStandardPublishers[i]]
-		cmpBools(t, "cfg.GDPR.NonStandardPublisherMap", found, true)
-	}
-	_, found = cfg.GDPR.NonStandardPublisherMap["appnexus"]
-	cmpBools(t, "cfg.GDPR.NonStandardPublisherMap", found, false)
+	assert.Equal(t, []string{"pub1", "pub2"}, cfg.GDPR.NonStandardPublishers, "gdpr.non_standard_publishers")
+	assert.Equal(t, map[string]struct{}{"pub1": {}, "pub2": {}}, cfg.GDPR.NonStandardPublisherMap, "gdpr.non_standard_publishers Hash Map")
+
+	// Assert EEA Countries was correctly unmarshalled and the EEACountriesMap built correctly.
+	assert.Equal(t, []string{"eea1", "eea2"}, cfg.GDPR.EEACountries, "gdpr.eea_countries")
+	assert.Equal(t, map[string]struct{}{"eea1": {}, "eea2": {}}, cfg.GDPR.EEACountriesMap, "gdpr.eea_countries Hash Map")
 
 	cmpBools(t, "ccpa.enforce", cfg.CCPA.Enforce, true)
 	cmpBools(t, "lmt.enforce", cfg.LMT.Enforce, true)
diff --git a/endpoints/info/bidders_test.go b/endpoints/info/bidders_test.go
index e48dd3d0..7110e188 100644
--- a/endpoints/info/bidders_test.go
+++ b/endpoints/info/bidders_test.go
@@ -10,7 +10,12 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestPrepareBiddersResponse(t *testing.T) {
+func TestPrepareBiddersResponseAll(t *testing.T) {
+	var (
+		enabled  = config.BidderInfo{Enabled: true}
+		disabled = config.BidderInfo{Enabled: false}
+	)
+
 	testCases := []struct {
 		description  string
 		givenBidders config.BidderInfos
@@ -24,45 +29,133 @@ func TestPrepareBiddersResponse(t *testing.T) {
 			expected:     `[]`,
 		},
 		{
-			description:  "Core Bidders Only - One",
-			givenBidders: config.BidderInfos{"a": {}},
+			description:  "Core Bidders Only - One - Enabled",
+			givenBidders: config.BidderInfos{"a": enabled},
+			givenAliases: nil,
+			expected:     `["a"]`,
+		},
+		{
+			description:  "Core Bidders Only - One - Disabled",
+			givenBidders: config.BidderInfos{"a": disabled},
 			givenAliases: nil,
 			expected:     `["a"]`,
 		},
 		{
 			description:  "Core Bidders Only - Many",
-			givenBidders: config.BidderInfos{"a": {}, "b": {}},
+			givenBidders: config.BidderInfos{"a": enabled, "b": enabled},
 			givenAliases: nil,
 			expected:     `["a","b"]`,
 		},
 		{
-			description:  "Core Bidders Only - Many Sorted",
-			givenBidders: config.BidderInfos{"z": {}, "a": {}},
+			description:  "Core Bidders Only - Many - Mixed",
+			givenBidders: config.BidderInfos{"a": disabled, "b": enabled},
 			givenAliases: nil,
+			expected:     `["a","b"]`,
+		},
+		{
+			description:  "Core Bidders Only - Many - Sorted",
+			givenBidders: config.BidderInfos{"b": enabled, "a": enabled},
+			givenAliases: nil,
+			expected:     `["a","b"]`,
+		},
+		{
+			description:  "With Aliases - One",
+			givenBidders: config.BidderInfos{"a": enabled},
+			givenAliases: map[string]string{"b": "a"},
+			expected:     `["a","b"]`,
+		},
+		{
+			description:  "With Aliases - Many",
+			givenBidders: config.BidderInfos{"a": enabled, "b": disabled},
+			givenAliases: map[string]string{"x": "a", "y": "b"},
+			expected:     `["a","b","x","y"]`,
+		},
+		{
+			description:  "With Aliases - Sorted",
+			givenBidders: config.BidderInfos{"z": enabled},
+			givenAliases: map[string]string{"a": "z"},
 			expected:     `["a","z"]`,
 		},
+	}
+
+	for _, test := range testCases {
+		result, err := prepareBiddersResponseAll(test.givenBidders, test.givenAliases)
+
+		assert.NoError(t, err, test.description)
+		assert.Equal(t, []byte(test.expected), result, test.description)
+	}
+}
+
+func TestPrepareBiddersResponseEnabledOnly(t *testing.T) {
+	var (
+		enabled  = config.BidderInfo{Enabled: true}
+		disabled = config.BidderInfo{Enabled: false}
+	)
+
+	testCases := []struct {
+		description  string
+		givenBidders config.BidderInfos
+		givenAliases map[string]string
+		expected     string
+	}{
+		{
+			description:  "None",
+			givenBidders: config.BidderInfos{},
+			givenAliases: nil,
+			expected:     `[]`,
+		},
+		{
+			description:  "Core Bidders Only - One - Enabled",
+			givenBidders: config.BidderInfos{"a": enabled},
+			givenAliases: nil,
+			expected:     `["a"]`,
+		},
+		{
+			description:  "Core Bidders Only - One - Disabled",
+			givenBidders: config.BidderInfos{"a": disabled},
+			givenAliases: nil,
+			expected:     `[]`,
+		},
+		{
+			description:  "Core Bidders Only - Many",
+			givenBidders: config.BidderInfos{"a": enabled, "b": enabled},
+			givenAliases: nil,
+			expected:     `["a","b"]`,
+		},
+		{
+			description:  "Core Bidders Only - Many - Mixed",
+			givenBidders: config.BidderInfos{"a": disabled, "b": enabled},
+			givenAliases: nil,
+			expected:     `["b"]`,
+		},
+		{
+			description:  "Core Bidders Only - Many - Sorted",
+			givenBidders: config.BidderInfos{"b": enabled, "a": enabled},
+			givenAliases: nil,
+			expected:     `["a","b"]`,
+		},
 		{
 			description:  "With Aliases - One",
-			givenBidders: config.BidderInfos{"a": {}},
-			givenAliases: map[string]string{"b": "b"},
+			givenBidders: config.BidderInfos{"a": enabled},
+			givenAliases: map[string]string{"b": "a"},
 			expected:     `["a","b"]`,
 		},
 		{
 			description:  "With Aliases - Many",
-			givenBidders: config.BidderInfos{"a": {}},
-			givenAliases: map[string]string{"b": "b", "c": "c"},
-			expected:     `["a","b","c"]`,
+			givenBidders: config.BidderInfos{"a": enabled, "b": disabled},
+			givenAliases: map[string]string{"x": "a", "y": "b"},
+			expected:     `["a","x"]`,
 		},
 		{
 			description:  "With Aliases - Sorted",
-			givenBidders: config.BidderInfos{"z": {}},
-			givenAliases: map[string]string{"a": "a"},
+			givenBidders: config.BidderInfos{"z": enabled},
+			givenAliases: map[string]string{"a": "z"},
 			expected:     `["a","z"]`,
 		},
 	}
 
 	for _, test := range testCases {
-		result, err := prepareBiddersResponse(test.givenBidders, test.givenAliases)
+		result, err := prepareBiddersResponseEnabledOnly(test.givenBidders, test.givenAliases)
 
 		assert.NoError(t, err, test.description)
 		assert.Equal(t, []byte(test.expected), result, test.description)
@@ -70,20 +163,87 @@ func TestPrepareBiddersResponse(t *testing.T) {
 }
 
 func TestBiddersHandler(t *testing.T) {
-	bidders := config.BidderInfos{"a": {}}
-	aliases := map[string]string{"b": "b"}
+	var (
+		enabled  = config.BidderInfo{Enabled: true}
+		disabled = config.BidderInfo{Enabled: false}
+	)
+
+	bidders := config.BidderInfos{"a": enabled, "b": disabled}
+	aliases := map[string]string{"x": "a", "y": "b"}
 
-	handler := NewBiddersEndpoint(bidders, aliases)
+	testCases := []struct {
+		description     string
+		givenURL        string
+		expectedStatus  int
+		expectedBody    string
+		expectedHeaders http.Header
+	}{
+		{
+			description:     "No Query Paramters - Backwards Compatability",
+			givenURL:        "/info/bidders",
+			expectedStatus:  http.StatusOK,
+			expectedBody:    `["a","b","x","y"]`,
+			expectedHeaders: http.Header{"Content-Type": []string{"application/json"}},
+		},
+		{
+			description:     "Enabled Only - False",
+			givenURL:        "/info/bidders?enabledonly=false",
+			expectedStatus:  http.StatusOK,
+			expectedBody:    `["a","b","x","y"]`,
+			expectedHeaders: http.Header{"Content-Type": []string{"application/json"}},
+		},
+		{
+			description:     "Enabled Only - False - Case Insensitive",
+			givenURL:        "/info/bidders?enabledonly=fAlSe",
+			expectedStatus:  http.StatusOK,
+			expectedBody:    `["a","b","x","y"]`,
+			expectedHeaders: http.Header{"Content-Type": []string{"application/json"}},
+		},
+		{
+			description:     "Enabled Only - True",
+			givenURL:        "/info/bidders?enabledonly=true",
+			expectedStatus:  http.StatusOK,
+			expectedBody:    `["a","x"]`,
+			expectedHeaders: http.Header{"Content-Type": []string{"application/json"}},
+		},
+		{
+			description:     "Enabled Only - True - Case Insensitive",
+			givenURL:        "/info/bidders?enabledonly=TrUe",
+			expectedStatus:  http.StatusOK,
+			expectedBody:    `["a","x"]`,
+			expectedHeaders: http.Header{"Content-Type": []string{"application/json"}},
+		},
+		{
+			description:     "Enabled Only - Invalid",
+			givenURL:        "/info/bidders?enabledonly=foo",
+			expectedStatus:  http.StatusBadRequest,
+			expectedBody:    `Invalid value for 'enabledonly' query param, must be of boolean type`,
+			expectedHeaders: http.Header{},
+		},
+		{
+			description:     "Enabled Only - Missing Value",
+			givenURL:        "/info/bidders?enabledonly=",
+			expectedStatus:  http.StatusBadRequest,
+			expectedBody:    `Invalid value for 'enabledonly' query param, must be of boolean type`,
+			expectedHeaders: http.Header{},
+		},
+	}
+
+	for _, test := range testCases {
+		handler := NewBiddersEndpoint(bidders, aliases)
 
-	responseRecorder := httptest.NewRecorder()
-	handler(responseRecorder, nil, nil)
+		request := httptest.NewRequest("GET", test.givenURL, nil)
 
-	result := responseRecorder.Result()
-	assert.Equal(t, result.StatusCode, http.StatusOK)
+		responseRecorder := httptest.NewRecorder()
+		handler(responseRecorder, request, nil)
 
-	resultBody, _ := ioutil.ReadAll(result.Body)
-	assert.Equal(t, []byte(`["a","b"]`), resultBody)
+		result := responseRecorder.Result()
+		assert.Equal(t, result.StatusCode, test.expectedStatus)
 
-	resultHeaders := result.Header
-	assert.Equal(t, http.Header{"Content-Type": []string{"application/json"}}, resultHeaders)
+		resultBody, _ := ioutil.ReadAll(result.Body)
+		assert.Equal(t, []byte(test.expectedBody), resultBody)
+
+		resultHeaders := result.Header
+		assert.Equal(t, test.expectedHeaders, resultHeaders)
+	}
 }
diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go
index 667c4aef..ae3c036a 100644
--- a/endpoints/openrtb2/amp_auction_test.go
+++ b/endpoints/openrtb2/amp_auction_test.go
@@ -11,6 +11,8 @@ import (
 	"strconv"
 	"testing"
 
+	"github.com/julienschmidt/httprouter"
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
 	"github.com/prebid/prebid-server/analytics"
 	analyticsConf "github.com/prebid/prebid-server/analytics/config"
 	"github.com/prebid/prebid-server/config"
@@ -19,7 +21,6 @@ import (
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
 
-	"github.com/mxmCherry/openrtb/v15/openrtb2"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -38,6 +39,7 @@ func TestGoodAmpRequests(t *testing.T) {
 	}
 
 	endpoint, _ := NewAmpEndpoint(
+		fakeUUIDGenerator{},
 		&mockAmpExchange{},
 		newParamsValidator(t),
 		&mockAmpStoredReqFetcher{goodRequests},
@@ -91,6 +93,7 @@ func TestAMPPageInfo(t *testing.T) {
 	exchange := &mockAmpExchange{}
 
 	endpoint, _ := NewAmpEndpoint(
+		fakeUUIDGenerator{},
 		exchange,
 		newParamsValidator(t),
 		&mockAmpStoredReqFetcher{stored},
@@ -188,6 +191,7 @@ func TestGDPRConsent(t *testing.T) {
 		// Build Exchange Endpoint
 		mockExchange := &mockAmpExchange{}
 		endpoint, _ := NewAmpEndpoint(
+			fakeUUIDGenerator{},
 			mockExchange,
 			newParamsValidator(t),
 			&mockAmpStoredReqFetcher{stored},
@@ -340,6 +344,7 @@ func TestCCPAConsent(t *testing.T) {
 		// Build Exchange Endpoint
 		mockExchange := &mockAmpExchange{}
 		endpoint, _ := NewAmpEndpoint(
+			fakeUUIDGenerator{},
 			mockExchange,
 			newParamsValidator(t),
 			&mockAmpStoredReqFetcher{stored},
@@ -450,6 +455,7 @@ func TestConsentWarnings(t *testing.T) {
 			mockExchange = &mockAmpExchange{}
 		}
 		endpoint, _ := NewAmpEndpoint(
+			fakeUUIDGenerator{},
 			mockExchange,
 			newParamsValidator(t),
 			&mockAmpStoredReqFetcher{stored},
@@ -542,6 +548,7 @@ func TestNewAndLegacyConsentBothProvided(t *testing.T) {
 		// Build Exchange Endpoint
 		mockExchange := &mockAmpExchange{}
 		endpoint, _ := NewAmpEndpoint(
+			fakeUUIDGenerator{},
 			mockExchange,
 			newParamsValidator(t),
 			&mockAmpStoredReqFetcher{stored},
@@ -593,6 +600,7 @@ func TestAMPSiteExt(t *testing.T) {
 	}
 	exchange := &mockAmpExchange{}
 	endpoint, _ := NewAmpEndpoint(
+		fakeUUIDGenerator{},
 		exchange,
 		newParamsValidator(t),
 		&mockAmpStoredReqFetcher{stored},
@@ -629,6 +637,7 @@ func TestAmpBadRequests(t *testing.T) {
 	}
 
 	endpoint, _ := NewAmpEndpoint(
+		fakeUUIDGenerator{},
 		&mockAmpExchange{},
 		newParamsValidator(t),
 		&mockAmpStoredReqFetcher{badRequests},
@@ -659,6 +668,7 @@ func TestAmpDebug(t *testing.T) {
 	}
 
 	endpoint, _ := NewAmpEndpoint(
+		fakeUUIDGenerator{},
 		&mockAmpExchange{},
 		newParamsValidator(t),
 		&mockAmpStoredReqFetcher{requests},
@@ -731,6 +741,7 @@ func TestQueryParamOverrides(t *testing.T) {
 	}
 
 	endpoint, _ := NewAmpEndpoint(
+		fakeUUIDGenerator{},
 		&mockAmpExchange{},
 		newParamsValidator(t),
 		&mockAmpStoredReqFetcher{requests},
@@ -883,6 +894,7 @@ func (s formatOverrideSpec) execute(t *testing.T) {
 	}
 
 	endpoint, _ := NewAmpEndpoint(
+		fakeUUIDGenerator{},
 		&mockAmpExchange{},
 		newParamsValidator(t),
 		&mockAmpStoredReqFetcher{requests},
@@ -1249,31 +1261,8 @@ func TestBuildAmpObject(t *testing.T) {
 	recorder := httptest.NewRecorder()
 
 	for _, test := range testCases {
-
 		// Set up test, declare a new mock logger every time
-		actualAmpObject := new(analytics.AmpObject)
-
-		logger := newMockLogger(actualAmpObject)
-
-		mockAmpFetcher := &mockAmpStoredReqFetcher{
-			data: map[string]json.RawMessage{
-				test.inTagId: json.RawMessage(test.inStoredRequest),
-			},
-		}
-
-		endpoint, _ := NewAmpEndpoint(
-			&mockAmpExchange{},
-			newParamsValidator(t),
-			mockAmpFetcher,
-			empty_fetcher.EmptyFetcher{},
-			&config.Configuration{MaxRequestSize: maxSize},
-			&metricsConfig.DummyMetricsEngine{},
-			logger,
-			map[string]string{},
-			[]byte{},
-			openrtb_ext.BuildBidderMap(),
-		)
-
+		actualAmpObject, endpoint := AmpObjectTestSetup(t, test.inTagId, test.inStoredRequest, false)
 		// Run test
 		endpoint(recorder, request, nil)
 
@@ -1286,3 +1275,76 @@ func TestBuildAmpObject(t *testing.T) {
 		assert.Equalf(t, test.expectedAmpObject.Origin, actualAmpObject.Origin, "Amp Object Origin field doesn't match expected: %s\n", test.description)
 	}
 }
+
+func TestIdGeneration(t *testing.T) {
+	uuid := "foo"
+
+	testCases := []struct {
+		description            string
+		givenInStoredRequest   json.RawMessage
+		givenGenerateRequestID bool
+		expectedID             string
+	}{
+		{
+			description:            "The givenGenerateRequestID flag is set to true, so even though the stored amp request already has an id, we should still generate a new uuid",
+			givenInStoredRequest:   json.RawMessage(`{"id":"ThisID","site":{"page":"prebid.org"},"imp":[{"id":"some-imp-id","banner":{"format":[{"w":300,"h":250}]},"ext":{"appnexus":{"placementId":1}}}],"tmax":1}`),
+			givenGenerateRequestID: true,
+			expectedID:             uuid,
+		},
+		{
+			description:            "The givenGenerateRequestID flag is set to true and the stored amp request ID is blank, so we should generate a new uuid for the request",
+			givenInStoredRequest:   json.RawMessage(`{"id":"","site":{"page":"prebid.org"},"imp":[{"id":"some-imp-id","banner":{"format":[{"w":300,"h":250}]},"ext":{"appnexus":{"placementId":1}}}],"tmax":1}`),
+			givenGenerateRequestID: true,
+			expectedID:             uuid,
+		},
+		{
+			description:            "The givenGenerateRequestID flag is false, so the ID shouldn't change",
+			givenInStoredRequest:   json.RawMessage(`{"id":"ThisID","site":{"page":"prebid.org"},"imp":[{"id":"some-imp-id","banner":{"format":[{"w":300,"h":250}]},"ext":{"appnexus":{"placementId":1}}}],"tmax":1}`),
+			givenGenerateRequestID: false,
+			expectedID:             "ThisID",
+		},
+		{
+			description:            "The givenGenerateRequestID flag is true, and if the id field isn't included in the stored request",
+			givenInStoredRequest:   json.RawMessage(`{"site":{"page":"prebid.org"},"imp":[{"id":"some-imp-id","banner":{"format":[{"w":300,"h":250}]},"ext":{"appnexus":{"placementId":1}}}],"tmax":1}`),
+			givenGenerateRequestID: true,
+			expectedID:             uuid,
+		},
+	}
+
+	request := httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=test", nil)
+	recorder := httptest.NewRecorder()
+
+	for _, test := range testCases {
+		// Set up and run test
+		actualAmpObject, endpoint := AmpObjectTestSetup(t, "test", test.givenInStoredRequest, test.givenGenerateRequestID)
+		endpoint(recorder, request, nil)
+
+		assert.Equalf(t, test.expectedID, actualAmpObject.Request.ID, "Bid Request ID is incorrect: %s\n", test.description)
+	}
+}
+
+func AmpObjectTestSetup(t *testing.T, inTagId string, inStoredRequest json.RawMessage, generateRequestID bool) (*analytics.AmpObject, httprouter.Handle) {
+	actualAmpObject := analytics.AmpObject{}
+	logger := newMockLogger(&actualAmpObject)
+
+	mockAmpFetcher := &mockAmpStoredReqFetcher{
+		data: map[string]json.RawMessage{
+			inTagId: json.RawMessage(inStoredRequest),
+		},
+	}
+
+	endpoint, _ := NewAmpEndpoint(
+		fakeUUIDGenerator{id: "foo", err: nil},
+		&mockAmpExchange{},
+		newParamsValidator(t),
+		mockAmpFetcher,
+		empty_fetcher.EmptyFetcher{},
+		&config.Configuration{MaxRequestSize: maxSize, GenerateRequestID: generateRequestID},
+		&metricsConfig.DummyMetricsEngine{},
+		logger,
+		map[string]string{},
+		[]byte{},
+		openrtb_ext.BuildBidderMap(),
+	)
+	return &actualAmpObject, endpoint
+}
diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go
index ecc0554f..f2e0fb2a 100644
--- a/endpoints/openrtb2/auction_benchmark_test.go
+++ b/endpoints/openrtb2/auction_benchmark_test.go
@@ -87,6 +87,7 @@ func BenchmarkOpenrtbEndpoint(b *testing.B) {
 	)
 
 	endpoint, _ := NewEndpoint(
+		fakeUUIDGenerator{},
 		exchange,
 		paramValidator,
 		empty_fetcher.EmptyFetcher{},
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index 31c3c109..ebf18994 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -430,6 +430,7 @@ func TestExplicitUserId(t *testing.T) {
 	})
 
 	endpoint, _ := NewEndpoint(
+		fakeUUIDGenerator{},
 		ex,
 		newParamsValidator(t),
 		empty_fetcher.EmptyFetcher{},
@@ -464,6 +465,7 @@ func doRequest(t *testing.T, test testCase) (int, string) {
 	mockExchange := newMockBidExchange(test.Config.MockBidder, test.Config.CurrencyRates)
 
 	endpoint, _ := NewEndpoint(
+		fakeUUIDGenerator{},
 		mockExchange,
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -534,6 +536,7 @@ func doBadAliasRequest(t *testing.T, filename string, expectMsg string) {
 	// NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it.
 	// As a side effect this gives us some coverage of the go_metrics piece of the metrics engine.
 	endpoint, _ := NewEndpoint(
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -584,6 +587,7 @@ func TestNilExchange(t *testing.T) {
 	// NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it.
 	// As a side effect this gives us some coverage of the go_metrics piece of the metrics engine.
 	_, err := NewEndpoint(
+		fakeUUIDGenerator{},
 		nil,
 		newParamsValidator(t),
 		empty_fetcher.EmptyFetcher{},
@@ -604,6 +608,7 @@ func TestNilValidator(t *testing.T) {
 	// NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it.
 	// As a side effect this gives us some coverage of the go_metrics piece of the metrics engine.
 	_, err := NewEndpoint(
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		nil,
 		empty_fetcher.EmptyFetcher{},
@@ -625,6 +630,7 @@ func TestExchangeError(t *testing.T) {
 	// NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it.
 	// As a side effect this gives us some coverage of the go_metrics piece of the metrics engine.
 	endpoint, _ := NewEndpoint(
+		fakeUUIDGenerator{},
 		&brokenExchange{},
 		newParamsValidator(t),
 		empty_fetcher.EmptyFetcher{},
@@ -747,6 +753,7 @@ func TestImplicitIPsEndToEnd(t *testing.T) {
 			},
 		}
 		endpoint, _ := NewEndpoint(
+			fakeUUIDGenerator{},
 			exchange,
 			newParamsValidator(t),
 			&mockStoredReqFetcher{},
@@ -941,6 +948,7 @@ func TestImplicitDNTEndToEnd(t *testing.T) {
 	for _, test := range testCases {
 		exchange := &nobidExchange{}
 		endpoint, _ := NewEndpoint(
+			fakeUUIDGenerator{},
 			exchange,
 			newParamsValidator(t),
 			&mockStoredReqFetcher{},
@@ -1094,6 +1102,7 @@ func TestParseImpInfoMultipleImpressions(t *testing.T) {
 // Test the stored request functionality
 func TestStoredRequests(t *testing.T) {
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -1140,10 +1149,81 @@ func TestStoredRequests(t *testing.T) {
 	}
 }
 
+func TestStoredRequestGenerateUuid(t *testing.T) {
+	uuid := "foo"
+
+	deps := &endpointDeps{
+		fakeUUIDGenerator{id: "foo", err: nil},
+		&nobidExchange{},
+		newParamsValidator(t),
+		&mockStoredReqFetcher{},
+		empty_fetcher.EmptyFetcher{},
+		empty_fetcher.EmptyFetcher{},
+		&config.Configuration{MaxRequestSize: maxSize},
+		&metricsConfig.DummyMetricsEngine{},
+		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		map[string]string{},
+		false,
+		[]byte{},
+		openrtb_ext.BuildBidderMap(),
+		nil,
+		nil,
+		hardcodedResponseIPValidator{response: true},
+	}
+
+	req := &openrtb2.BidRequest{}
+
+	testCases := []struct {
+		description            string
+		givenRawData           string
+		givenGenerateRequestID bool
+		expectedID             string
+	}{
+		{
+			description:            "GenerateRequestID is true, rawData is an app request, and stored bid we should generate uuid",
+			givenRawData:           testStoredRequestsUuid[2],
+			givenGenerateRequestID: true,
+			expectedID:             uuid,
+		},
+		{
+			description:            "GenerateRequestID is true, rawData is an app request, but no stored bid, we should not generate uuid",
+			givenRawData:           testStoredRequestsUuid[0],
+			givenGenerateRequestID: true,
+			expectedID:             "ThisID",
+		},
+		{
+			description:            "GenerateRequestID is false so we should not generate uuid",
+			givenRawData:           testStoredRequestsUuid[0],
+			givenGenerateRequestID: false,
+			expectedID:             "ThisID",
+		},
+		{
+			description:            "GenerateRequestID is true, but rawData is a site request, we should not generate uuid",
+			givenRawData:           testStoredRequestsUuid[1],
+			givenGenerateRequestID: true,
+			expectedID:             "ThisID",
+		},
+	}
+
+	for _, test := range testCases {
+		deps.cfg.GenerateRequestID = test.givenGenerateRequestID
+		impInfo, errs := parseImpInfo([]byte(test.givenRawData))
+		assert.Empty(t, errs, test.description)
+		newRequest, _, errList := deps.processStoredRequests(context.Background(), json.RawMessage(test.givenRawData), impInfo)
+		assert.Empty(t, errList, test.description)
+
+		if err := json.Unmarshal(newRequest, req); err != nil {
+			t.Errorf("processStoredRequests Error: %s", err.Error())
+		}
+		assert.Equalf(t, test.expectedID, req.ID, "The Bid Request ID is incorrect: %s\n", test.description)
+	}
+}
+
 // TestOversizedRequest makes sure we behave properly when the request size exceeds the configured max.
 func TestOversizedRequest(t *testing.T) {
 	reqBody := validRequest(t, "site.json")
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -1179,6 +1259,7 @@ func TestOversizedRequest(t *testing.T) {
 func TestRequestSizeEdgeCase(t *testing.T) {
 	reqBody := validRequest(t, "site.json")
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -1213,6 +1294,7 @@ func TestRequestSizeEdgeCase(t *testing.T) {
 // TestNoEncoding prevents #231.
 func TestNoEncoding(t *testing.T) {
 	endpoint, _ := NewEndpoint(
+		fakeUUIDGenerator{},
 		&mockExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -1288,6 +1370,7 @@ func TestExplicitAMP(t *testing.T) {
 // TestContentType prevents #328
 func TestContentType(t *testing.T) {
 	endpoint, _ := NewEndpoint(
+		fakeUUIDGenerator{},
 		&mockExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -1607,6 +1690,7 @@ func TestValidateImpExt(t *testing.T) {
 	}
 
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -1653,6 +1737,7 @@ func validRequest(t *testing.T, filename string) string {
 
 func TestCurrencyTrunc(t *testing.T) {
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -1697,6 +1782,7 @@ func TestCurrencyTrunc(t *testing.T) {
 
 func TestCCPAInvalid(t *testing.T) {
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -1745,6 +1831,7 @@ func TestCCPAInvalid(t *testing.T) {
 
 func TestNoSaleInvalid(t *testing.T) {
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -1796,6 +1883,7 @@ func TestValidateSourceTID(t *testing.T) {
 	}
 
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -1837,6 +1925,7 @@ func TestValidateSourceTID(t *testing.T) {
 
 func TestSChainInvalid(t *testing.T) {
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -2056,6 +2145,7 @@ func TestValidateAndFillSourceTID(t *testing.T) {
 
 func TestEidPermissionsInvalid(t *testing.T) {
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&nobidExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -2307,6 +2397,7 @@ func TestIOS14EndToEnd(t *testing.T) {
 	exchange := &nobidExchange{}
 
 	endpoint, _ := NewEndpoint(
+		fakeUUIDGenerator{},
 		exchange,
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -2334,6 +2425,7 @@ func TestIOS14EndToEnd(t *testing.T) {
 func TestAuctionWarnings(t *testing.T) {
 	reqBody := validRequest(t, "us-privacy-invalid.json")
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&warningsCheckExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -2373,6 +2465,7 @@ func TestAuctionWarnings(t *testing.T) {
 func TestParseRequestParseImpInfoError(t *testing.T) {
 	reqBody := validRequest(t, "imp-info-invalid.json")
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		&warningsCheckExchange{},
 		newParamsValidator(t),
 		&mockStoredReqFetcher{},
@@ -3452,6 +3545,105 @@ var testStoredImps = []string{
 	``,
 }
 
+var testStoredRequestsUuid = []string{
+	`{
+		"id": "ThisID",
+		"app": {
+			"id": "123"
+		},
+		"imp": [
+			{
+				"video":{
+					"h":300,
+					"w":200
+				},
+				"ext": {
+					"prebid": {
+						"storedrequest": {
+							"id": "1"
+						},
+						"options": {
+							"echovideoattrs": true
+						}
+					}
+				}
+			}
+		],
+		"ext": {
+			"prebid": {
+				"cache": {
+					"markup": 1
+				},
+				"targeting": {
+				}
+			}
+		}
+	}`,
+	`{
+		"id": "ThisID",
+		"site": {
+			"page": "prebid.org"
+		},
+		"imp": [
+			{
+				"id": "adUnit2",
+				"ext": {
+					"prebid": {
+						"storedrequest": {
+							"id": "1"
+						},
+						"options": {
+							"echovideoattrs": true
+						}
+					},
+					"appnexus": {
+						"placementId": "def",
+						"trafficSourceCode": "mysite.com",
+						"reserve": null
+					},
+					"rubicon": null
+				}
+			}
+		],
+		"ext": {
+			"prebid": {
+				"cache": {
+					"markup": 1
+				},
+				"targeting": {
+				}
+			}
+		}
+	}`,
+	`{
+		"id": "ThisID",
+		"app": {
+			"id": "123"
+		},
+		"imp": [
+			{
+				"ext": {
+					"prebid": {
+						"storedrequest": {
+							"id": "2"
+						},
+						"options": {
+							"echovideoattrs": false
+						}
+					}
+				}
+			}
+		],
+		"ext": {
+			"prebid": {
+				"storedrequest": {
+					"id": "2"
+				}
+			}
+		}
+	}`,
+}
+
 type mockStoredReqFetcher struct {
 }
 
@@ -3514,3 +3706,12 @@ type hardcodedResponseIPValidator struct {
 func (v hardcodedResponseIPValidator) IsValid(net.IP, iputil.IPVersion) bool {
 	return v.response
 }
+
+type fakeUUIDGenerator struct {
+	id  string
+	err error
+}
+
+func (f fakeUUIDGenerator) Generate() (string, error) {
+	return f.id, f.err
+}
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index b00467d9..26d9f028 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -1210,6 +1210,7 @@ func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *m
 	metrics := metrics.NewMetrics(gometrics.NewRegistry(), openrtb_ext.CoreBidderNames(), config.DisabledMetrics{}, nil)
 
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		ex,
 		newParamsValidator(t),
 		&mockVideoStoredReqFetcher{},
@@ -1252,6 +1253,7 @@ func (m *mockAnalyticsModule) LogNotificationEventObject(ne *analytics.Notificat
 
 func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		ex,
 		newParamsValidator(t),
 		&mockVideoStoredReqFetcher{},
@@ -1274,6 +1276,7 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 
 func mockDepsAppendBidderNames(t *testing.T, ex *mockExchangeAppendBidderNames) *endpointDeps {
 	deps := &endpointDeps{
+		fakeUUIDGenerator{},
 		ex,
 		newParamsValidator(t),
 		&mockVideoStoredReqFetcher{},
@@ -1296,6 +1299,7 @@ func mockDepsAppendBidderNames(t *testing.T, ex *mockExchangeAppendBidderNames)
 
 func mockDepsNoBids(t *testing.T, ex *mockExchangeVideoNoBids) *endpointDeps {
 	edep := &endpointDeps{
+		fakeUUIDGenerator{},
 		ex,
 		newParamsValidator(t),
 		&mockVideoStoredReqFetcher{},
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index 1591d353..1a630a96 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -69,7 +69,6 @@ func TestSingleBidder(t *testing.T) {
 	}
 
 	ctx := context.Background()
-	ctx = context.WithValue(ctx, DebugContextKey, true)
 
 	for _, test := range testCases {
 		mockBidderResponse := &adapters.BidderResponse{
@@ -170,7 +169,6 @@ func TestRequestBidRemovesSensitiveHeaders(t *testing.T) {
 
 	debugInfo := &config.DebugInfo{Allow: true}
 	ctx := context.Background()
-	ctx = context.WithValue(ctx, DebugContextKey, true)
 
 	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, debugInfo)
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
@@ -211,7 +209,6 @@ func TestSetGPCHeader(t *testing.T) {
 
 	debugInfo := &config.DebugInfo{Allow: true}
 	ctx := context.Background()
-	ctx = context.WithValue(ctx, DebugContextKey, true)
 
 	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, debugInfo)
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
@@ -249,7 +246,6 @@ func TestSetGPCHeaderNil(t *testing.T) {
 
 	debugInfo := &config.DebugInfo{Allow: true}
 	ctx := context.Background()
-	ctx = context.WithValue(ctx, DebugContextKey, true)
 
 	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, debugInfo)
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 706d118d..6258d121 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -322,9 +322,6 @@ func TestDebugBehaviour(t *testing.T) {
 			openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &config.DebugInfo{Allow: test.debugData.bidderLevelDebugAllowed}),
 		}
 
-		//request level debug key
-		ctx = context.WithValue(ctx, DebugContextKey, test.in.debug)
-
 		bidRequest.Test = test.in.test
 
 		if test.in.debug {
@@ -2025,38 +2022,6 @@ func TestTimeoutComputation(t *testing.T) {
 	}
 }
 
-func TestSetDebugContextKey(t *testing.T) {
-	// Test cases
-	testCases := []struct {
-		desc              string
-		inDebugInfo       bool
-		expectedDebugInfo bool
-	}{
-		{
-			desc:              "debugInfo flag on, we expect to find DebugContextKey key in context",
-			inDebugInfo:       true,
-			expectedDebugInfo: true,
-		},
-		{
-			desc:              "debugInfo flag off, we don't expect to find DebugContextKey key in context",
-			inDebugInfo:       false,
-			expectedDebugInfo: false,
-		},
-	}
-
-	// Setup test
-	ex := exchange{}
-
-	// Run tests
-	for _, test := range testCases {
-		auctionCtx := ex.makeDebugContext(context.Background(), test.inDebugInfo)
-
-		debugInfo := auctionCtx.Value(DebugContextKey)
-		assert.NotNil(t, debugInfo, "%s. Flag set, `debugInfo` shouldn't be nil")
-		assert.Equal(t, test.expectedDebugInfo, debugInfo.(bool), "Desc: %s. Incorrect value mapped to DebugContextKey(`debugInfo`) in the context\n", test.desc)
-	}
-}
-
 // TestExchangeJSON executes tests for all the *.json files in exchangetest.
 func TestExchangeJSON(t *testing.T) {
 	if specFiles, err := ioutil.ReadDir("./exchangetest"); err == nil {
@@ -2145,7 +2110,6 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 		auctionRequest.StartTime = time.Unix(0, spec.StartTime*1e+6)
 	}
 	ctx := context.Background()
-	ctx = context.WithValue(ctx, DebugContextKey, true)
 
 	bid, err := ex.HoldAuction(ctx, auctionRequest, debugLog)
 	responseTimes := extractResponseTimes(t, filename, bid)
@@ -3491,11 +3455,19 @@ func TestMakeBidExtJSON(t *testing.T) {
 
 	testCases := []aTest{
 		{
-			description:        "Valid extension, non empty extBidPrebid and valid imp ext info",
+			description:        "Valid extension, non empty extBidPrebid, valid imp ext info, meta from adapter",
 			ext:                json.RawMessage(`{"video":{"h":100}}`),
+			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video"), Meta: &openrtb_ext.ExtBidPrebidMeta{BrandName: "foo"}},
+			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`)}},
+			expectedBidExt:     `{"prebid":{"meta": {"brandName": "foo"}, "type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}}`,
+			expectedErrMessage: "",
+		},
+		{
+			description:        "Valid extension, non empty extBidPrebid, valid imp ext info, meta from response",
+			ext:                json.RawMessage(`{"video":{"h":100},"prebid":{"meta": {"brandName": "foo"}}}`),
 			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video")},
 			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`)}},
-			expectedBidExt:     `{"prebid":{"type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}}`,
+			expectedBidExt:     `{"prebid":{"meta": {"brandName": "foo"}, "type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}}`,
 			expectedErrMessage: "",
 		},
 		{
@@ -3554,6 +3526,54 @@ func TestMakeBidExtJSON(t *testing.T) {
 			expectedBidExt:     `{"prebid":{"type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]}}`,
 			expectedErrMessage: "",
 		},
+		{
+			description:        "Meta - Defined By Bid - Nil Extension",
+			ext:                nil,
+			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("banner"), Meta: &openrtb_ext.ExtBidPrebidMeta{BrandName: "foo"}},
+			impExtInfo:         map[string]ImpExtInfo{},
+			expectedBidExt:     `{"prebid":{"meta":{"brandName":"foo"},"type":"banner"}}`,
+			expectedErrMessage: "",
+		},
+		{
+			description:        "Meta - Defined By Bid - Empty Extension",
+			ext:                json.RawMessage(`{}`),
+			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("banner"), Meta: &openrtb_ext.ExtBidPrebidMeta{BrandName: "foo"}},
+			impExtInfo:         nil,
+			expectedBidExt:     `{"prebid":{"meta":{"brandName":"foo"},"type":"banner"}}`,
+			expectedErrMessage: "",
+		},
+		{
+			description:        "Meta - Defined By Bid - Existing Extension Overwritten",
+			ext:                json.RawMessage(`{"prebid":{"meta":{"brandName":"notfoo", "brandId": 42}}}`),
+			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("banner"), Meta: &openrtb_ext.ExtBidPrebidMeta{BrandName: "foo"}},
+			impExtInfo:         nil,
+			expectedBidExt:     `{"prebid":{"meta":{"brandName":"foo"},"type":"banner"}}`,
+			expectedErrMessage: "",
+		},
+		{
+			description:        "Meta - Not Defined By Bid - Persists From Bid Ext",
+			ext:                json.RawMessage(`{"prebid":{"meta":{"brandName":"foo"}}}`),
+			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("banner")},
+			impExtInfo:         nil,
+			expectedBidExt:     `{"prebid":{"meta":{"brandName":"foo"},"type":"banner"}}`,
+			expectedErrMessage: "",
+		},
+		{
+			description:        "Meta - Not Defined By Bid - Persists From Bid Ext - Invalid Fields Ignored",
+			ext:                json.RawMessage(`{"prebid":{"meta":{"brandName":"foo","unknown":"value"}}}`),
+			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("banner")},
+			impExtInfo:         nil,
+			expectedBidExt:     `{"prebid":{"meta":{"brandName":"foo"},"type":"banner"}}`,
+			expectedErrMessage: "",
+		},
+		{
+			description:        "Meta - Not Defined",
+			ext:                nil,
+			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("banner")},
+			impExtInfo:         nil,
+			expectedBidExt:     `{"prebid":{"type":"banner"}}`,
+			expectedErrMessage: "",
+		},
 		//Error cases
 		{
 			description:        "Invalid extension, valid extBidPrebid and valid imp ext info",
@@ -3571,6 +3591,14 @@ func TestMakeBidExtJSON(t *testing.T) {
 			expectedBidExt:     ``,
 			expectedErrMessage: "invalid character",
 		},
+		{
+			description:        "Meta - Invalid",
+			ext:                json.RawMessage(`{"prebid":{"meta":{"brandId":"foo"}}}`), // brandId should be an int, but is a string in this test case
+			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("banner")},
+			impExtInfo:         nil,
+			expectedErrMessage: "error validaing response from server, json: cannot unmarshal string into Go struct field ExtBidPrebidMeta.prebid.meta.brandId of type int",
+		},
+		// add invalid
 	}
 
 	for _, test := range testCases {
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 6ce4b28a..14ebb8e9 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -1289,7 +1289,7 @@ func TestGetExtTargetData(t *testing.T) {
 	}
 }
 
-func TestGetDebugInfo(t *testing.T) {
+func TestParseRequestDebugValues(t *testing.T) {
 	type inTest struct {
 		bidRequest *openrtb2.BidRequest
 		requestExt *openrtb_ext.ExtRequest
@@ -1336,12 +1336,84 @@ func TestGetDebugInfo(t *testing.T) {
 		},
 	}
 	for _, test := range testCases {
-		actualDebugInfo := getDebugInfo(test.in.bidRequest, test.in.requestExt)
+		actualDebugInfo := parseRequestDebugValues(test.in.bidRequest, test.in.requestExt)
 
 		assert.Equal(t, test.out, actualDebugInfo, "%s. Unexpected debug value. \n", test.desc)
 	}
 }
 
+func TestSetDebugLogValues(t *testing.T) {
+
+	type aTest struct {
+		desc               string
+		inAccountDebugFlag bool
+		inDebugLog         *DebugLog
+		expectedDebugLog   *DebugLog
+	}
+
+	testGroups := []struct {
+		desc      string
+		testCases []aTest
+	}{
+
+		{
+			"nil debug log",
+			[]aTest{
+				{
+					desc:               "accountDebugFlag false, expect all false flags in resulting debugLog",
+					inAccountDebugFlag: false,
+					inDebugLog:         nil,
+					expectedDebugLog:   &DebugLog{},
+				},
+				{
+					desc:               "accountDebugFlag true, expect debugLog.Enabled to be true",
+					inAccountDebugFlag: true,
+					inDebugLog:         nil,
+					expectedDebugLog:   &DebugLog{Enabled: true},
+				},
+			},
+		},
+		{
+			"non-nil debug log",
+			[]aTest{
+				{
+					desc:               "both accountDebugFlag and DebugEnabledOrOverridden are false, expect debugLog.Enabled to be false",
+					inAccountDebugFlag: false,
+					inDebugLog:         &DebugLog{},
+					expectedDebugLog:   &DebugLog{},
+				},
+				{
+					desc:               "accountDebugFlag false but DebugEnabledOrOverridden is true, expect debugLog.Enabled to be true",
+					inAccountDebugFlag: false,
+					inDebugLog:         &DebugLog{DebugEnabledOrOverridden: true},
+					expectedDebugLog:   &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
+				},
+				{
+					desc:               "accountDebugFlag true but DebugEnabledOrOverridden is false, expect debugLog.Enabled to be true",
+					inAccountDebugFlag: true,
+					inDebugLog:         &DebugLog{},
+					expectedDebugLog:   &DebugLog{Enabled: true},
+				},
+				{
+					desc:               "Both accountDebugFlag and DebugEnabledOrOverridden are true, expect debugLog.Enabled to be true",
+					inAccountDebugFlag: true,
+					inDebugLog:         &DebugLog{DebugEnabledOrOverridden: true},
+					expectedDebugLog:   &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
+				},
+			},
+		},
+	}
+
+	for _, group := range testGroups {
+		for _, tc := range group.testCases {
+			// run
+			actualDebugLog := setDebugLogValues(tc.inAccountDebugFlag, tc.inDebugLog)
+			// assertions
+			assert.Equal(t, tc.expectedDebugLog, actualDebugLog, "%s. %s", group.desc, tc.desc)
+		}
+	}
+}
+
 func TestGetExtBidAdjustmentFactors(t *testing.T) {
 	testCases := []struct {
 		desc                    string
@@ -2164,6 +2236,118 @@ func TestRemoveUnpermissionedEidsUnmarshalErrors(t *testing.T) {
 	}
 }
 
+func TestGetDebugInfo(t *testing.T) {
+	type testInput struct {
+		debugEnabledOrOverridden bool
+		accountDebugFlag         bool
+	}
+	type testOut struct {
+		responseDebugAllow bool
+		accountDebugAllow  bool
+		debugLog           *DebugLog
+	}
+	type testCase struct {
+		in       testInput
+		expected testOut
+	}
+
+	testGroups := []struct {
+		desc   string
+		bidReq *openrtb2.BidRequest
+		tests  []testCase
+	}{
+		{
+			"Bid request doesn't call for debug info",
+			&openrtb2.BidRequest{Test: 0},
+			[]testCase{
+				{
+					testInput{debugEnabledOrOverridden: false, accountDebugFlag: false},
+					testOut{
+						responseDebugAllow: false,
+						accountDebugAllow:  false,
+						debugLog:           &DebugLog{Enabled: false},
+					},
+				},
+				{
+					testInput{debugEnabledOrOverridden: false, accountDebugFlag: true},
+					testOut{
+						responseDebugAllow: false,
+						accountDebugAllow:  false,
+						debugLog:           &DebugLog{Enabled: true},
+					},
+				},
+				{
+					testInput{debugEnabledOrOverridden: true, accountDebugFlag: false},
+					testOut{
+						responseDebugAllow: true,
+						accountDebugAllow:  false,
+						debugLog:           &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
+					},
+				},
+				{
+					testInput{debugEnabledOrOverridden: true, accountDebugFlag: true},
+					testOut{
+						responseDebugAllow: true,
+						accountDebugAllow:  true,
+						debugLog:           &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
+					},
+				},
+			},
+		},
+		{
+			"Bid request requires debug info",
+			&openrtb2.BidRequest{Test: 1},
+			[]testCase{
+				{
+					testInput{debugEnabledOrOverridden: false, accountDebugFlag: false},
+					testOut{
+						responseDebugAllow: false,
+						accountDebugAllow:  false,
+						debugLog:           &DebugLog{Enabled: false},
+					},
+				},
+				{
+					testInput{debugEnabledOrOverridden: false, accountDebugFlag: true},
+					testOut{
+						responseDebugAllow: true,
+						accountDebugAllow:  true,
+						debugLog:           &DebugLog{Enabled: true},
+					},
+				},
+				{
+					testInput{debugEnabledOrOverridden: true, accountDebugFlag: false},
+					testOut{
+						responseDebugAllow: true,
+						accountDebugAllow:  false,
+						debugLog:           &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
+					},
+				},
+				{
+					testInput{debugEnabledOrOverridden: true, accountDebugFlag: true},
+					testOut{
+						responseDebugAllow: true,
+						accountDebugAllow:  true,
+						debugLog:           &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
+					},
+				},
+			},
+		},
+	}
+	for _, group := range testGroups {
+		for i, tc := range group.tests {
+			inDebugLog := &DebugLog{DebugEnabledOrOverridden: tc.in.debugEnabledOrOverridden}
+
+			// run
+			responseDebugAllow, accountDebugAllow, debugLog := getDebugInfo(group.bidReq, nil, tc.in.accountDebugFlag, inDebugLog)
+
+			// assertions
+			assert.Equal(t, tc.expected.responseDebugAllow, responseDebugAllow, "%s - %d", group.desc, i)
+			assert.Equal(t, tc.expected.accountDebugAllow, accountDebugAllow, "%s - %d", group.desc, i)
+			assert.Equal(t, tc.expected.debugLog, debugLog, "%s - %d", group.desc, i)
+		}
+	}
+}
+
 func TestRemoveUnpermissionedEidsEmptyValidations(t *testing.T) {
 	testCases := []struct {
 		description string
diff --git a/router/router_test.go b/router/router_test.go
index 07e59388..24a7709c 100644
--- a/router/router_test.go
+++ b/router/router_test.go
@@ -88,6 +88,14 @@ func TestApplyBidderInfoConfigOverrides(t *testing.T) {
 			givenAdaptersCfg:    map[string]config.Adapter{"a": {Syncer: &config.Syncer{Key: "override"}}},
 			expectedBidderInfos: config.BidderInfos{"a": {Syncer: &config.Syncer{Key: "override"}}},
 		},
+		{
+			// Adapter Configs use a lower case bidder name, but the Bidder Infos follow the official
+			// bidder name casing.
+			description:         "Syncer Override - Case Sensitivity",
+			givenBidderInfos:    config.BidderInfos{"A": {Syncer: &config.Syncer{Key: "original"}}},
+			givenAdaptersCfg:    map[string]config.Adapter{"a": {Syncer: &config.Syncer{Key: "override"}}},
+			expectedBidderInfos: config.BidderInfos{"A": {Syncer: &config.Syncer{Key: "override"}}},
+		},
 		{
 			description:         "UserSyncURL Override IFrame",
 			givenBidderInfos:    config.BidderInfos{"a": {Syncer: &config.Syncer{IFrame: &config.SyncerEndpoint{URL: "original"}}}},
diff --git a/util/maputil/maputil_test.go b/util/maputil/maputil_test.go
index abce102f..114c2332 100644
--- a/util/maputil/maputil_test.go
+++ b/util/maputil/maputil_test.go
@@ -164,3 +164,84 @@ func TestReadEmbeddedString(t *testing.T) {
 		assert.Equal(t, test.expectedOK, resultOK, test.description+":ok")
 	}
 }
+
+func TestHasElement(t *testing.T) {
+	testCases := []struct {
+		description string
+		value       map[string]interface{}
+		keys        []string
+		expected    bool
+	}{
+		{
+			description: "Level 1 - Exists",
+			value:       map[string]interface{}{"foo": "exists"},
+			keys:        []string{"foo"},
+			expected:    true,
+		},
+		{
+			description: "Level 1 - Does Not Exist",
+			value:       map[string]interface{}{"foo": "exists"},
+			keys:        []string{"doesnotexist"},
+			expected:    false,
+		},
+		{
+			description: "Level 2 - Exists",
+			value:       map[string]interface{}{"foo": map[string]interface{}{"bar": "exists"}},
+			keys:        []string{"foo", "bar"},
+			expected:    true,
+		},
+		{
+			description: "Level 2 - Top Level Does Not Exist",
+			value:       map[string]interface{}{"foo": map[string]interface{}{"bar": "exists"}},
+			keys:        []string{"doesnotexist", "bar"},
+			expected:    false,
+		},
+		{
+			description: "Level 2 - Lower Level Does Not Exist",
+			value:       map[string]interface{}{"foo": map[string]interface{}{"bar": "exists"}},
+			keys:        []string{"foo", "doesnotexist"},
+			expected:    false,
+		},
+		{
+			description: "Level 2 - Does Not Exist At All",
+			value:       map[string]interface{}{"foo": map[string]interface{}{"bar": "exists"}},
+			keys:        []string{"doesnotexist", "doesnotexist"},
+			expected:    false,
+		},
+		{
+			description: "Keys Nil",
+			value:       map[string]interface{}{"foo": "exists"},
+			keys:        nil,
+			expected:    false,
+		},
+		{
+			description: "Keys Empty",
+			value:       map[string]interface{}{"foo": "exists"},
+			keys:        []string{},
+			expected:    false,
+		},
+		{
+			description: "Map Nil",
+			value:       nil,
+			keys:        []string{"foo"},
+			expected:    false,
+		},
+		{
+			description: "Map Empty",
+			value:       map[string]interface{}{},
+			keys:        []string{"foo"},
+			expected:    false,
+		},
+		{
+			description: "Nil",
+			value:       nil,
+			keys:        nil,
+			expected:    false,
+		},
+	}
+
+	for _, test := range testCases {
+		result := HasElement(test.value, test.keys...)
+		assert.Equal(t, test.expected, result, test.description)
+	}
+}