Repository: prebid/prebid-server
Pull request #2583 (2023-02-14 15:00:12)

nextMillennium Bid Adapter: Add URL for sync type redirect


Patch:
diff --git a/adapters/criteo/criteo.go b/adapters/criteo/criteo.go
index 584ef40c..66e2a547 100644
--- a/adapters/criteo/criteo.go
+++ b/adapters/criteo/criteo.go
@@ -13,102 +13,66 @@ import (
 )
 
 type adapter struct {
-	uri             string
-	slotIDGenerator slotIDGenerator
+	endpoint string
 }
 
-func (a *adapter) MakeRequests(request *openrtb2.BidRequest, extraRequestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
-	criteoRequest, errs := newCriteoRequest(a.slotIDGenerator, request)
-	if len(errs) > 0 {
-		return nil, errs
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
 	}
+	return bidder, nil
+}
 
-	jsonRequest, err := json.Marshal(criteoRequest)
+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}
 	}
 
-	rqData := adapters.RequestData{
-		Method:  "POST",
-		Uri:     a.uri,
-		Body:    jsonRequest,
-		Headers: getCriteoRequestHeaders(&criteoRequest),
+	requestData := &adapters.RequestData{
+		Method: "POST",
+		Uri:    a.endpoint,
+		Body:   requestJSON,
 	}
 
-	return []*adapters.RequestData{&rqData}, nil
+	return []*adapters.RequestData{requestData}, nil
 }
 
-func getCriteoRequestHeaders(criteoRequest *criteoRequest) http.Header {
-	headers := http.Header{}
-
-	// criteoRequest is known not to be nil
-	// If there was an error generating it from newCriteoRequest, the errors will be returned immediately
-	// and this method won't be called
-
-	if criteoRequest.User.CookieID != "" {
-		headers.Add("Cookie", "uid="+criteoRequest.User.CookieID)
-	}
-
-	if criteoRequest.User.IP != "" {
-		headers.Add("X-Forwarded-For", criteoRequest.User.IP)
-	}
-
-	if criteoRequest.User.IPv6 != "" {
-		headers.Add("X-Forwarded-For", criteoRequest.User.IPv6)
-	}
-
-	if criteoRequest.User.UA != "" {
-		headers.Add("User-Agent", criteoRequest.User.UA)
-	}
-
-	return headers
-}
-
-func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
-	if response.StatusCode == http.StatusNoContent {
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if responseData.StatusCode == http.StatusNoContent {
 		return nil, nil
 	}
 
-	if response.StatusCode != http.StatusOK {
-		return nil, []error{&errortypes.BadServerResponse{
-			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
-		}}
+	if responseData.StatusCode == http.StatusBadRequest {
+		err := &errortypes.BadInput{
+			Message: "Unexpected status code: 400. Run with request.debug = 1 for more info.",
+		}
+		return nil, []error{err}
 	}
 
-	bidResponse, err := newCriteoResponseFromBytes(response.Body)
-	if err != nil {
+	if responseData.StatusCode != http.StatusOK {
+		err := &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
+		}
 		return nil, []error{err}
 	}
 
-	bidderResponse := adapters.NewBidderResponse()
-	bidderResponse.Bids = make([]*adapters.TypedBid, len(bidResponse.Slots))
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(responseData.Body, &response); err != nil {
+		return nil, []error{err}
+	}
 
-	for i := 0; i < len(bidResponse.Slots); i++ {
-		bidderResponse.Bids[i] = &adapters.TypedBid{
-			Bid: &openrtb2.Bid{
-				ID:    bidResponse.Slots[i].ArbitrageID,
-				ImpID: bidResponse.Slots[i].ImpID,
-				Price: bidResponse.Slots[i].CPM,
-				AdM:   bidResponse.Slots[i].Creative,
-				W:     bidResponse.Slots[i].Width,
-				H:     bidResponse.Slots[i].Height,
-				CrID:  bidResponse.Slots[i].CreativeCode,
-			},
-			BidType: openrtb_ext.BidTypeBanner,
+	bidResponse := adapters.NewBidderResponse()
+	bidResponse.Currency = response.Cur
+	for _, seatBid := range response.SeatBid {
+		for i := range seatBid.Bid {
+			b := &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: openrtb_ext.BidTypeBanner,
+			}
+			bidResponse.Bids = append(bidResponse.Bids, b)
 		}
 	}
 
-	return bidderResponse, nil
-}
-
-// Builder builds a new instance of the Criteo adapter for the given bidder with the given config.
-func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
-	return builderWithGuidGenerator(bidderName, config, server, newRandomSlotIDGenerator())
-}
-
-func builderWithGuidGenerator(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server, slotIDGenerator slotIDGenerator) (adapters.Bidder, error) {
-	return &adapter{
-		uri:             config.Endpoint,
-		slotIDGenerator: slotIDGenerator,
-	}, nil
+	return bidResponse, nil
 }
diff --git a/adapters/criteo/generators.go b/adapters/criteo/generators.go
deleted file mode 100644
index 0ae0ceca..00000000
--- a/adapters/criteo/generators.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package criteo
-
-import "github.com/gofrs/uuid"
-
-type slotIDGenerator interface {
-	NewSlotID() (string, error)
-}
-
-type randomSlotIDGenerator struct{}
-
-func newRandomSlotIDGenerator() randomSlotIDGenerator {
-	return randomSlotIDGenerator{}
-}
-
-func (g randomSlotIDGenerator) NewSlotID() (string, error) {
-	guid, err := uuid.NewV4()
-	if err != nil {
-		return "", err
-	}
-
-	return guid.String(), nil
-}
-
-type fakeSlotIDGenerator struct {
-	fakeSlotID string
-}
-
-func newFakeGuidGenerator(fakeSlotID string) fakeSlotIDGenerator {
-	return fakeSlotIDGenerator{
-		fakeSlotID,
-	}
-}
-
-func (f fakeSlotIDGenerator) NewSlotID() (string, error) {
-	return f.fakeSlotID, nil
-}
diff --git a/adapters/criteo/models.go b/adapters/criteo/models.go
deleted file mode 100644
index 893a5a11..00000000
--- a/adapters/criteo/models.go
+++ /dev/null
@@ -1,299 +0,0 @@
-package criteo
-
-import (
-	"encoding/json"
-	"fmt"
-	"strings"
-
-	"github.com/prebid/openrtb/v17/openrtb2"
-	"github.com/prebid/prebid-server/adapters"
-	"github.com/prebid/prebid-server/errortypes"
-	"github.com/prebid/prebid-server/openrtb_ext"
-)
-
-type criteoRequest struct {
-	ID          string              `json:"id,omitempty"`
-	Publisher   criteoPublisher     `json:"publisher,omitempty"`
-	User        criteoUser          `json:"user,omitempty"`
-	GdprConsent criteoGdprConsent   `json:"gdprconsent,omitempty"`
-	Slots       []criteoRequestSlot `json:"slots,omitempty"`
-	Eids        []openrtb2.EID      `json:"eids,omitempty"`
-}
-
-func newCriteoRequest(slotIDGenerator slotIDGenerator, request *openrtb2.BidRequest) (criteoRequest, []error) {
-	var errs []error
-
-	// request cannot be nil by design
-
-	criteoRequest := criteoRequest{}
-
-	criteoRequest.ID = request.ID
-
-	// Extracting request slots
-	if len(request.Imp) > 0 {
-		criteoSlots, slotsErr := newCriteoRequestSlots(slotIDGenerator, request.Imp)
-		if len(slotsErr) > 0 {
-			return criteoRequest, slotsErr
-		}
-		criteoRequest.Slots = criteoSlots
-	}
-
-	var networkId *int64
-	for _, criteoSlot := range criteoRequest.Slots {
-		if networkId == nil && criteoSlot.NetworkID != nil && *criteoSlot.NetworkID > 0 {
-			networkId = criteoSlot.NetworkID
-		} else if networkId != nil && criteoSlot.NetworkID != nil && *criteoSlot.NetworkID != *networkId {
-			return criteoRequest, []error{&errortypes.BadInput{
-				Message: "Bid request has slots coming with several network IDs which is not allowed",
-			}}
-		}
-	}
-
-	criteoRequest.Publisher = newCriteoPublisher(networkId, request.App, request.Site)
-
-	var regsExt *openrtb_ext.ExtRegs
-	if request.Regs != nil && request.Regs.Ext != nil {
-		if err := json.Unmarshal(request.Regs.Ext, &regsExt); err != nil {
-			errs = append(errs, err)
-		}
-	}
-
-	criteoRequest.User = newCriteoUser(request.User, request.Device, regsExt)
-
-	if gdprConsent, err := newCriteoGdprConsent(request.User, regsExt); err != nil {
-		errs = append(errs, err)
-	} else {
-		criteoRequest.GdprConsent = gdprConsent
-	}
-
-	if request.User != nil && request.User.Ext != nil {
-		var extUser openrtb_ext.ExtUser
-		if err := json.Unmarshal(request.User.Ext, &extUser); err != nil {
-			errs = append(errs, err)
-		} else {
-			criteoRequest.Eids = extUser.Eids
-		}
-	}
-
-	return criteoRequest, errs
-}
-
-type criteoPublisher struct {
-	SiteID    string `json:"siteid,omitempty"`
-	BundleID  string `json:"bundleid,omitempty"`
-	URL       string `json:"url,omitempty"`
-	NetworkID *int64 `json:"networkid,omitempty"`
-}
-
-func newCriteoPublisher(networkId *int64, app *openrtb2.App, site *openrtb2.Site) criteoPublisher {
-	// Both app and site cannot be nil at the same time by design in PBS
-
-	criteoPublisher := criteoPublisher{}
-
-	if networkId != nil && *networkId > 0 {
-		criteoPublisher.NetworkID = networkId
-	}
-
-	if app != nil {
-		criteoPublisher.BundleID = app.Bundle
-	}
-
-	if site != nil {
-		criteoPublisher.SiteID = site.ID
-		criteoPublisher.URL = site.Page
-	}
-
-	return criteoPublisher
-}
-
-type criteoUser struct {
-	DeviceID     string `json:"deviceid,omitempty"`
-	DeviceOS     string `json:"deviceos,omitempty"`
-	DeviceIDType string `json:"deviceidtype,omitempty"`
-	CookieID     string `json:"cookieuid,omitempty"`
-	UID          string `json:"uid,omitempty"`
-	IP           string `json:"ip,omitempty"`
-	IPv6         string `json:"ipv6,omitempty"`
-	UA           string `json:"ua,omitempty"`
-	UspIab       string `json:"uspIab,omitempty"`
-}
-
-func newCriteoUser(user *openrtb2.User, device *openrtb2.Device, regsExt *openrtb_ext.ExtRegs) criteoUser {
-	criteoUser := criteoUser{}
-
-	if user == nil && device == nil {
-		return criteoUser
-	}
-
-	if user != nil {
-		criteoUser.CookieID = user.BuyerUID
-	}
-
-	if device != nil {
-		deviceType := getDeviceType(device.OS)
-		criteoUser.DeviceIDType = deviceType
-
-		criteoUser.DeviceOS = device.OS
-		criteoUser.DeviceID = device.IFA
-		criteoUser.IP = device.IP
-		criteoUser.IPv6 = device.IPv6
-		criteoUser.UA = device.UA
-	}
-
-	if regsExt != nil {
-		criteoUser.UspIab = regsExt.USPrivacy // CCPA
-	}
-
-	return criteoUser
-}
-
-type criteoGdprConsent struct {
-	GdprApplies *bool  `json:"gdprapplies,omitempty"`
-	ConsentData string `json:"consentdata,omitempty"`
-}
-
-func newCriteoGdprConsent(user *openrtb2.User, regsExt *openrtb_ext.ExtRegs) (criteoGdprConsent, error) {
-	consent := criteoGdprConsent{}
-
-	if user == nil && regsExt == nil {
-		return consent, nil
-	}
-
-	if user != nil && user.Ext != nil {
-		var userExt *openrtb_ext.ExtUser
-		if err := json.Unmarshal(user.Ext, &userExt); err != nil {
-			return consent, err
-		}
-		consent.ConsentData = userExt.Consent
-	}
-
-	if regsExt != nil {
-		if regsExt.GDPR != nil {
-			gdprApplies := bool((*regsExt.GDPR & 1) == 1)
-			consent.GdprApplies = &gdprApplies
-		}
-	}
-
-	return consent, nil
-}
-
-type criteoRequestSlot struct {
-	SlotID    string              `json:"slotid,omitempty"`
-	ImpID     string              `json:"impid,omitempty"`
-	ZoneID    *int64              `json:"zoneid,omitempty"`
-	NetworkID *int64              `json:"networkid,omitempty"`
-	Sizes     []criteoRequestSize `json:"sizes,omitempty"`
-}
-
-func newCriteoRequestSlots(slotIDGenerator slotIDGenerator, impressions []openrtb2.Imp) ([]criteoRequestSlot, []error) {
-	var errs []error
-
-	// `impressions` known not to be nil or empty by design, PBS checks it upstream.
-
-	// Criteo slot should comes with any of (both are ok as well):
-	//   - `zoneid`
-	//   - `networkid`, `slotid`, `sizes`
-	//
-	// if not, criteo will reject the slot.
-
-	var criteoSlots = make([]criteoRequestSlot, len(impressions))
-
-	for i := 0; i < len(impressions); i++ {
-		criteoSlots[i] = criteoRequestSlot{}
-
-		criteoSlots[i].ImpID = impressions[i].ID
-
-		// Generating a random slot ID
-		generatedSlotID, err := slotIDGenerator.NewSlotID()
-		if err != nil {
-			errs = append(errs, err)
-			continue
-		}
-		criteoSlots[i].SlotID = generatedSlotID
-
-		if impressions[i].Banner != nil {
-			if impressions[i].Banner.Format != nil {
-				criteoSlots[i].Sizes = make([]criteoRequestSize, len(impressions[i].Banner.Format))
-				for idx, format := range impressions[i].Banner.Format {
-					criteoSlots[i].Sizes[idx] = newCriteoRequestSize(format.W, format.H)
-				}
-			} else if impressions[i].Banner.W != nil && *impressions[i].Banner.W > 0 && impressions[i].Banner.H != nil && *impressions[i].Banner.H > 0 {
-				criteoSlots[i].Sizes = make([]criteoRequestSize, 1)
-				criteoSlots[i].Sizes[0] = newCriteoRequestSize(*impressions[i].Banner.W, *impressions[i].Banner.H)
-			}
-		}
-
-		var bidderExt adapters.ExtImpBidder
-		if err := json.Unmarshal(impressions[i].Ext, &bidderExt); err != nil {
-			errs = append(errs, err)
-			continue
-		}
-
-		if bidderExt.Bidder != nil {
-			var criteoExt openrtb_ext.ExtImpCriteo
-			if err := json.Unmarshal(bidderExt.Bidder, &criteoExt); err != nil {
-				errs = append(errs, err)
-				continue
-			}
-			if criteoExt.ZoneID > 0 {
-				criteoSlots[i].ZoneID = &criteoExt.ZoneID
-			}
-			if criteoExt.NetworkID > 0 {
-				criteoSlots[i].NetworkID = &criteoExt.NetworkID
-			}
-		}
-	}
-
-	return criteoSlots, errs
-}
-
-type criteoRequestSize = string
-
-func newCriteoRequestSize(width int64, height int64) criteoRequestSize {
-	return fmt.Sprintf("%dx%d", width, height)
-}
-
-var deviceType = map[string]string{
-	"ios":     "idfa",
-	"android": "gaid",
-	"unknown": "unknown",
-}
-
-func getDeviceType(os string) string {
-	if os != "" {
-		if dtype, ok := deviceType[strings.ToLower(os)]; ok {
-			return dtype
-		}
-	}
-
-	return deviceType["unknown"]
-}
-
-type criteoResponse struct {
-	ID    string               `json:"id,omitempty"`
-	Slots []criteoResponseSlot `json:"slots,omitempty"`
-}
-
-func newCriteoResponseFromBytes(bytes []byte) (criteoResponse, error) {
-	var err error
-	var bidResponse criteoResponse
-
-	if err = json.Unmarshal(bytes, &bidResponse); err != nil {
-		return bidResponse, err
-	}
-
-	return bidResponse, nil
-}
-
-type criteoResponseSlot struct {
-	ArbitrageID  string  `json:"arbitrageid,omitempty"`
-	ImpID        string  `json:"impid,omitempty"`
-	ZoneID       int64   `json:"zoneid,omitempty"`
-	NetworkID    int64   `json:"networkid,omitempty"`
-	CPM          float64 `json:"cpm,omitempty"`
-	Currency     string  `json:"currency,omitempty"`
-	Width        int64   `json:"width,omitempty"`
-	Height       int64   `json:"height,omitempty"`
-	Creative     string  `json:"creative,omitempty"`
-	CreativeCode string  `json:"creativecode,omitempty"`
-}
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 6e38f8f8..96637a64 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -17,6 +17,7 @@ import (
 	"github.com/prebid/openrtb/v17/openrtb2"
 	"github.com/prebid/openrtb/v17/openrtb3"
 	"github.com/prebid/prebid-server/hooks/hookexecution"
+	"github.com/prebid/prebid-server/ortb"
 	"github.com/prebid/prebid-server/util/uuidutil"
 	jsonpatch "gopkg.in/evanphx/json-patch.v4"
 
@@ -444,16 +445,20 @@ func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openr
 	deps.setFieldsImplicitly(httpRequest, req)
 
 	// Need to ensure cache and targeting are turned on
-	e = defaultRequestExt(req)
+	e = initAmpTargetingAndCache(req)
 	if errs = append(errs, e...); errortypes.ContainsFatalError(errs) {
 		return
 	}
 
-	// At this point, we should have a valid request that definitely has Targeting and Cache turned on
-	hasStoredResponses := len(storedAuctionResponses) > 0
+	if err := ortb.SetDefaults(req); err != nil {
+		errs = append(errs, err)
+		return
+	}
 
+	hasStoredResponses := len(storedAuctionResponses) > 0
 	e = deps.validateRequest(req, true, hasStoredResponses, storedBidResponses, false)
 	errs = append(errs, e...)
+
 	return
 }
 
@@ -697,7 +702,7 @@ func setHeights(formats []openrtb2.Format, height int64) {
 
 // AMP won't function unless ext.prebid.targeting and ext.prebid.cache.bids are defined.
 // If the user didn't include them, default those here.
-func defaultRequestExt(req *openrtb_ext.RequestWrapper) []error {
+func initAmpTargetingAndCache(req *openrtb_ext.RequestWrapper) []error {
 	extRequest, err := req.GetRequestExt()
 	if err != nil {
 		return []error{err}
@@ -706,27 +711,23 @@ func defaultRequestExt(req *openrtb_ext.RequestWrapper) []error {
 	prebid := extRequest.GetPrebid()
 	prebidModified := false
 
-	// create prebid object if missing from request
+	// create prebid object if missing
 	if prebid == nil {
 		prebid = &openrtb_ext.ExtRequestPrebid{}
 	}
 
-	// Ensure Targeting and caching is on
+	// create targeting object if missing
 	if prebid.Targeting == nil {
-		prebid.Targeting = &openrtb_ext.ExtRequestTargeting{
-			IncludeWinners:    true,
-			IncludeBidderKeys: true,
-			PriceGranularity:  openrtb_ext.PriceGranularityFromString("med"),
-		}
+		prebid.Targeting = &openrtb_ext.ExtRequestTargeting{}
 		prebidModified = true
 	}
 
+	// create cache object if missing
 	if prebid.Cache == nil {
-		prebid.Cache = &openrtb_ext.ExtRequestPrebidCache{
-			Bids: &openrtb_ext.ExtRequestPrebidCacheBids{},
-		}
+		prebid.Cache = &openrtb_ext.ExtRequestPrebidCache{}
 		prebidModified = true
-	} else if prebid.Cache.Bids == nil {
+	}
+	if prebid.Cache.Bids == nil {
 		prebid.Cache.Bids = &openrtb_ext.ExtRequestPrebidCacheBids{}
 		prebidModified = true
 	}
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index f8c4aecd..967bdf04 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -24,6 +24,7 @@ import (
 	"github.com/prebid/openrtb/v17/openrtb2"
 	"github.com/prebid/openrtb/v17/openrtb3"
 	"github.com/prebid/prebid-server/hooks"
+	"github.com/prebid/prebid-server/ortb"
 	"golang.org/x/net/publicsuffix"
 	jsonpatch "gopkg.in/evanphx/json-patch.v4"
 
@@ -439,6 +440,11 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric
 	// Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers).
 	deps.setFieldsImplicitly(httpRequest, req)
 
+	if err := ortb.SetDefaults(req); err != nil {
+		errs = []error{err}
+		return
+	}
+
 	if err := processInterstitials(req); err != nil {
 		errs = []error{err}
 		return
@@ -446,10 +452,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric
 
 	lmt.ModifyForIOS(req.BidRequest)
 
-	var hasStoredResponses bool
-	if len(storedAuctionResponses) > 0 {
-		hasStoredResponses = true
-	}
+	hasStoredResponses := len(storedAuctionResponses) > 0
 	errL := deps.validateRequest(req, false, hasStoredResponses, storedBidResponses, hasStoredBidRequest)
 	if len(errL) > 0 {
 		errs = append(errs, errL...)
@@ -671,7 +674,9 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp
 	reqPrebid := reqExt.GetPrebid()
 	if err := deps.parseBidExt(req); err != nil {
 		return []error{err}
-	} else if reqPrebid != nil {
+	}
+
+	if reqPrebid != nil {
 		aliases = reqPrebid.Aliases
 
 		if err := deps.validateAliases(aliases); err != nil {
@@ -1499,8 +1504,56 @@ func validateRequestExt(req *openrtb_ext.RequestWrapper) error {
 	}
 
 	prebid := reqExt.GetPrebid()
-	if prebid != nil && prebid.Cache != nil && (prebid.Cache.Bids == nil && prebid.Cache.VastXML == nil) {
-		return errors.New(`request.ext is invalid: request.ext.prebid.cache requires one of the "bids" or "vastxml" properties`)
+
+	// exit early if there is no request.ext.prebid to validate
+	if prebid == nil {
+		return nil
+	}
+
+	if prebid.Cache != nil {
+		if prebid.Cache.Bids == nil && prebid.Cache.VastXML == nil {
+			return errors.New(`request.ext is invalid: request.ext.prebid.cache requires one of the "bids" or "vastxml" properties`)
+		}
+	}
+
+	if err := validateTargeting(prebid.Targeting); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func validateTargeting(t *openrtb_ext.ExtRequestTargeting) error {
+	if t == nil {
+		return nil
+	}
+
+	if (t.IncludeWinners == nil || !*t.IncludeWinners) && (t.IncludeBidderKeys == nil || !*t.IncludeBidderKeys) {
+		return errors.New("ext.prebid.targeting: At least one of includewinners or includebidderkeys must be enabled to enable targeting support")
+	}
+
+	if t.PriceGranularity != nil {
+		pg := t.PriceGranularity
+
+		if pg.Precision == nil {
+			return errors.New("Price granularity error: precision is required")
+		} else if *pg.Precision < 0 {
+			return errors.New("Price granularity error: precision must be non-negative")
+		} else if *pg.Precision > openrtb_ext.MaxDecimalFigures {
+			return fmt.Errorf("Price granularity error: precision of more than %d significant figures is not supported", openrtb_ext.MaxDecimalFigures)
+		}
+
+		var prevMax float64 = 0
+		for _, gr := range pg.Ranges {
+			if gr.Max <= prevMax {
+				return errors.New(`Price granularity error: range list must be ordered with increasing "max"`)
+			}
+
+			if gr.Increment <= 0.0 {
+				return errors.New("Price granularity error: increment must be a nonzero positive number")
+			}
+			prevMax = gr.Max
+		}
 	}
 
 	return nil
diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/addtl-consent-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/addtl-consent-through-query.json
index d67df50e..c775d332 100644
--- a/endpoints/openrtb2/sample-requests/amp/consent-through-query/addtl-consent-through-query.json
+++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/addtl-consent-through-query.json
@@ -3,12 +3,18 @@
   "query": "tag_id=101&addtl_consent=1~1.35.41.101",
   "config": {
     "mockBidders": [
-      {"bidderName": "appnexus", "currency": "USD", "price": 15}
+      {
+        "bidderName": "appnexus",
+        "currency": "USD",
+        "price": 15
+      }
     ]
   },
   "mockBidRequest": {
     "id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
-    "site": {"page": "prebid.org"},
+    "site": {
+      "page": "prebid.org"
+    },
     "imp": [
       {
         "id": "/19968336/header-bid-tag-0",
@@ -32,10 +38,12 @@
     "id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
     "site": {
       "page": "prebid.org",
-      "ext": {"amp":1}
+      "ext": {
+        "amp": 1
+      }
     },
     "device": {
-      "ip":"192.0.2.1"
+      "ip": "192.0.2.1"
     },
     "at": 1,
     "imp": [
@@ -63,11 +71,44 @@
     ],
     "user": {
       "ext": {
-        "ConsentedProvidersSettings":{"consented_providers":"1~1.35.41.101"},
-        "consented_providers_settings":{"consented_providers":[1,35,41,101]}
+        "ConsentedProvidersSettings": {
+          "consented_providers": "1~1.35.41.101"
+        },
+        "consented_providers_settings": {
+          "consented_providers": [
+            1,
+            35,
+            41,
+            101
+          ]
+        }
       }
     },
-    "ext": {"prebid":{"cache":{"bids":{"returnCreative":null},"vastxml":null},"channel":{"name":"amp","version":""},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true,"includebrandcategory":null,"includeformat":false,"durationrangesec":null,"preferdeals":false}}}
+    "ext": {
+      "prebid": {
+        "cache": {
+          "bids": {}
+        },
+        "channel": {
+          "name": "amp",
+          "version": ""
+        },
+        "targeting": {
+          "pricegranularity": {
+            "precision": 2,
+            "ranges": [
+              {
+                "min": 0,
+                "max": 20,
+                "increment": 0.1
+              }
+            ]
+          },
+          "includewinners": true,
+          "includebidderkeys": true
+        }
+      }
+    }
   },
   "expectedAmpResponse": {
     "targeting": {
@@ -96,4 +137,4 @@
     }
   },
   "expectedReturnCode": 200
-}
+}
\ No newline at end of file
diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-ccpa-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-ccpa-through-query.json
index cbcd5ed7..f2e2a217 100644
--- a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-ccpa-through-query.json
+++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-ccpa-through-query.json
@@ -81,10 +81,7 @@
     "ext": {
       "prebid": {
         "cache": {
-          "bids": {
-            "returnCreative": null
-          },
-          "vastxml": null
+          "bids": {}
         },
         "channel": {
           "name": "amp",
@@ -102,11 +99,7 @@
             ]
           },
           "includewinners": true,
-          "includebidderkeys": true,
-          "includebrandcategory": null,
-          "includeformat": false,
-          "durationrangesec": null,
-          "preferdeals": false
+          "includebidderkeys": true
         }
       }
     }
diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-legacy-tcf2-consent-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-legacy-tcf2-consent-through-query.json
index 3a3baca9..1cf81ea2 100644
--- a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-legacy-tcf2-consent-through-query.json
+++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-legacy-tcf2-consent-through-query.json
@@ -86,10 +86,7 @@
     "ext": {
       "prebid": {
         "cache": {
-          "bids": {
-            "returnCreative": null
-          },
-          "vastxml": null
+          "bids": {}
         },
         "channel": {
           "name": "amp",
@@ -107,11 +104,7 @@
             ]
           },
           "includewinners": true,
-          "includebidderkeys": true,
-          "includebrandcategory": null,
-          "includeformat": false,
-          "durationrangesec": null,
-          "preferdeals": false
+          "includebidderkeys": true
         }
       }
     }
diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf1-consent-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf1-consent-through-query.json
index c81dff7a..c596a832 100644
--- a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf1-consent-through-query.json
+++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf1-consent-through-query.json
@@ -86,10 +86,7 @@
     "ext": {
       "prebid": {
         "cache": {
-          "bids": {
-            "returnCreative": null
-          },
-          "vastxml": null
+          "bids": {}
         },
         "channel": {
           "name": "amp",
@@ -107,11 +104,7 @@
             ]
           },
           "includewinners": true,
-          "includebidderkeys": true,
-          "includebrandcategory": null,
-          "includeformat": false,
-          "durationrangesec": null,
-          "preferdeals": false
+          "includebidderkeys": true
         }
       }
     }
diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf2-consent-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf2-consent-through-query.json
index 7d3d9931..fdc25ed9 100644
--- a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf2-consent-through-query.json
+++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf2-consent-through-query.json
@@ -86,10 +86,7 @@
     "ext": {
       "prebid": {
         "cache": {
-          "bids": {
-            "returnCreative": null
-          },
-          "vastxml": null
+          "bids": {}
         },
         "channel": {
           "name": "amp",
@@ -107,11 +104,7 @@
             ]
           },
           "includewinners": true,
-          "includebidderkeys": true,
-          "includebrandcategory": null,
-          "includeformat": false,
-          "durationrangesec": null,
-          "preferdeals": false
+          "includebidderkeys": true
         }
       }
     }
diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-backward-compatible-merge.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-backward-compatible-merge.json
index 425070d1..9e9fd538 100644
--- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-backward-compatible-merge.json
+++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-backward-compatible-merge.json
@@ -64,7 +64,18 @@
   "expectedReqExt": {
     "prebid": {
       "targeting": {
-        "pricegranularity": "low"
+        "pricegranularity": {
+          "precision": 2,
+          "ranges": [
+            {
+              "min": 0,
+              "max": 5,
+              "increment": 0.5
+            }
+          ]
+        },
+        "includewinners": true,
+        "includebidderkeys": true
       },
       "cache": {
         "bids": {}
diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-merge.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-merge.json
index 22026f56..9f0e78be 100644
--- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-merge.json
+++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-merge.json
@@ -66,7 +66,18 @@
   "expectedReqExt": {
     "prebid": {
       "targeting": {
-        "pricegranularity": "low"
+        "pricegranularity": {
+          "precision": 2,
+          "ranges": [
+            {
+              "min": 0,
+              "max": 5,
+              "increment": 0.5
+            }
+          ]
+        },
+        "includewinners": true,
+        "includebidderkeys": true
       },
       "cache": {
         "bids": {}
diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params.json
index 9d4bcd0d..27ee9da4 100644
--- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params.json
+++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params.json
@@ -59,7 +59,18 @@
   "expectedReqExt": {
     "prebid": {
       "targeting": {
-        "pricegranularity": "low"
+        "pricegranularity": {
+          "precision": 2,
+          "ranges": [
+            {
+              "min": 0,
+              "max": 5,
+              "increment": 0.5
+            }
+          ]
+        },
+        "includewinners": true,
+        "includebidderkeys": true
       },
       "cache": {
         "bids": {}
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index eddc5173..8fbb7f42 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -19,6 +19,7 @@ import (
 	"github.com/julienschmidt/httprouter"
 	"github.com/prebid/openrtb/v17/openrtb2"
 	"github.com/prebid/prebid-server/hooks/hookexecution"
+	"github.com/prebid/prebid-server/ortb"
 	jsonpatch "gopkg.in/evanphx/json-patch.v4"
 
 	accountService "github.com/prebid/prebid-server/account"
@@ -33,6 +34,7 @@ import (
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
 	"github.com/prebid/prebid-server/usersync"
 	"github.com/prebid/prebid-server/util/iputil"
+	"github.com/prebid/prebid-server/util/ptrutil"
 	"github.com/prebid/prebid-server/util/uuidutil"
 	"github.com/prebid/prebid-server/version"
 )
@@ -59,7 +61,7 @@ func NewVideoEndpoint(
 		return nil, errors.New("NewVideoEndpoint requires non-nil arguments.")
 	}
 
-	defRequest := defReqJSON != nil && len(defReqJSON) > 0
+	defRequest := len(defReqJSON) > 0
 
 	ipValidator := iputil.PublicNetworkIPValidator{
 		IPv4PrivateNetworks: cfg.RequestValidation.IPv4PrivateNetworksParsed,
@@ -253,6 +255,11 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	// Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers).
 	deps.setFieldsImplicitly(r, bidReqWrapper)
 
+	if err := ortb.SetDefaults(bidReqWrapper); err != nil {
+		handleError(&labels, w, errL, &vo, &debugLog)
+		return
+	}
+
 	errL = deps.validateRequest(bidReqWrapper, false, false, nil, false)
 	if errortypes.ContainsFatalError(errL) {
 		handleError(&labels, w, errL, &vo, &debugLog)
@@ -586,7 +593,6 @@ func getVideoStoredRequestId(request []byte) (string, error) {
 }
 
 func mergeData(videoRequest *openrtb_ext.BidRequestVideo, bidRequest *openrtb2.BidRequest) error {
-
 	if videoRequest.Site != nil {
 		bidRequest.Site = videoRequest.Site
 		if &videoRequest.Content != nil {
@@ -639,7 +645,6 @@ func mergeData(videoRequest *openrtb_ext.BidRequestVideo, bidRequest *openrtb2.B
 }
 
 func createBidExtension(videoRequest *openrtb_ext.BidRequestVideo) ([]byte, error) {
-
 	var inclBrandCat *openrtb_ext.ExtIncludeBrandCategory
 	if videoRequest.IncludeBrandCategory != nil {
 		inclBrandCat = &openrtb_ext.ExtIncludeBrandCategory{
@@ -659,16 +664,11 @@ func createBidExtension(videoRequest *openrtb_ext.BidRequestVideo) ([]byte, erro
 		durationRangeSec = videoRequest.PodConfig.DurationRangeSec
 	}
 
-	priceGranularity := openrtb_ext.PriceGranularityFromString("med")
-	if videoRequest.PriceGranularity.Precision != 0 {
-		priceGranularity = videoRequest.PriceGranularity
-	}
-
 	targeting := openrtb_ext.ExtRequestTargeting{
-		PriceGranularity:     priceGranularity,
+		PriceGranularity:     videoRequest.PriceGranularity,
 		IncludeBrandCategory: inclBrandCat,
 		DurationRangeSec:     durationRangeSec,
-		IncludeBidderKeys:    true,
+		IncludeBidderKeys:    ptrutil.ToPtr(true),
 		AppendBidderNames:    videoRequest.AppendBidderNames,
 	}
 
@@ -684,11 +684,7 @@ func createBidExtension(videoRequest *openrtb_ext.BidRequestVideo) ([]byte, erro
 	}
 	extReq := openrtb_ext.ExtRequest{Prebid: prebid}
 
-	reqJSON, err := json.Marshal(extReq)
-	if err != nil {
-		return nil, err
-	}
-	return reqJSON, nil
+	return json.Marshal(extReq)
 }
 
 func (deps *endpointDeps) parseVideoRequest(request []byte, headers http.Header) (req *openrtb_ext.BidRequestVideo, errs []error, podErrors []PodError) {
diff --git a/exchange/price_granularity.go b/exchange/price_granularity.go
index af3300b2..1af56ea2 100644
--- a/exchange/price_granularity.go
+++ b/exchange/price_granularity.go
@@ -11,9 +11,10 @@ import (
 func GetPriceBucket(cpm float64, config openrtb_ext.PriceGranularity) string {
 	cpmStr := ""
 	bucketMax := 0.0
-	increment := 0.0
-	precision := config.Precision
 	bucketMin := 0.0
+	increment := 0.0
+	precision := *config.Precision
+
 	for i := 0; i < len(config.Ranges); i++ {
 		if config.Ranges[i].Max > bucketMax {
 			bucketMax = config.Ranges[i].Max
diff --git a/exchange/utils.go b/exchange/utils.go
index 5450707d..d9c1c022 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -731,12 +731,12 @@ func getExtTargetData(requestExt *openrtb_ext.ExtRequest, cacheInstructions *ext
 
 	if requestExt != nil && requestExt.Prebid.Targeting != nil {
 		targData = &targetData{
-			priceGranularity:  requestExt.Prebid.Targeting.PriceGranularity,
-			includeWinners:    requestExt.Prebid.Targeting.IncludeWinners,
-			includeBidderKeys: requestExt.Prebid.Targeting.IncludeBidderKeys,
+			includeWinners:    *requestExt.Prebid.Targeting.IncludeWinners,
+			includeBidderKeys: *requestExt.Prebid.Targeting.IncludeBidderKeys,
 			includeCacheBids:  cacheInstructions.cacheBids,
 			includeCacheVast:  cacheInstructions.cacheVAST,
 			includeFormat:     requestExt.Prebid.Targeting.IncludeFormat,
+			priceGranularity:  *requestExt.Prebid.Targeting.PriceGranularity,
 			preferDeals:       requestExt.Prebid.Targeting.PreferDeals,
 		}
 	}
diff --git a/openrtb_ext/bid_request_video.go b/openrtb_ext/bid_request_video.go
index 171bd6dc..fa0a31d0 100644
--- a/openrtb_ext/bid_request_video.go
+++ b/openrtb_ext/bid_request_video.go
@@ -98,7 +98,7 @@ type BidRequestVideo struct {
 	//   object; optional
 	// Description:
 	//    Object to tell ad server how much money the “bidder” demand is worth to you
-	PriceGranularity PriceGranularity `json:"pricegranularity,omitempty"`
+	PriceGranularity *PriceGranularity `json:"pricegranularity,omitempty"`
 
 	// Attribute:
 	//   tmax
diff --git a/openrtb_ext/convert_down.go b/openrtb_ext/convert_down.go
index d473271a..98745414 100644
--- a/openrtb_ext/convert_down.go
+++ b/openrtb_ext/convert_down.go
@@ -31,9 +31,10 @@ func ConvertDownTo25(r *RequestWrapper) error {
 		}
 	}
 
-	// Do not remove new OpenRTB 2.6 fields. The spec specifies that bidders and exchanges
-	// must tolerate new or unexpected fields gracefully, either ignoring them or treating
-	// them as unknown.
+	// Remove fields introduced in OpenRTB 2.6+. The previous OpenRTB 2.5 spec did not specify that
+	// bidders must tolerate new or unexpected fields.
+	clear26Fields(r)
+	clear202211Fields(r)
 
 	return nil
 }
@@ -177,3 +178,120 @@ func moveRewardedFrom26ToPrebidExt(i *ImpWrapper) error {
 
 	return nil
 }
+
+// clear26Fields sets all fields introduced in OpenRTB 2.6 to default values, which
+// will cause them to omitted during json marshal.
+func clear26Fields(r *RequestWrapper) {
+	r.WLangB = nil
+	r.CatTax = 0
+
+	if app := r.App; app != nil {
+		app.CatTax = 0
+		app.KwArray = nil
+
+		if content := r.App.Content; content != nil {
+			content.CatTax = 0
+			content.KwArray = nil
+			content.LangB = ""
+			content.Network = nil
+			content.Channel = nil
+
+			if producer := r.App.Content.Producer; producer != nil {
+				producer.CatTax = 0
+			}
+		}
+
+		if publisher := r.App.Publisher; publisher != nil {
+			publisher.CatTax = 0
+		}
+	}
+
+	if site := r.Site; site != nil {
+		site.CatTax = 0
+		site.KwArray = nil
+
+		if content := r.Site.Content; content != nil {
+			content.CatTax = 0
+			content.KwArray = nil
+			content.LangB = ""
+			content.Network = nil
+			content.Channel = nil
+
+			if producer := r.Site.Content.Producer; producer != nil {
+				producer.CatTax = 0
+			}
+		}
+
+		if publisher := r.Site.Publisher; publisher != nil {
+			publisher.CatTax = 0
+		}
+	}
+
+	if device := r.Device; device != nil {
+		device.SUA = nil
+		device.LangB = ""
+	}
+
+	if regs := r.Regs; regs != nil {
+		regs.GDPR = nil
+		regs.USPrivacy = ""
+	}
+
+	if source := r.Source; source != nil {
+		source.SChain = nil
+	}
+
+	if user := r.User; user != nil {
+		user.KwArray = nil
+		user.Consent = ""
+		user.EIDs = nil
+	}
+
+	for _, imp := range r.GetImp() {
+		imp.Rwdd = 0
+		imp.SSAI = 0
+
+		if audio := imp.Audio; audio != nil {
+			audio.PodDur = 0
+			audio.RqdDurs = nil
+			audio.PodID = 0
+			audio.PodSeq = 0
+			audio.SlotInPod = 0
+			audio.MinCPMPerSec = 0
+		}
+
+		if video := imp.Video; video != nil {
+			video.MaxSeq = 0
+			video.PodDur = 0
+			video.PodID = 0
+			video.PodSeq = 0
+			video.RqdDurs = nil
+			video.SlotInPod = 0
+			video.MinCPMPerSec = 0
+		}
+	}
+}
+
+// clear202211Fields sets all fields introduced in OpenRTB 2.6-202211 to default values
+// which will cause them to omitted during json marshal.
+func clear202211Fields(r *RequestWrapper) {
+	r.DOOH = nil
+
+	if app := r.App; app != nil {
+		app.InventoryPartnerDomain = ""
+	}
+
+	if site := r.Site; site != nil {
+		site.InventoryPartnerDomain = ""
+	}
+
+	if regs := r.Regs; regs != nil {
+		regs.GPP = ""
+		regs.GPPSID = nil
+	}
+
+	for _, imp := range r.GetImp() {
+		imp.Qty = nil
+		imp.DT = 0
+	}
+}
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index 6559e632..bc42769e 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -2,7 +2,6 @@ package openrtb_ext
 
 import (
 	"encoding/json"
-	"errors"
 
 	"github.com/prebid/openrtb/v17/openrtb2"
 )
@@ -118,8 +117,8 @@ type ExtRequestPrebidChannel struct {
 
 // ExtRequestPrebidCache defines the contract for bidrequest.ext.prebid.cache
 type ExtRequestPrebidCache struct {
-	Bids    *ExtRequestPrebidCacheBids `json:"bids"`
-	VastXML *ExtRequestPrebidCacheVAST `json:"vastxml"`
+	Bids    *ExtRequestPrebidCacheBids `json:"bids,omitempty"`
+	VastXML *ExtRequestPrebidCacheVAST `json:"vastxml,omitempty"`
 }
 
 type ExtRequestPrebidServer struct {
@@ -130,23 +129,23 @@ type ExtRequestPrebidServer struct {
 
 // ExtRequestPrebidCacheBids defines the contract for bidrequest.ext.prebid.cache.bids
 type ExtRequestPrebidCacheBids struct {
-	ReturnCreative *bool `json:"returnCreative"`
+	ReturnCreative *bool `json:"returnCreative,omitempty"`
 }
 
 // ExtRequestPrebidCacheVAST defines the contract for bidrequest.ext.prebid.cache.vastxml
 type ExtRequestPrebidCacheVAST struct {
-	ReturnCreative *bool `json:"returnCreative"`
+	ReturnCreative *bool `json:"returnCreative,omitempty"`
 }
 
 // ExtRequestTargeting defines the contract for bidrequest.ext.prebid.targeting
 type ExtRequestTargeting struct {
-	PriceGranularity     PriceGranularity         `json:"pricegranularity"`
-	IncludeWinners       bool                     `json:"includewinners"`
-	IncludeBidderKeys    bool                     `json:"includebidderkeys"`
-	IncludeBrandCategory *ExtIncludeBrandCategory `json:"includebrandcategory"`
-	IncludeFormat        bool                     `json:"includeformat"`
-	DurationRangeSec     []int                    `json:"durationrangesec"`
-	PreferDeals          bool                     `json:"preferdeals"`
+	PriceGranularity     *PriceGranularity        `json:"pricegranularity,omitempty"`
+	IncludeWinners       *bool                    `json:"includewinners,omitempty"`
+	IncludeBidderKeys    *bool                    `json:"includebidderkeys,omitempty"`
+	IncludeBrandCategory *ExtIncludeBrandCategory `json:"includebrandcategory,omitempty"`
+	IncludeFormat        bool                     `json:"includeformat,omitempty"`
+	DurationRangeSec     []int                    `json:"durationrangesec,omitempty"`
+	PreferDeals          bool                     `json:"preferdeals,omitempty"`
 	AppendBidderNames    bool                     `json:"appendbiddernames,omitempty"`
 }
 
@@ -157,34 +156,9 @@ type ExtIncludeBrandCategory struct {
 	TranslateCategories *bool  `json:"translatecategories,omitempty"`
 }
 
-// Make an unmarshaller that will set a default PriceGranularity
-func (ert *ExtRequestTargeting) UnmarshalJSON(b []byte) error {
-	if string(b) == "null" {
-		return nil
-	}
-
-	// define separate type to prevent infinite recursive calls to UnmarshalJSON
-	type extRequestTargetingDefaults ExtRequestTargeting
-	defaults := &extRequestTargetingDefaults{
-		PriceGranularity:  priceGranularityMed,
-		IncludeWinners:    true,
-		IncludeBidderKeys: true,
-	}
-
-	err := json.Unmarshal(b, defaults)
-	if err == nil {
-		if !defaults.IncludeWinners && !defaults.IncludeBidderKeys {
-			return errors.New("ext.prebid.targeting: At least one of includewinners or includebidderkeys must be enabled to enable targeting support")
-		}
-		*ert = ExtRequestTargeting(*defaults)
-	}
-
-	return err
-}
-
 // PriceGranularity defines the allowed values for bidrequest.ext.prebid.targeting.pricegranularity
 type PriceGranularity struct {
-	Precision int                `json:"precision,omitempty"`
+	Precision *int               `json:"precision,omitempty"`
 	Ranges    []GranularityRange `json:"ranges,omitempty"`
 }
 
@@ -197,141 +171,109 @@ type GranularityRange struct {
 	Increment float64 `json:"increment"`
 }
 
-// UnmarshalJSON : custom unmarshaller to handle legacy string granularites.
 func (pg *PriceGranularity) UnmarshalJSON(b []byte) error {
-	// We default to medium
-	if len(b) == 0 {
-		*pg = priceGranularityMed
-		return nil
-	}
-	// First check for legacy strings
-	var pgString string
-	err := json.Unmarshal(b, &pgString)
-	if err == nil {
-		*pg = PriceGranularityFromString(pgString)
-		if len(pg.Ranges) > 0 {
-			// Only exit if we matched something, else we try processing as custom granularity
-			// This way we error as expecting the new custom granularity standard.
+	// price granularity used to be a string referencing a predefined value, try to parse
+	// and map the legacy string before falling back to the modern custom model.
+	legacyID := ""
+	if err := json.Unmarshal(b, &legacyID); err == nil {
+		if legacyValue, ok := NewPriceGranularityFromLegacyID(legacyID); ok {
+			*pg = legacyValue
 			return nil
 		}
 	}
-	// Not legacy, so we do a normal Unmarshal
-	pgraw := PriceGranularityRaw{}
-	pgraw.Precision = 2
-	err = json.Unmarshal(b, &pgraw)
-	if err != nil {
-		return err
-	}
-	if pgraw.Precision < 0 {
-		return errors.New("Price granularity error: precision must be non-negative")
-	}
-	if pgraw.Precision > MaxDecimalFigures {
-		return errors.New("Price granularity error: precision of more than 15 significant figures is not supported")
-	}
-	if len(pgraw.Ranges) > 0 {
-		var prevMax float64 = 0
-		for i, gr := range pgraw.Ranges {
-			if gr.Max <= prevMax {
-				return errors.New("Price granularity error: range list must be ordered with increasing \"max\"")
-			}
-			if gr.Increment <= 0.0 {
-				return errors.New("Price granularity error: increment must be a nonzero positive number")
-			}
-			// Enforce that we don't read "min" from the request
-			pgraw.Ranges[i].Min = prevMax
-			prevMax = gr.Max
-		}
-		*pg = PriceGranularity(pgraw)
-		return nil
+
+	// use a type-alias to avoid calling back into this UnmarshalJSON implementation
+	modernValue := PriceGranularityRaw{}
+	err := json.Unmarshal(b, &modernValue)
+	if err == nil {
+		*pg = (PriceGranularity)(modernValue)
 	}
-	// Default to medium if no ranges are specified
-	*pg = priceGranularityMed
-	return nil
+	return err
 }
 
-// PriceGranularityFromString converts a legacy string into the new PriceGranularity
-func PriceGranularityFromString(gran string) PriceGranularity {
-	switch gran {
+func NewPriceGranularityDefault() PriceGranularity {
+	pg, _ := NewPriceGranularityFromLegacyID("medium")
+	return pg
+}
+
+// NewPriceGranularityFromLegacyID converts a legacy string into the new PriceGranularity structure.
+func NewPriceGranularityFromLegacyID(v string) (PriceGranularity, bool) {
+	precision2 := 2
+
+	switch v {
 	case "low":
-		return priceGranularityLow
+		return PriceGranularity{
+			Precision: &precision2,
+			Ranges: []GranularityRange{{
+				Min:       0,
+				Max:       5,
+				Increment: 0.5}},
+		}, true
+
 	case "med", "medium":
-		// Seems that PBS was written with medium = "med", so hacking that in
-		return priceGranularityMed
+		return PriceGranularity{
+			Precision: &precision2,
+			Ranges: []GranularityRange{{
+				Min:       0,
+				Max:       20,
+				Increment: 0.1}},
+		}, true
+
 	case "high":
-		return priceGranularityHigh
+		return PriceGranularity{
+			Precision: &precision2,
+			Ranges: []GranularityRange{{
+				Min:       0,
+				Max:       20,
+				Increment: 0.01}},
+		}, true
+
 	case "auto":
-		return priceGranularityAuto
+		return PriceGranularity{
+			Precision: &precision2,
+			Ranges: []GranularityRange{
+				{
+					Min:       0,
+					Max:       5,
+					Increment: 0.05,
+				},
+				{
+					Min:       5,
+					Max:       10,
+					Increment: 0.1,
+				},
+				{
+					Min:       10,
+					Max:       20,
+					Increment: 0.5,
+				},
+			},
+		}, true
+
 	case "dense":
-		return priceGranularityDense
+		return PriceGranularity{
+			Precision: &precision2,
+			Ranges: []GranularityRange{
+				{
+					Min:       0,
+					Max:       3,
+					Increment: 0.01,
+				},
+				{
+					Min:       3,
+					Max:       8,
+					Increment: 0.05,
+				},
+				{
+					Min:       8,
+					Max:       20,
+					Increment: 0.5,
+				},
+			},
+		}, true
 	}
-	// Return empty if not matched
-	return PriceGranularity{}
-}
-
-var priceGranularityLow = PriceGranularity{
-	Precision: 2,
-	Ranges: []GranularityRange{{
-		Min:       0,
-		Max:       5,
-		Increment: 0.5}},
-}
-
-var priceGranularityMed = PriceGranularity{
-	Precision: 2,
-	Ranges: []GranularityRange{{
-		Min:       0,
-		Max:       20,
-		Increment: 0.1}},
-}
-
-var priceGranularityHigh = PriceGranularity{
-	Precision: 2,
-	Ranges: []GranularityRange{{
-		Min:       0,
-		Max:       20,
-		Increment: 0.01}},
-}
-
-var priceGranularityDense = PriceGranularity{
-	Precision: 2,
-	Ranges: []GranularityRange{
-		{
-			Min:       0,
-			Max:       3,
-			Increment: 0.01,
-		},
-		{
-			Min:       3,
-			Max:       8,
-			Increment: 0.05,
-		},
-		{
-			Min:       8,
-			Max:       20,
-			Increment: 0.5,
-		},
-	},
-}
 
-var priceGranularityAuto = PriceGranularity{
-	Precision: 2,
-	Ranges: []GranularityRange{
-		{
-			Min:       0,
-			Max:       5,
-			Increment: 0.05,
-		},
-		{
-			Min:       5,
-			Max:       10,
-			Increment: 0.1,
-		},
-		{
-			Min:       10,
-			Max:       20,
-			Increment: 0.5,
-		},
-	},
+	return PriceGranularity{}, false
 }
 
 // ExtRequestPrebidData defines Prebid's First Party Data (FPD) and related bid request options.
diff --git a/openrtb_ext/request_wrapper.go b/openrtb_ext/request_wrapper.go
index 526d86f0..aa301803 100644
--- a/openrtb_ext/request_wrapper.go
+++ b/openrtb_ext/request_wrapper.go
@@ -712,7 +712,7 @@ func (re *RequestExt) SetExt(ext map[string]json.RawMessage) {
 }
 
 func (re *RequestExt) GetPrebid() *ExtRequestPrebid {
-	if re.prebid == nil {
+	if re == nil || re.prebid == nil {
 		return nil
 	}
 	prebid := *re.prebid
diff --git a/ortb/default.go b/ortb/default.go
new file mode 100644
index 00000000..2f4255d3
--- /dev/null
+++ b/ortb/default.go
@@ -0,0 +1,87 @@
+package ortb
+
+import (
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/util/ptrutil"
+)
+
+const (
+	DefaultPriceGranularityPrecision  = 2
+	DefaultTargetingIncludeWinners    = true
+	DefaultTargetingIncludeBidderKeys = true
+)
+
+func SetDefaults(r *openrtb_ext.RequestWrapper) error {
+	requestExt, err := r.GetRequestExt()
+	if err != nil {
+		return err
+	}
+
+	requestExtPrebid := requestExt.GetPrebid()
+	if requestExtPrebid != nil {
+		modified := setDefaultsTargeting(requestExtPrebid.Targeting)
+
+		if modified {
+			requestExt.SetPrebid(requestExtPrebid)
+		}
+	}
+
+	return nil
+}
+
+func setDefaultsTargeting(targeting *openrtb_ext.ExtRequestTargeting) bool {
+	if targeting == nil {
+		return false
+	}
+
+	modified := false
+
+	if targeting.PriceGranularity == nil || len(targeting.PriceGranularity.Ranges) == 0 {
+		targeting.PriceGranularity = ptrutil.ToPtr(openrtb_ext.NewPriceGranularityDefault())
+		modified = true
+	} else if setDefaultsPriceGranularity(targeting.PriceGranularity) {
+		modified = true
+	}
+
+	if targeting.IncludeWinners == nil {
+		targeting.IncludeWinners = ptrutil.ToPtr(DefaultTargetingIncludeWinners)
+		modified = true
+	}
+
+	if targeting.IncludeBidderKeys == nil {
+		targeting.IncludeBidderKeys = ptrutil.ToPtr(DefaultTargetingIncludeBidderKeys)
+		modified = true
+	}
+
+	return modified
+}
+
+func setDefaultsPriceGranularity(pg *openrtb_ext.PriceGranularity) bool {
+	modified := false
+
+	if pg.Precision == nil {
+		pg.Precision = ptrutil.ToPtr(DefaultPriceGranularityPrecision)
+		modified = true
+	}
+
+	if setDefaultsPriceGranularityRange(pg.Ranges) {
+		modified = true
+	}
+
+	return modified
+}
+
+func setDefaultsPriceGranularityRange(ranges []openrtb_ext.GranularityRange) bool {
+	modified := false
+
+	var prevMax float64 = 0
+	for i, r := range ranges {
+		if ranges[i].Min != prevMax {
+			ranges[i].Min = prevMax
+			modified = true
+		}
+		prevMax = r.Max
+	}
+
+	return modified
+}
diff --git a/static/bidder-info/beyondmedia.yaml b/static/bidder-info/beyondmedia.yaml
index 4fb782b7..eb0d239f 100644
--- a/static/bidder-info/beyondmedia.yaml
+++ b/static/bidder-info/beyondmedia.yaml
@@ -13,3 +13,7 @@ capabilities:
     - banner
     - video
     - native
+userSync:
+  redirect:
+    url: "https://cookies.andbeyond.media/pbserver?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&ccpa={{.USPrivacy}}&redir={{.RedirectURL}}"
+    userMacro: "[UID]"
diff --git a/static/bidder-info/criteo.yaml b/static/bidder-info/criteo.yaml
index a522a144..3957ae75 100644
--- a/static/bidder-info/criteo.yaml
+++ b/static/bidder-info/criteo.yaml
@@ -1,4 +1,4 @@
-endpoint: "https://bidder.criteo.com/cdb?profileId=230"
+endpoint: "https://bidder.criteo.com/openrtb/prebid/auction/request?profile=230"
 maintainer:
   email: "prebid@criteo.com"
 gvlVendorID: 91
diff --git a/static/bidder-info/nextmillennium.yaml b/static/bidder-info/nextmillennium.yaml
index e6f498ca..a6712846 100644
--- a/static/bidder-info/nextmillennium.yaml
+++ b/static/bidder-info/nextmillennium.yaml
@@ -12,3 +12,6 @@ userSync:
   iframe:
     url: "https://cookies.nextmillmedia.com/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}"
     userMacro: "[NMUID]"
+  redirect:
+    url: "https://cookies.nextmillmedia.com/sync?type=image&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}"
+    userMacro: "[NMUID]"
diff --git a/util/ptrutil/ptrutil.go b/util/ptrutil/ptrutil.go
new file mode 100644
index 00000000..999ac34a
--- /dev/null
+++ b/util/ptrutil/ptrutil.go
@@ -0,0 +1,5 @@
+package ptrutil
+
+func ToPtr[T any](v T) *T {
+	return &v
+}


Test patch:
diff --git a/adapters/criteo/criteo_test.go b/adapters/criteo/criteo_test.go
index 23e9633b..ea42bbda 100644
--- a/adapters/criteo/criteo_test.go
+++ b/adapters/criteo/criteo_test.go
@@ -1,24 +1,17 @@
 package criteo
 
 import (
-	"testing"
-
 	"github.com/prebid/prebid-server/adapters/adapterstest"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
+	"testing"
 )
 
 func TestJsonSamples(t *testing.T) {
 
-	// Setup:
-	bidder, buildErr := builderWithGuidGenerator(
-		openrtb_ext.BidderCriteo,
-		config.Adapter{
-			Endpoint: "https://bidder.criteo.com/cdb?profileId=230",
-		},
-		config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"},
-		newFakeGuidGenerator("00000000-0000-0000-00000000"),
-	)
+	bidder, buildErr := Builder(openrtb_ext.BidderCriteo, config.Adapter{
+		Endpoint: "https://bidder.criteo.com/openrtb/prebid/auction/request?profile=230"},
+		config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
diff --git a/adapters/criteo/criteotest/exemplary/simple-banner-cookie-uid.json b/adapters/criteo/criteotest/exemplary/simple-banner-cookie-uid.json
index cb117950..3692ed9a 100755
--- a/adapters/criteo/criteotest/exemplary/simple-banner-cookie-uid.json
+++ b/adapters/criteo/criteotest/exemplary/simple-banner-cookie-uid.json
@@ -36,36 +36,39 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://bidder.criteo.com/cdb?profileId=230",
-        "headers": {
-          "X-Forwarded-For": ["91.199.242.236"],
-          "User-Agent": ["random user agent"],
-          "Cookie": ["uid=criteo-user-id"]
-        },
+        "uri": "https://bidder.criteo.com/openrtb/prebid/auction/request?profile=230",
+        "headers": {},
         "body": {
           "id": "test-request-id",
-          "publisher": {
-            "siteid": "site-id",
-            "url": "criteo.com",
-            "networkid": 78910
+          "site": {
+            "id": "site-id",
+            "page": "criteo.com"
           },
-          "user": {
-            "cookieuid": "criteo-user-id",
+          "device": {
+            "os": "android",
             "ip": "91.199.242.236",
-            "ua": "random user agent",
-            "deviceos": "android",
-            "deviceidtype": "gaid"
+            "ua": "random user agent"
           },
-          "gdprconsent": {},
-          "slots": [
+          "user": {
+            "buyeruid": "criteo-user-id"
+          },
+          "imp": [
             {
-              "slotid": "00000000-0000-0000-00000000",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "sizes": [
-                "300x250"
-              ]
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "zoneid": 123456,
+                  "networkid": 78910
+                }
+              }
             }
           ]
         }
@@ -74,18 +77,20 @@
         "status": 200,
         "body": {
           "id": "test-request-id",
-          "slots": [
+          "currency": "USD",
+          "seatbid": [
             {
-              "arbitrageid": "test-slot-id",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "cpm": 0.1,
-              "currency": "USD",
-              "width": 300,
-              "height": 250,
-              "creativecode": "creative-123",
-              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              "bid": [
+                {
+                  "id": "test-slot-id",
+                  "impid": "test-imp-id",
+                  "price": 0.1,
+                  "crid": "creative-123",
+                  "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+                  "w": 300,
+                  "h": 250
+                }
+              ]
             }
           ]
         }
diff --git a/adapters/criteo/criteotest/exemplary/simple-banner-inapp.json b/adapters/criteo/criteotest/exemplary/simple-banner-inapp.json
index 3dec57ff..0e03914b 100755
--- a/adapters/criteo/criteotest/exemplary/simple-banner-inapp.json
+++ b/adapters/criteo/criteotest/exemplary/simple-banner-inapp.json
@@ -1,92 +1,95 @@
 {
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "app": {
-        "bundle": "test.app.bundle"
-      },
-      "device": {
-        "ifa": "test-ifa-123456",
-        "os": "android",
-        "ip": "91.199.242.236",
-        "ua": "random user agent"
-      },
-      "imp": [
-        {
-          "id": "test-imp-id",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app": {
+      "bundle": "test.app.bundle"
+    },
+    "device": {
+      "ifa": "test-ifa-123456",
+      "os": "android",
+      "ip": "91.199.242.236",
+      "ua": "random user agent"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
             }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "zoneid": 123456,
+            "networkid": 78910
           }
         }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": ["91.199.242.236"],
-            "User-Agent": ["random user agent"]
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bidder.criteo.com/openrtb/prebid/auction/request?profile=230",
+        "headers": {},
+        "body": {
+          "id": "test-request-id",
+          "app": {
+            "bundle": "test.app.bundle"
+          },
+          "device": {
+            "ifa": "test-ifa-123456",
+            "os": "android",
+            "ip": "91.199.242.236",
+            "ua": "random user agent"
           },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "bundleid": "test.app.bundle",
-              "networkid": 78910
-            },
-            "user": {
-              "deviceid": "test-ifa-123456",
-              "deviceos": "android",
-              "deviceidtype": "gaid",
-              "ip": "91.199.242.236",
-              "ua": "random user agent"
-            },
-            "gdprconsent": {},
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  }
                 ]
+              },
+              "ext": {
+                "bidder": {
+                  "zoneid": 123456,
+                  "networkid": 78910
+                }
               }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 200,
-          "body": {
-            "id": "test-request-id",
-            "slots": [
-              {
-                "arbitrageid": "test-slot-id",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "cpm": 0.1,
-                "currency": "USD",
-                "width": 300,
-                "height": 250,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              }
-            ]
-          }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "currency": "USD",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test-slot-id",
+                  "impid": "test-imp-id",
+                  "price": 0.1,
+                  "crid": "creative-123",
+                  "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ]
         }
       }
-    ],
+    }
+  ],
   "expectedBidResponses": [
     {
       "currency": "USD",
diff --git a/adapters/criteo/criteotest/exemplary/simple-banner-multiple-bids.json b/adapters/criteo/criteotest/exemplary/simple-banner-multiple-bids.json
new file mode 100644
index 00000000..c0aa0bb4
--- /dev/null
+++ b/adapters/criteo/criteotest/exemplary/simple-banner-multiple-bids.json
@@ -0,0 +1,200 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id-multiple-bids",
+    "site": {
+      "id": "site-id",
+      "page": "criteo.com"
+    },
+    "device": {
+      "os": "android",
+      "ip": "91.199.242.236",
+      "ua": "random user agent"
+    },
+    "user": {
+      "ext": {
+        "eids": [
+          {
+            "source": "criteo.com",
+            "uids": [
+              {
+                "id": "criteo-eid"
+              }
+            ]
+          }
+        ]
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "zoneid": 123456,
+            "networkid": 78910
+          }
+        }
+      },
+      {
+        "id": "test-imp-id2",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "zoneid": 12345,
+            "networkid": 7891
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bidder.criteo.com/openrtb/prebid/auction/request?profile=230",
+        "headers": {},
+        "body": {
+          "id": "test-request-id-multiple-bids",
+          "site": {
+            "id": "site-id",
+            "page": "criteo.com"
+          },
+          "device": {
+            "os": "android",
+            "ip": "91.199.242.236",
+            "ua": "random user agent"
+          },
+          "user": {
+            "ext": {
+              "eids": [
+                {
+                  "source": "criteo.com",
+                  "uids": [
+                    {
+                      "id": "criteo-eid"
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "zoneid": 123456,
+                  "networkid": 78910
+                }
+              }
+            },
+            {
+              "id": "test-imp-id2",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "zoneid": 12345,
+                  "networkid": 7891
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "currency": "USD",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test-slot-id",
+                  "impid": "test-imp-id",
+                  "price": 0.1,
+                  "crid": "creative-123",
+                  "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            },
+            {
+              "bid": [
+                {
+                  "id": "test-slot-id2",
+                  "impid": "test-imp-id2",
+                  "price": 0.5,
+                  "crid": "creative-123",
+                  "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-slot-id",
+            "impid": "test-imp-id",
+            "price": 0.1,
+            "crid": "creative-123",
+            "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        },
+        {
+          "bid": {
+            "id": "test-slot-id2",
+            "impid": "test-imp-id2",
+            "price": 0.5,
+            "crid": "creative-123",
+            "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/adapters/criteo/criteotest/exemplary/simple-banner-uid.json b/adapters/criteo/criteotest/exemplary/simple-banner-uid.json
index ea453dee..c935c2a5 100755
--- a/adapters/criteo/criteotest/exemplary/simple-banner-uid.json
+++ b/adapters/criteo/criteotest/exemplary/simple-banner-uid.json
@@ -11,15 +11,17 @@
       "ua": "random user agent"
     },
     "user": {
-      "ip": "91.199.242.236",
-      "ua": "random user agent",
       "ext": {
-        "eids": [{
-          "source": "criteo.com",
-          "uids": [{
-            "id": "criteo-eid"
-          }]
-        }]
+        "eids": [
+          {
+            "source": "criteo.com",
+            "uids": [
+              {
+                "id": "criteo-eid"
+              }
+            ]
+          }
+        ]
       }
     },
     "imp": [
@@ -45,44 +47,50 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://bidder.criteo.com/cdb?profileId=230",
-        "headers": {
-          "X-Forwarded-For": ["91.199.242.236"],
-          "User-Agent": ["random user agent"]
-        },
+        "uri": "https://bidder.criteo.com/openrtb/prebid/auction/request?profile=230",
+        "headers": {},
         "body": {
           "id": "test-request-id",
-          "publisher": {
-            "siteid": "site-id",
-            "url": "criteo.com",
-            "networkid": 78910
+          "site": {
+            "id": "site-id",
+            "page": "criteo.com"
           },
-          "user": {
+          "device": {
+            "os": "android",
             "ip": "91.199.242.236",
-            "ua": "random user agent",
-            "deviceos": "android",
-            "deviceidtype": "gaid"
+            "ua": "random user agent"
           },
-          "gdprconsent": {},
-          "slots": [
-            {
-              "slotid": "00000000-0000-0000-00000000",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "sizes": [
-                "300x250"
+          "user": {
+            "ext": {
+              "eids": [
+                {
+                  "source": "criteo.com",
+                  "uids": [
+                    {
+                      "id": "criteo-eid"
+                    }
+                  ]
+                }
               ]
             }
-          ],
-          "eids": [
+          },
+          "imp": [
             {
-              "source": "criteo.com",
-              "uids": [
-                {
-                  "id": "criteo-eid"
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "zoneid": 123456,
+                  "networkid": 78910
                 }
-              ]
+              }
             }
           ]
         }
@@ -90,19 +98,20 @@
       "mockResponse": {
         "status": 200,
         "body": {
-          "id": "test-request-id",
-          "slots": [
+          "currency": "USD",
+          "seatbid": [
             {
-              "arbitrageid": "test-slot-id",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "cpm": 0.1,
-              "currency": "USD",
-              "width": 300,
-              "height": 250,
-              "creativecode": "creative-123",
-              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              "bid": [
+                {
+                  "id": "test-slot-id",
+                  "impid": "test-imp-id",
+                  "price": 0.1,
+                  "crid": "creative-123",
+                  "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+                  "w": 300,
+                  "h": 250
+                }
+              ]
             }
           ]
         }
diff --git a/adapters/criteo/criteotest/supplemental/204-response-from-target.json b/adapters/criteo/criteotest/supplemental/204-response-from-target.json
index 9ac14ff6..c8393ec2 100755
--- a/adapters/criteo/criteotest/supplemental/204-response-from-target.json
+++ b/adapters/criteo/criteotest/supplemental/204-response-from-target.json
@@ -38,35 +38,41 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": ["91.199.242.236"],
-            "User-Agent": ["random user agent"]
-          },
+          "uri": "https://bidder.criteo.com/openrtb/prebid/auction/request?profile=230",
+          "headers": {},
           "body": {
             "id": "test-request-id",
-            "publisher": {
-              "bundleid": "test.app.bundle",
-              "networkid": 78910
+            "app": {
+              "bundle": "test.app.bundle"
             },
-            "user": {
-              "deviceid": "test-ifa-123456",
-              "deviceos": "android",
-              "deviceidtype": "gaid",
-              "uspIab": "1YYY",
+            "device": {
+              "ifa": "test-ifa-123456",
               "ip": "91.199.242.236",
-              "ua": "random user agent"
+              "ua": "random user agent",
+              "os": "android"
+            },
+            "regs": {
+              "ext": {
+                "us_privacy": "1YYY"
+              }
             },
-            "gdprconsent": {},
-            "slots": [
+            "imp": [
               {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
+                "id": "test-imp-id",
+                "banner": {
+                  "format": [
+                    {
+                      "w": 300,
+                      "h": 250
+                    }
+                  ]
+                },
+                "ext": {
+                  "bidder": {
+                    "zoneid": 123456,
+                    "networkid": 78910
+                  }
+                }
               }
             ]
           }
diff --git a/adapters/criteo/criteotest/supplemental/400-response-from-target.json b/adapters/criteo/criteotest/supplemental/400-response-from-target.json
index ce3a4b62..cd670062 100755
--- a/adapters/criteo/criteotest/supplemental/400-response-from-target.json
+++ b/adapters/criteo/criteotest/supplemental/400-response-from-target.json
@@ -38,35 +38,41 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": ["91.199.242.236"],
-            "User-Agent": ["random user agent"]
-          },
+          "uri": "https://bidder.criteo.com/openrtb/prebid/auction/request?profile=230",
+          "headers": {},
           "body": {
             "id": "test-request-id",
-            "publisher": {
-              "bundleid": "test.app.bundle",
-              "networkid": 78910
+            "app": {
+              "bundle": "test.app.bundle"
             },
-            "user": {
-              "deviceid": "test-ifa-123456",
-              "deviceos": "android",
-              "deviceidtype": "gaid",
-              "uspIab": "1YYY",
+            "device": {
+              "ifa": "test-ifa-123456",
               "ip": "91.199.242.236",
-              "ua": "random user agent"
+              "ua": "random user agent",
+              "os": "android"
+            },
+            "regs": {
+              "ext": {
+                "us_privacy": "1YYY"
+              }
             },
-            "gdprconsent": {},
-            "slots": [
+            "imp": [
               {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
+                "id": "test-imp-id",
+                "banner": {
+                  "format": [
+                    {
+                      "w": 300,
+                      "h": 250
+                    }
+                  ]
+                },
+                "ext": {
+                  "bidder": {
+                    "zoneid": 123456,
+                    "networkid": 78910
+                  }
+                }
               }
             ]
           }
@@ -78,7 +84,7 @@
     ],
     "expectedMakeBidsErrors": [
       {
-        "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+        "value": "Unexpected status code: 400. Run with request.debug = 1 for more info.",
         "comparison": "literal"
       }
     ]
diff --git a/adapters/criteo/criteotest/supplemental/500-response-from-target.json b/adapters/criteo/criteotest/supplemental/500-response-from-target.json
index db6c16a3..cc98b6cb 100755
--- a/adapters/criteo/criteotest/supplemental/500-response-from-target.json
+++ b/adapters/criteo/criteotest/supplemental/500-response-from-target.json
@@ -38,35 +38,41 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": ["91.199.242.236"],
-            "User-Agent": ["random user agent"]
-          },
+          "uri": "https://bidder.criteo.com/openrtb/prebid/auction/request?profile=230",
+          "headers": {},
           "body": {
             "id": "test-request-id",
-            "publisher": {
-              "bundleid": "test.app.bundle",
-              "networkid": 78910
+            "app": {
+              "bundle": "test.app.bundle"
             },
-            "user": {
-              "deviceid": "test-ifa-123456",
-              "deviceos": "android",
-              "deviceidtype": "gaid",
-              "uspIab": "1YYY",
+            "device": {
+              "ifa": "test-ifa-123456",
               "ip": "91.199.242.236",
-              "ua": "random user agent"
+              "ua": "random user agent",
+              "os": "android"
+            },
+            "regs": {
+              "ext": {
+                "us_privacy": "1YYY"
+              }
             },
-            "gdprconsent": {},
-            "slots": [
+            "imp": [
               {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
+                "id": "test-imp-id",
+                "banner": {
+                  "format": [
+                    {
+                      "w": 300,
+                      "h": 250
+                    }
+                  ]
+                },
+                "ext": {
+                  "bidder": {
+                    "zoneid": 123456,
+                    "networkid": 78910
+                  }
+                }
               }
             ]
           }
@@ -78,7 +84,7 @@
     ],
     "expectedMakeBidsErrors": [
       {
-        "value": "Unexpected status code: 500. Run with request.debug = 1 for more info",
+        "value": "Unexpected status code: 500. Run with request.debug = 1 for more info.",
         "comparison": "literal"
       }
     ]
diff --git a/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-inapp.json b/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-inapp.json
deleted file mode 100755
index febc3e7d..00000000
--- a/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-inapp.json
+++ /dev/null
@@ -1,114 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "app": {
-        "bundle": "test.app.bundle"
-      },
-      "device": {
-        "ifa": "test-ifa-123456",
-        "ip": "91.199.242.236",
-        "ua": "random user agent",
-        "os": "android"
-      },
-      "regs": {
-        "ext": {
-          "us_privacy": "1YYY"
-        }
-      },
-      "imp": [
-        {
-          "id": "test-imp-id",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
-            }
-          }
-        }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": ["91.199.242.236"],
-            "User-Agent": ["random user agent"]
-          },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "bundleid": "test.app.bundle",
-              "networkid": 78910
-            },
-            "user": {
-              "deviceid": "test-ifa-123456",
-              "deviceos": "android",
-              "deviceidtype": "gaid",
-              "uspIab": "1YYY",
-              "ip": "91.199.242.236",
-              "ua": "random user agent"
-            },
-            "gdprconsent": {},
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 200,
-          "body": {
-            "slots": [
-              {
-                "arbitrageid": "test-slot-id",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "cpm": 0.1,
-                "currency": "USD",
-                "width": 300,
-                "height": 250,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              }
-            ]
-          }
-        }
-      }
-    ],
-    "expectedBidResponses": [
-      {
-        "currency": "USD",
-        "bids": [
-          {
-            "bid": {
-              "id": "test-slot-id",
-              "impid": "test-imp-id",
-              "price": 0.1,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 250
-            },
-            "type": "banner"
-          }
-        ]
-      }
-    ]
-  }
diff --git a/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-uid.json b/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-uid.json
deleted file mode 100755
index 4046b641..00000000
--- a/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-uid.json
+++ /dev/null
@@ -1,138 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "site": {
-        "id": "site-id",
-        "page": "criteo.com"
-      },
-      "device": {
-        "os": "android",
-        "ip": "91.199.242.236",
-        "ua": "random user agent"
-      },
-      "user": {
-        "ext": {
-          "eids": [{
-            "source": "criteo.com",
-            "uids": [{
-              "id": "criteo-eid"
-            }]
-          }]
-        }
-      },
-      "regs": {
-        "ext": {
-          "us_privacy": "1YYY"
-        }
-      },
-      "imp": [
-        {
-          "id": "test-imp-id",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
-            }
-          }
-        }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": [
-              "91.199.242.236"
-            ],
-            "User-Agent": [
-              "random user agent"
-            ]
-          },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "siteid": "site-id",
-              "url": "criteo.com",
-              "networkid": 78910
-            },
-            "user": {
-              "ip": "91.199.242.236",
-              "ua": "random user agent",
-              "deviceos": "android",
-              "deviceidtype": "gaid",
-              "uspIab": "1YYY"
-            },
-            "gdprconsent": {},
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              }
-            ],
-            "eids": [
-              {
-                "source": "criteo.com",
-                "uids": [
-                  {
-                    "id": "criteo-eid"
-                  }
-                ]
-              }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 200,
-          "body": {
-            "slots": [
-              {
-                "arbitrageid": "test-slot-id",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "cpm": 0.1,
-                "currency": "USD",
-                "width": 300,
-                "height": 250,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              }
-            ]
-          }
-        }
-      }
-    ],
-    "expectedBidResponses": [
-      {
-        "currency": "USD",
-        "bids": [
-          {
-            "bid": {
-              "id": "test-slot-id",
-              "impid": "test-imp-id",
-              "price": 0.1,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 250
-            },
-            "type": "banner"
-          }
-        ]
-      }
-    ]
-  }
diff --git a/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-inapp.json b/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-inapp.json
deleted file mode 100755
index 77a37361..00000000
--- a/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-inapp.json
+++ /dev/null
@@ -1,85 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "app": {
-        "bundle": "test.app.bundle"
-      },
-      "device": {
-        "ifa": "test-ifa-123456",
-        "ip": "91.199.242.236",
-        "ua": "random user agent",
-        "os": "android"
-      },
-      "regs": {
-        "ext": {
-          "us_privacy": "1YNN"
-        }
-      },
-      "imp": [
-        {
-          "id": "test-imp-id",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
-            }
-          }
-        }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": [
-              "91.199.242.236"
-            ],
-            "User-Agent": [
-              "random user agent"
-            ]
-          },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "bundleid": "test.app.bundle",
-              "networkid": 78910
-            },
-            "user": {
-              "deviceid": "test-ifa-123456",
-              "deviceos": "android",
-              "deviceidtype": "gaid",
-              "uspIab": "1YNN",
-              "ip": "91.199.242.236",
-              "ua": "random user agent"
-            },
-            "gdprconsent": {},
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 204
-        }
-      }
-    ],
-    "expectedBidResponses": []
-  }
-  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-uid.json b/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-uid.json
deleted file mode 100755
index 31c14fc4..00000000
--- a/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-uid.json
+++ /dev/null
@@ -1,105 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "site": {
-        "id": "site-id",
-        "page": "criteo.com"
-      },
-      "device": {
-        "os": "android",
-        "ip": "91.199.242.236",
-        "ua": "random user agent"
-      },
-      "user": {
-        "ext": {
-          "eids": [{
-            "source": "criteo.com",
-            "uids": [{
-              "id": "criteo-eid"
-            }]
-          }]
-        }
-      },
-      "regs": {
-        "ext": {
-          "us_privacy": "1YNN"
-        }
-      },
-      "imp": [
-        {
-          "id": "test-imp-id",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
-            }
-          }
-        }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": [
-              "91.199.242.236"
-            ],
-            "User-Agent": [
-              "random user agent"
-            ]
-          },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "siteid": "site-id",
-              "url": "criteo.com",
-              "networkid": 78910
-            },
-            "user": {
-              "ip": "91.199.242.236",
-              "ua": "random user agent",
-              "deviceos": "android",
-              "deviceidtype": "gaid",
-              "uspIab": "1YNN"
-            },
-            "gdprconsent": {},
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              }
-            ],
-            "eids": [
-              {
-                "source": "criteo.com",
-                "uids": [
-                  {
-                    "id": "criteo-eid"
-                  }
-                ]
-              }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 204
-        }
-      }
-    ],
-    "expectedBidResponses": []
-  }
-  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-inapp.json b/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-inapp.json
deleted file mode 100755
index 5bb5f98b..00000000
--- a/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-inapp.json
+++ /dev/null
@@ -1,125 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "app": {
-        "bundle": "test.app.bundle"
-      },
-      "device": {
-        "ifa": "test-ifa-123456",
-        "os": "android",
-        "ip": "91.199.242.236",
-        "ua": "random user agent"
-      },
-      "user": {
-        "ext": {
-          "consent": "iabconsentstringwithconsent"
-        }
-      },
-      "regs": {
-        "ext": {
-          "gdpr": 1
-        }
-      },
-      "imp": [
-        {
-          "id": "test-imp-id",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
-            }
-          }
-        }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": [
-              "91.199.242.236"
-            ],
-            "User-Agent": [
-              "random user agent"
-            ]
-          },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "bundleid": "test.app.bundle",
-              "networkid": 78910
-            },
-            "user": {
-              "deviceid": "test-ifa-123456",
-              "deviceos": "android",
-              "deviceidtype": "gaid",
-              "ip": "91.199.242.236",
-              "ua": "random user agent"
-            },
-            "gdprconsent": {
-              "consentdata": "iabconsentstringwithconsent",
-              "gdprapplies": true
-            },
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 200,
-          "body": {
-            "slots": [
-              {
-                "arbitrageid": "test-slot-id",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "cpm": 0.1,
-                "currency": "USD",
-                "width": 300,
-                "height": 250,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              }
-            ]
-          }
-        }
-      }
-    ],
-    "expectedBidResponses": [
-      {
-        "currency": "USD",
-        "bids": [
-          {
-            "bid": {
-              "id": "test-slot-id",
-              "impid": "test-imp-id",
-              "price": 0.1,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 250
-            },
-            "type": "banner"
-          }
-        ]
-      }
-    ]
-  }
diff --git a/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-uid.json b/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-uid.json
deleted file mode 100755
index ea64fb0c..00000000
--- a/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-uid.json
+++ /dev/null
@@ -1,141 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "site": {
-        "id": "site-id",
-        "page": "criteo.com"
-      },
-      "device": {
-        "os": "android",
-        "ip": "91.199.242.236",
-        "ua": "random user agent"
-      },
-      "user": {
-        "ext": {
-          "consent": "iabconsentstringwithconsent",
-          "eids": [{
-            "source": "criteo.com",
-            "uids": [{
-              "id": "criteo-eid"
-            }]
-          }]
-        }
-      },
-      "regs": {
-        "ext": {
-          "gdpr": 1
-        }
-      },
-      "imp": [
-        {
-          "id": "test-imp-id",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
-            }
-          }
-        }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": [
-              "91.199.242.236"
-            ],
-            "User-Agent": [
-              "random user agent"
-            ]
-          },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "siteid": "site-id",
-              "url": "criteo.com",
-              "networkid": 78910
-            },
-            "user": {
-              "ip": "91.199.242.236",
-              "ua": "random user agent",
-              "deviceos": "android",
-              "deviceidtype": "gaid"
-            },
-            "gdprconsent": {
-              "consentdata": "iabconsentstringwithconsent",
-              "gdprapplies": true
-            },
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              }
-            ],
-            "eids": [
-              {
-                "source": "criteo.com",
-                "uids": [
-                  {
-                    "id": "criteo-eid"
-                  }
-                ]
-              }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 200,
-          "body": {
-            "slots": [
-              {
-                "arbitrageid": "test-slot-id",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "cpm": 0.1,
-                "currency": "USD",
-                "width": 300,
-                "height": 250,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              }
-            ]
-          }
-        }
-      }
-    ],
-    "expectedBidResponses": [
-      {
-        "currency": "USD",
-        "bids": [
-          {
-            "bid": {
-              "id": "test-slot-id",
-              "impid": "test-imp-id",
-              "price": 0.1,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 250
-            },
-            "type": "banner"
-          }
-        ]
-      }
-    ]
-  }
diff --git a/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-inapp.json b/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-inapp.json
deleted file mode 100755
index 7385b937..00000000
--- a/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-inapp.json
+++ /dev/null
@@ -1,92 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "app": {
-        "bundle": "test.app.bundle"
-      },
-      "device": {
-        "ifa": "test-ifa-123456",
-        "os": "android",
-        "ip": "91.199.242.236",
-        "ua": "random user agent"
-      },
-      "user": {
-        "ext": {
-          "consent": "iabconsentstringwithnoconsent"
-        }
-      },
-      "regs": {
-        "ext": {
-          "gdpr": 1
-        }
-      },
-      "imp": [
-        {
-          "id": "test-imp-id",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
-            }
-          }
-        }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": [
-              "91.199.242.236"
-            ],
-            "User-Agent": [
-              "random user agent"
-            ]
-          },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "bundleid": "test.app.bundle",
-              "networkid": 78910
-            },
-            "user": {
-              "deviceid": "test-ifa-123456",
-              "deviceos": "android",
-              "deviceidtype": "gaid",
-              "ip": "91.199.242.236",
-              "ua": "random user agent"
-            },
-            "gdprconsent": {
-              "consentdata": "iabconsentstringwithnoconsent",
-              "gdprapplies": true
-            },
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 204
-        }
-      }
-    ],
-    "expectedBidResponses": []
-  }
-  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-uid.json b/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-uid.json
deleted file mode 100755
index dbc5fb32..00000000
--- a/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-uid.json
+++ /dev/null
@@ -1,108 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "site": {
-        "id": "site-id",
-        "page": "criteo.com"
-      },
-      "device": {
-        "os": "android",
-        "ip": "91.199.242.236",
-        "ua": "random user agent"
-      },
-      "user": {
-        "ext": {
-          "consent": "iabconsentstringwithoutconsent",
-          "eids": [{
-            "source": "criteo.com",
-            "uids": [{
-              "id": "criteo-eid"
-            }]
-          }]
-        }
-      },
-      "regs": {
-        "ext": {
-          "gdpr": 1
-        }
-      },
-      "imp": [
-        {
-          "id": "test-imp-id",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
-            }
-          }
-        }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": [
-              "91.199.242.236"
-            ],
-            "User-Agent": [
-              "random user agent"
-            ]
-          },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "siteid": "site-id",
-              "url": "criteo.com",
-              "networkid": 78910
-            },
-            "user": {
-              "ip": "91.199.242.236",
-              "ua": "random user agent",
-              "deviceos": "android",
-              "deviceidtype": "gaid"
-            },
-            "gdprconsent": {
-              "consentdata": "iabconsentstringwithoutconsent",
-              "gdprapplies": true
-            },
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              }
-            ],
-            "eids": [
-              {
-                "source": "criteo.com",
-                "uids": [
-                  {
-                    "id": "criteo-eid"
-                  }
-                ]
-              }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 204
-        }
-      }
-    ],
-    "expectedBidResponses": []
-  }
-  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/multislots-alt-case.json b/adapters/criteo/criteotest/supplemental/multislots-alt-case.json
deleted file mode 100644
index cb7022c0..00000000
--- a/adapters/criteo/criteotest/supplemental/multislots-alt-case.json
+++ /dev/null
@@ -1,232 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "site": {
-        "id": "site-id",
-        "page": "criteo.com"
-      },
-      "device": {
-        "os": "android",
-        "ip": "91.199.242.236",
-        "ua": "random user agent"
-      },
-      "user": {
-        "ext": {
-          "eids": [{
-            "source": "criteo.com",
-            "uids": [{
-              "id": "criteo-eid"
-            }]
-          }]
-        }
-      },
-      "imp": [
-        {
-          "id": "test-imp-id-1",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneId": 123456,
-              "networkId": 78910
-            }
-          }
-        },
-        {
-          "id": "test-imp-id-2",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneId": 7891011,
-              "networkId": 78910
-            }
-          }
-        },
-        {
-          "id": "test-imp-id-3",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneId": 121314,
-              "networkId": 78910
-            }
-          }
-        }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": [
-              "91.199.242.236"
-            ],
-            "User-Agent": [
-              "random user agent"
-            ]
-          },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "siteid": "site-id",
-              "url": "criteo.com",
-              "networkid": 78910
-            },
-            "user": {
-              "ip": "91.199.242.236",
-              "ua": "random user agent",
-              "deviceos": "android",
-              "deviceidtype": "gaid"
-            },
-            "gdprconsent": {},
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id-1",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              },
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id-2",
-                "zoneid": 7891011,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              },
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id-3",
-                "zoneid": 121314,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              }
-            ],
-            "eids": [
-              {
-                "source": "criteo.com",
-                "uids": [
-                  {
-                    "id": "criteo-eid"
-                  }
-                ]
-              }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 200,
-          "body": {
-            "slots": [
-              {
-                "arbitrageid": "test-slot-id-1",
-                "impid": "test-imp-id-1",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "cpm": 0.1,
-                "currency": "USD",
-                "width": 300,
-                "height": 250,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              },
-              {
-                "arbitrageid": "test-slot-id-2",
-                "impid": "test-imp-id-2",
-                "zoneid": 7891011,
-                "networkid": 78910,
-                "cpm": 0.2,
-                "currency": "USD",
-                "width": 320,
-                "height": 50,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              },
-              {
-                "arbitrageid": "test-slot-id-3",
-                "impid": "test-imp-id-3",
-                "zoneid": 121314,
-                "networkid": 78910,
-                "cpm": 0.3,
-                "currency": "USD",
-                "width": 300,
-                "height": 600,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              }
-            ]
-          }
-        }
-      }
-    ],
-    "expectedBidResponses": [
-      {
-        "currency": "USD",
-        "bids": [
-          {
-            "bid": {
-              "id": "test-slot-id-1",
-              "impid": "test-imp-id-1",
-              "price": 0.1,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 250
-            },
-            "type": "banner"
-          },
-          {
-            "bid": {
-              "id": "test-slot-id-2",
-              "impid": "test-imp-id-2",
-              "price": 0.2,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 320,
-              "h": 50
-            },
-            "type": "banner"
-          },
-          {
-            "bid": {
-              "id": "test-slot-id-3",
-              "impid": "test-imp-id-3",
-              "price": 0.3,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 600
-            },
-            "type": "banner"
-          }
-        ]
-      }
-    ]
-  }
diff --git a/adapters/criteo/criteotest/supplemental/multislots-simple-banner-inapp.json b/adapters/criteo/criteotest/supplemental/multislots-simple-banner-inapp.json
deleted file mode 100755
index 29866013..00000000
--- a/adapters/criteo/criteotest/supplemental/multislots-simple-banner-inapp.json
+++ /dev/null
@@ -1,212 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "app": {
-        "bundle": "test.app.bundle"
-      },
-      "device": {
-        "ip": "91.199.242.236",
-        "ua": "random user agent",
-        "ifa": "test-ifa-123456",
-        "os": "android"
-      },
-      "imp": [
-        {
-          "id": "test-imp-id-1",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
-            }
-          }
-        },
-        {
-          "id": "test-imp-id-2",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 7891011,
-              "networkid": 78910
-            }
-          }
-        },
-        {
-          "id": "test-imp-id-3",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 121314,
-              "networkid": 78910
-            }
-          }
-        }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": [
-              "91.199.242.236"
-            ],
-            "User-Agent": [
-              "random user agent"
-            ]
-          },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "bundleid": "test.app.bundle",
-              "networkid": 78910
-            },
-            "user": {
-              "deviceid": "test-ifa-123456",
-              "deviceos": "android",
-              "deviceidtype": "gaid",
-              "ip": "91.199.242.236",
-              "ua": "random user agent"
-            },
-            "gdprconsent": {},
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id-1",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              },
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id-2",
-                "zoneid": 7891011,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              },
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id-3",
-                "zoneid": 121314,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 200,
-          "body": {
-            "slots": [
-              {
-                "arbitrageid": "test-slot-id-1",
-                "impid": "test-imp-id-1",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "cpm": 0.1,
-                "currency": "USD",
-                "width": 300,
-                "height": 250,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              },
-              {
-                "arbitrageid": "test-slot-id-2",
-                "impid": "test-imp-id-2",
-                "zoneid": 7891011,
-                "networkid": 78910,
-                "cpm": 0.2,
-                "currency": "USD",
-                "width": 320,
-                "height": 50,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              },
-              {
-                "arbitrageid": "test-slot-id-3",
-                "impid": "test-imp-id-3",
-                "zoneid": 121314,
-                "networkid": 78910,
-                "cpm": 0.3,
-                "currency": "USD",
-                "width": 300,
-                "height": 600,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              }
-            ]
-          }
-        }
-      }
-    ],
-    "expectedBidResponses": [
-      {
-        "currency": "USD",
-        "bids": [
-          {
-            "bid": {
-              "id": "test-slot-id-1",
-              "impid": "test-imp-id-1",
-              "price": 0.1,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 250
-            },
-            "type": "banner"
-          },
-          {
-            "bid": {
-              "id": "test-slot-id-2",
-              "impid": "test-imp-id-2",
-              "price": 0.2,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 320,
-              "h": 50
-            },
-            "type": "banner"
-          },
-          {
-            "bid": {
-              "id": "test-slot-id-3",
-              "impid": "test-imp-id-3",
-              "price": 0.3,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 600
-            },
-            "type": "banner"
-          }
-        ]
-      }
-    ]
-  }
diff --git a/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid-different-network-ids.json b/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid-different-network-ids.json
deleted file mode 100755
index dcb51825..00000000
--- a/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid-different-network-ids.json
+++ /dev/null
@@ -1,84 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "site": {
-        "id": "site-id",
-        "page": "criteo.com"
-      },
-      "device": {
-        "os": "android",
-        "ip": "91.199.242.236",
-        "ua": "random user agent"
-      },
-      "user": {
-        "ext": {
-          "eids": [{
-            "source": "criteo.com",
-            "uids": [{
-              "id": "criteo-eid"
-            }]
-          }]
-        }
-      },
-      "imp": [
-        {
-          "id": "test-imp-id-1",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
-            }
-          }
-        },
-        {
-          "id": "test-imp-id-2",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 7891011,
-              "networkid": 123456
-            }
-          }
-        },
-        {
-          "id": "test-imp-id-3",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 121314,
-              "networkid": 467890
-            }
-          }
-        }
-      ]
-    },
-      "expectedMakeRequestsErrors": [
-        {
-          "value": "Bid request has slots coming with several network IDs which is not allowed",
-          "comparison": "literal"
-        }
-      ]
-  }
-  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid.json b/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid.json
deleted file mode 100755
index 913f19b7..00000000
--- a/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid.json
+++ /dev/null
@@ -1,232 +0,0 @@
-{
-    "mockBidRequest": {
-      "id": "test-request-id",
-      "site": {
-        "id": "site-id",
-        "page": "criteo.com"
-      },
-      "device": {
-        "os": "android",
-        "ip": "91.199.242.236",
-        "ua": "random user agent"
-      },
-      "user": {
-        "ext": {
-          "eids": [{
-            "source": "criteo.com",
-            "uids": [{
-              "id": "criteo-eid"
-            }]
-          }]
-        }
-      },
-      "imp": [
-        {
-          "id": "test-imp-id-1",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 123456,
-              "networkid": 78910
-            }
-          }
-        },
-        {
-          "id": "test-imp-id-2",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 7891011,
-              "networkid": 78910
-            }
-          }
-        },
-        {
-          "id": "test-imp-id-3",
-          "banner": {
-            "format": [
-              {
-                "w": 300,
-                "h": 250
-              }
-            ]
-          },
-          "ext": {
-            "bidder": {
-              "zoneid": 121314,
-              "networkid": 78910
-            }
-          }
-        }
-      ]
-    },
-    "httpCalls": [
-      {
-        "expectedRequest": {
-          "uri": "https://bidder.criteo.com/cdb?profileId=230",
-          "headers": {
-            "X-Forwarded-For": [
-              "91.199.242.236"
-            ],
-            "User-Agent": [
-              "random user agent"
-            ]
-          },
-          "body": {
-            "id": "test-request-id",
-            "publisher": {
-              "siteid": "site-id",
-              "url": "criteo.com",
-              "networkid": 78910
-            },
-            "user": {
-              "ip": "91.199.242.236",
-              "ua": "random user agent",
-              "deviceos": "android",
-              "deviceidtype": "gaid"
-            },
-            "gdprconsent": {},
-            "slots": [
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id-1",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              },
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id-2",
-                "zoneid": 7891011,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              },
-              {
-                "slotid": "00000000-0000-0000-00000000",
-                "impid": "test-imp-id-3",
-                "zoneid": 121314,
-                "networkid": 78910,
-                "sizes": [
-                  "300x250"
-                ]
-              }
-            ],
-            "eids": [
-              {
-                "source": "criteo.com",
-                "uids": [
-                  {
-                    "id": "criteo-eid"
-                  }
-                ]
-              }
-            ]
-          }
-        },
-        "mockResponse": {
-          "status": 200,
-          "body": {
-            "slots": [
-              {
-                "arbitrageid": "test-slot-id-1",
-                "impid": "test-imp-id-1",
-                "zoneid": 123456,
-                "networkid": 78910,
-                "cpm": 0.1,
-                "currency": "USD",
-                "width": 300,
-                "height": 250,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              },
-              {
-                "arbitrageid": "test-slot-id-2",
-                "impid": "test-imp-id-2",
-                "zoneid": 7891011,
-                "networkid": 78910,
-                "cpm": 0.2,
-                "currency": "USD",
-                "width": 320,
-                "height": 50,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              },
-              {
-                "arbitrageid": "test-slot-id-3",
-                "impid": "test-imp-id-3",
-                "zoneid": 121314,
-                "networkid": 78910,
-                "cpm": 0.3,
-                "currency": "USD",
-                "width": 300,
-                "height": 600,
-                "creativecode": "creative-123",
-                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-              }
-            ]
-          }
-        }
-      }
-    ],
-    "expectedBidResponses": [
-      {
-        "currency": "USD",
-        "bids": [
-          {
-            "bid": {
-              "id": "test-slot-id-1",
-              "impid": "test-imp-id-1",
-              "price": 0.1,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 250
-            },
-            "type": "banner"
-          },
-          {
-            "bid": {
-              "id": "test-slot-id-2",
-              "impid": "test-imp-id-2",
-              "price": 0.2,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 320,
-              "h": 50
-            },
-            "type": "banner"
-          },
-          {
-            "bid": {
-              "id": "test-slot-id-3",
-              "impid": "test-imp-id-3",
-              "price": 0.3,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 600
-            },
-            "type": "banner"
-          }
-        ]
-      }
-    ]
-  }
diff --git a/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats-not-included.json b/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats-not-included.json
deleted file mode 100755
index 8f421f2b..00000000
--- a/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats-not-included.json
+++ /dev/null
@@ -1,132 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "site": {
-      "id": "site-id",
-      "page": "criteo.com"
-    },
-    "device": {
-      "os": "android",
-      "ip": "91.199.242.236",
-      "ua": "random user agent"
-    },
-    "user": {
-      "ext": {
-        "eids": [{
-          "source": "criteo.com",
-          "uids": [{
-            "id": "criteo-eid"
-          }]
-        }]
-      }
-    },
-    "imp": [
-      {
-        "id": "test-imp-id",
-        "banner": {
-          "w": 300,
-          "h": 250,
-          "format": [
-            {
-              "w": 250,
-              "h": 250
-            }
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "zoneid": 123456,
-            "networkid": 78910
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "https://bidder.criteo.com/cdb?profileId=230",
-        "headers": {
-          "X-Forwarded-For": ["91.199.242.236"],
-          "User-Agent": ["random user agent"]
-        },
-        "body": {
-          "id": "test-request-id",
-          "publisher": {
-            "siteid": "site-id",
-            "url": "criteo.com",
-            "networkid": 78910
-          },
-          "user": {
-            "ip": "91.199.242.236",
-            "ua": "random user agent",
-            "deviceos": "android",
-            "deviceidtype": "gaid"
-          },
-          "gdprconsent": {},
-          "slots": [
-            {
-              "slotid": "00000000-0000-0000-00000000",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "sizes": [
-                "250x250"
-              ]
-            }
-          ],
-          "eids": [
-            {
-              "source": "criteo.com",
-              "uids": [
-                {
-                  "id": "criteo-eid"
-                }
-              ]
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "test-request-id",
-          "slots": [
-            {
-              "arbitrageid": "test-slot-id",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "cpm": 0.1,
-              "currency": "USD",
-              "width": 300,
-              "height": 250,
-              "creativecode": "creative-123",
-              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-            }
-          ]
-        }
-      }
-    }
-  ],
-    "expectedBidResponses": [
-      {
-        "currency": "USD",
-        "bids": [
-          {
-            "bid": {
-              "id": "test-slot-id",
-              "impid": "test-imp-id",
-              "price": 0.1,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 250
-            },
-            "type": "banner"
-          }
-        ]
-      }
-  ]
-}
-
diff --git a/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats.json b/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats.json
deleted file mode 100755
index cf73d307..00000000
--- a/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats.json
+++ /dev/null
@@ -1,132 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "site": {
-      "id": "site-id",
-      "page": "criteo.com"
-    },
-    "device": {
-      "os": "android",
-      "ip": "91.199.242.236",
-      "ua": "random user agent"
-    },
-    "user": {
-      "ext": {
-        "eids": [{
-          "source": "criteo.com",
-          "uids": [{
-            "id": "criteo-eid"
-          }]
-        }]
-      }
-    },
-    "imp": [
-      {
-        "id": "test-imp-id",
-        "banner": {
-          "w": 300,
-          "h": 250,
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            }
-          ]
-        },
-        "ext": {
-          "bidder": {
-            "zoneid": 123456,
-            "networkid": 78910
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "https://bidder.criteo.com/cdb?profileId=230",
-        "headers": {
-          "X-Forwarded-For": ["91.199.242.236"],
-          "User-Agent": ["random user agent"]
-        },
-        "body": {
-          "id": "test-request-id",
-          "publisher": {
-            "siteid": "site-id",
-            "url": "criteo.com",
-            "networkid": 78910
-          },
-          "user": {
-            "ip": "91.199.242.236",
-            "ua": "random user agent",
-            "deviceos": "android",
-            "deviceidtype": "gaid"
-          },
-          "gdprconsent": {},
-          "slots": [
-            {
-              "slotid": "00000000-0000-0000-00000000",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "sizes": [
-                "300x250"
-              ]
-            }
-          ],
-          "eids": [
-            {
-              "source": "criteo.com",
-              "uids": [
-                {
-                  "id": "criteo-eid"
-                }
-              ]
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "test-request-id",
-          "slots": [
-            {
-              "arbitrageid": "test-slot-id",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "cpm": 0.1,
-              "currency": "USD",
-              "width": 300,
-              "height": 250,
-              "creativecode": "creative-123",
-              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-            }
-          ]
-        }
-      }
-    }
-  ],
-    "expectedBidResponses": [
-      {
-        "currency": "USD",
-        "bids": [
-          {
-            "bid": {
-              "id": "test-slot-id",
-              "impid": "test-imp-id",
-              "price": 0.1,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 250
-            },
-            "type": "banner"
-          }
-        ]
-      }
-  ]
-}
-
diff --git a/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size.json b/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size.json
deleted file mode 100755
index ae965477..00000000
--- a/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size.json
+++ /dev/null
@@ -1,126 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "test-request-id",
-    "site": {
-      "id": "site-id",
-      "page": "criteo.com"
-    },
-    "device": {
-      "os": "android",
-      "ip": "91.199.242.236",
-      "ua": "random user agent"
-    },
-    "user": {
-      "ext": {
-        "eids": [{
-          "source": "criteo.com",
-          "uids": [{
-            "id": "criteo-eid"
-          }]
-        }]
-      }
-    },
-    "imp": [
-      {
-        "id": "test-imp-id",
-        "banner": {
-          "w": 300,
-          "h": 250
-        },
-        "ext": {
-          "bidder": {
-            "zoneid": 123456,
-            "networkid": 78910
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "uri": "https://bidder.criteo.com/cdb?profileId=230",
-        "headers": {
-          "X-Forwarded-For": ["91.199.242.236"],
-          "User-Agent": ["random user agent"]
-        },
-        "body": {
-          "id": "test-request-id",
-          "publisher": {
-            "siteid": "site-id",
-            "url": "criteo.com",
-            "networkid": 78910
-          },
-          "user": {
-            "ip": "91.199.242.236",
-            "ua": "random user agent",
-            "deviceos": "android",
-            "deviceidtype": "gaid"
-          },
-          "gdprconsent": {},
-          "slots": [
-            {
-              "slotid": "00000000-0000-0000-00000000",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "sizes": [
-                "300x250"
-              ]
-            }
-          ],
-          "eids": [
-            {
-              "source": "criteo.com",
-              "uids": [
-                {
-                  "id": "criteo-eid"
-                }
-              ]
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "test-request-id",
-          "slots": [
-            {
-              "arbitrageid": "test-slot-id",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "cpm": 0.1,
-              "currency": "USD",
-              "width": 300,
-              "height": 250,
-              "creativecode": "creative-123",
-              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
-            }
-          ]
-        }
-      }
-    }
-  ],
-    "expectedBidResponses": [
-      {
-        "currency": "USD",
-        "bids": [
-          {
-            "bid": {
-              "id": "test-slot-id",
-              "impid": "test-imp-id",
-              "price": 0.1,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 250
-            },
-            "type": "banner"
-          }
-        ]
-      }
-  ]
-}
-
diff --git a/adapters/criteo/criteotest/supplemental/simple-banner-with-ipv6.json b/adapters/criteo/criteotest/supplemental/simple-banner-with-ipv6.json
index 0aa021d2..1abf75cb 100755
--- a/adapters/criteo/criteotest/supplemental/simple-banner-with-ipv6.json
+++ b/adapters/criteo/criteotest/supplemental/simple-banner-with-ipv6.json
@@ -12,12 +12,16 @@
     },
     "user": {
       "ext": {
-        "eids": [{
-          "source": "criteo.com",
-          "uids": [{
-            "id": "criteo-eid"
-          }]
-        }]
+        "eids": [
+          {
+            "source": "criteo.com",
+            "uids": [
+              {
+                "id": "criteo-eid"
+              }
+            ]
+          }
+        ]
       }
     },
     "imp": [
@@ -39,44 +43,46 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://bidder.criteo.com/cdb?profileId=230",
-        "headers": {
-          "X-Forwarded-For": ["fd36:ce97:0fa1:dec0:0000:0000:0000:0000"],
-          "User-Agent": ["random user agent"]
-        },
+        "uri": "https://bidder.criteo.com/openrtb/prebid/auction/request?profile=230",
+        "headers": {},
         "body": {
           "id": "test-request-id",
-          "publisher": {
-            "siteid": "site-id",
-            "url": "criteo.com",
-            "networkid": 78910
+          "site": {
+            "id": "site-id",
+            "page": "criteo.com"
           },
-          "user": {
+          "device": {
+            "os": "android",
             "ipv6": "fd36:ce97:0fa1:dec0:0000:0000:0000:0000",
-            "ua": "random user agent",
-            "deviceos": "android",
-            "deviceidtype": "gaid"
+            "ua": "random user agent"
           },
-          "gdprconsent": {},
-          "slots": [
-            {
-              "slotid": "00000000-0000-0000-00000000",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "sizes": [
-                "300x250"
+          "user": {
+            "ext": {
+              "eids": [
+                {
+                  "source": "criteo.com",
+                  "uids": [
+                    {
+                      "id": "criteo-eid"
+                    }
+                  ]
+                }
               ]
             }
-          ],
-          "eids": [
+          },
+          "imp": [
             {
-              "source": "criteo.com",
-              "uids": [
-                {
-                  "id": "criteo-eid"
+              "id": "test-imp-id",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "zoneid": 123456,
+                  "networkid": 78910
                 }
-              ]
+              }
             }
           ]
         }
@@ -85,42 +91,44 @@
         "status": 200,
         "body": {
           "id": "test-request-id",
-          "slots": [
+          "currency": "USD",
+          "seatbid": [
             {
-              "arbitrageid": "test-slot-id",
-              "impid": "test-imp-id",
-              "zoneid": 123456,
-              "networkid": 78910,
-              "cpm": 0.1,
-              "currency": "USD",
-              "width": 300,
-              "height": 250,
-              "creativecode": "creative-123",
-              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              "bid": [
+                {
+                  "id": "test-slot-id",
+                  "impid": "test-imp-id",
+                  "price": 0.1,
+                  "crid": "creative-123",
+                  "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+                  "w": 300,
+                  "h": 250
+                }
+              ] 
             }
           ]
         }
       }
     }
   ],
-    "expectedBidResponses": [
-      {
-        "currency": "USD",
-        "bids": [
-          {
-            "bid": {
-              "id": "test-slot-id",
-              "impid": "test-imp-id",
-              "price": 0.1,
-              "crid": "creative-123",
-              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
-              "w": 300,
-              "h": 250
-            },
-            "type": "banner"
-          }
-        ]
-      }
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-slot-id",
+            "impid": "test-imp-id",
+            "price": 0.1,
+            "crid": "creative-123",
+            "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
   ]
 }
 
diff --git a/adapters/criteo/models_test.go b/adapters/criteo/models_test.go
deleted file mode 100644
index 60b8c67e..00000000
--- a/adapters/criteo/models_test.go
+++ /dev/null
@@ -1,452 +0,0 @@
-package criteo
-
-import (
-	"encoding/json"
-	"reflect"
-	"testing"
-
-	"github.com/prebid/openrtb/v17/openrtb2"
-	"github.com/prebid/prebid-server/adapters"
-	"github.com/prebid/prebid-server/openrtb_ext"
-)
-
-func TestGetDeviceType(t *testing.T) {
-
-	// Setup:
-	deviceTypeCases := []struct {
-		deviceType string
-		expected   string
-	}{
-		{"ios", "idfa"},
-		{"Ios", "idfa"},
-		{"IOS", "idfa"},
-		{"android", "gaid"},
-		{"unknown", "unknown"},
-		{"", "unknown"},
-		{"qwerty", "unknown"},
-		{"qWerty", "unknown"},
-		{"abc", "unknown"},
-	}
-
-	for _, uc := range deviceTypeCases {
-		// Execute:
-		result := getDeviceType(uc.deviceType)
-
-		// Verify:
-		if uc.expected != result {
-			t.Errorf("Bad getDeviceType for '%s'. Expected: %s, got %s", uc.deviceType, uc.expected, result)
-		}
-	}
-}
-
-func TestNewCriteoRequest(t *testing.T) {
-	// Setup:
-	var (
-		dummyRequestID         = "random request ID"
-		dummyPublisherBundleID = "bundleid"
-		dummyPublisherURL      = "test.com"
-		dummyPublisherSiteID   = "siteid"
-		dummyUserDeviceID      = "random-device-id"
-		dummyUserDeviceOS      = "android"
-		dummyUserDeviceIDType  = "gaid"
-		dummyUserCookieID      = "random-cookie-id"
-		dummyUserIP            = "1.1.1.1"
-		dummyUserUA            = "random UA"
-		dummyGdprApplies       = true
-		dummyGdprAppliesUint   = int8(1)
-		dummyGdprConsentData   = "randomconsentdata"
-		dummySlotID            = "11111111-1111-1111-11111111"
-		dummySlotImpID         = "fake-imp-id-1"
-		dummySlotZoneID        = int64(1)
-	)
-
-	fakeSlotIDGenerator := newFakeGuidGenerator(dummySlotID)
-
-	// The request doesn't make any sense but aims to fill every single criteo request fields
-	expectedCriteoRequest := criteoRequest{
-		ID: dummyRequestID,
-		Publisher: criteoPublisher{
-			SiteID:   dummyPublisherSiteID,
-			BundleID: dummyPublisherBundleID,
-			URL:      dummyPublisherURL,
-		},
-		User: criteoUser{
-			DeviceID:     dummyUserDeviceID,
-			DeviceOS:     dummyUserDeviceOS,
-			DeviceIDType: dummyUserDeviceIDType,
-			CookieID:     dummyUserCookieID,
-			IP:           dummyUserIP,
-			UA:           dummyUserUA,
-		},
-		GdprConsent: criteoGdprConsent{
-			GdprApplies: &dummyGdprApplies,
-			ConsentData: dummyGdprConsentData,
-		},
-		Slots: []criteoRequestSlot{
-			{
-				SlotID: dummySlotID,
-				ImpID:  dummySlotImpID,
-				ZoneID: &dummySlotZoneID,
-			},
-		},
-	}
-
-	userExtJSON, _ := json.Marshal(&openrtb_ext.ExtUser{
-		Consent: dummyGdprConsentData,
-	})
-	regsExtJSON, _ := json.Marshal(&openrtb_ext.ExtRegs{
-		GDPR: &dummyGdprAppliesUint,
-	})
-	bidderExtJSON, _ := json.Marshal(&openrtb_ext.ExtImpCriteo{
-		ZoneID: dummySlotZoneID,
-	})
-	impExtJSON, _ := json.Marshal(&adapters.ExtImpBidder{
-		Bidder: bidderExtJSON,
-	})
-	incomingRequest := &openrtb2.BidRequest{
-		ID: dummyRequestID,
-		App: &openrtb2.App{
-			Bundle: dummyPublisherBundleID,
-		},
-		Site: &openrtb2.Site{
-			ID:   dummyPublisherSiteID,
-			Page: dummyPublisherURL,
-		},
-		User: &openrtb2.User{
-			BuyerUID: dummyUserCookieID,
-			Ext:      userExtJSON,
-		},
-		Regs: &openrtb2.Regs{
-			Ext: regsExtJSON,
-		},
-		Device: &openrtb2.Device{
-			IFA: dummyUserDeviceID,
-			OS:  dummyUserDeviceOS,
-			IP:  dummyUserIP,
-			UA:  dummyUserUA,
-		},
-		Imp: []openrtb2.Imp{
-			{
-				ID:  dummySlotImpID,
-				Ext: impExtJSON,
-			},
-		},
-	}
-
-	// Execute:
-	result, err := newCriteoRequest(fakeSlotIDGenerator, incomingRequest)
-
-	// Verify:
-	if err != nil {
-		t.Errorf("newCriteoRequest has errors: %s", err)
-	}
-
-	if expectedCriteoRequest.ID != result.ID ||
-		!reflect.DeepEqual(expectedCriteoRequest, result) ||
-		!reflect.DeepEqual(expectedCriteoRequest.Publisher, result.Publisher) ||
-		!reflect.DeepEqual(expectedCriteoRequest.User, result.User) ||
-		!reflect.DeepEqual(expectedCriteoRequest.GdprConsent, result.GdprConsent) ||
-		len(expectedCriteoRequest.Slots) != len(result.Slots) ||
-		!reflect.DeepEqual(expectedCriteoRequest.Slots[0], result.Slots[0]) {
-		actualResultJSON, _ := json.Marshal(result)
-		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
-		t.Errorf("newCriteoRequest was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
-	}
-}
-
-func TestGetGdprConsent(t *testing.T) {
-	// Setup:
-	var (
-		dummyGdprApplies     = true
-		dummyGdprConsentData = "randomconsentdata"
-		dummyGdprAppliesUint = int8(1)
-	)
-
-	expectedCriteoRequest := criteoRequest{
-		GdprConsent: criteoGdprConsent{
-			GdprApplies: &dummyGdprApplies,
-			ConsentData: dummyGdprConsentData,
-		},
-	}
-
-	userExtJSON, _ := json.Marshal(&openrtb_ext.ExtUser{
-		Consent: dummyGdprConsentData,
-	})
-	regsExtJSON, _ := json.Marshal(&openrtb_ext.ExtRegs{
-		GDPR: &dummyGdprAppliesUint,
-	})
-	incomingRequest := &openrtb2.BidRequest{
-		User: &openrtb2.User{
-			Ext: userExtJSON,
-		},
-		Regs: &openrtb2.Regs{
-			Ext: regsExtJSON,
-		},
-	}
-
-	var regsExt *openrtb_ext.ExtRegs
-	if incomingRequest.Regs != nil {
-		json.Unmarshal(incomingRequest.Regs.Ext, &regsExt)
-	}
-
-	// Execute:
-	gdprConsent, _ := newCriteoGdprConsent(incomingRequest.User, regsExt)
-	result := criteoRequest{
-		GdprConsent: gdprConsent,
-	}
-
-	// Verify:
-	if !reflect.DeepEqual(expectedCriteoRequest, result) {
-		actualResultJSON, _ := json.Marshal(result)
-		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
-		t.Errorf("getGdprConsent was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
-	}
-}
-
-func TestGetUser(t *testing.T) {
-	// Setup:
-	var (
-		dummyUserDeviceID     = "random-device-id"
-		dummyUserDeviceOS     = "android"
-		dummyUserDeviceIDType = "gaid"
-		dummyUserCookieID     = "random-cookie-id"
-		dummyUserIP           = "1.1.1.1"
-		dummyUserUA           = "random UA"
-		dummyCcpaString       = "1YYY"
-	)
-	expectedCriteoRequest := &criteoRequest{
-		User: criteoUser{
-			DeviceID:     dummyUserDeviceID,
-			DeviceOS:     dummyUserDeviceOS,
-			DeviceIDType: dummyUserDeviceIDType,
-			CookieID:     dummyUserCookieID,
-			IP:           dummyUserIP,
-			UA:           dummyUserUA,
-			UspIab:       dummyCcpaString,
-		},
-	}
-
-	regsExt := &openrtb_ext.ExtRegs{
-		USPrivacy: dummyCcpaString,
-	}
-	regsExtData, err := json.Marshal(regsExt)
-	if err != nil {
-		t.Errorf("cannot marshal regsExt data")
-	}
-
-	incomingRequest := &openrtb2.BidRequest{
-		User: &openrtb2.User{
-			BuyerUID: dummyUserCookieID,
-		},
-		Device: &openrtb2.Device{
-			IFA: dummyUserDeviceID,
-			OS:  dummyUserDeviceOS,
-			IP:  dummyUserIP,
-			UA:  dummyUserUA,
-		},
-		Regs: &openrtb2.Regs{
-			Ext: regsExtData,
-		},
-	}
-
-	// Execute:
-	result := &criteoRequest{
-		User: newCriteoUser(incomingRequest.User, incomingRequest.Device, regsExt),
-	}
-
-	// Verify:
-	if !reflect.DeepEqual(expectedCriteoRequest.User, result.User) {
-		actualResultJSON, _ := json.Marshal(result)
-		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
-		t.Errorf("getUser was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
-	}
-}
-
-func TestGetUser_NilUserAndDevice(t *testing.T) {
-	// Setup:
-	var dummyCcpaString = "1YYY"
-
-	expectedCriteoRequest := &criteoRequest{}
-
-	regsExt := &openrtb_ext.ExtRegs{
-		USPrivacy: dummyCcpaString,
-	}
-	regsExtData, err := json.Marshal(regsExt)
-	if err != nil {
-		t.Errorf("cannot marshal regsExt data")
-	}
-
-	incomingRequest := &openrtb2.BidRequest{
-		Regs: &openrtb2.Regs{
-			Ext: regsExtData,
-		},
-	}
-
-	// Execute:
-	result := &criteoRequest{
-		User: newCriteoUser(incomingRequest.User, incomingRequest.Device, regsExt),
-	}
-
-	// Verify:
-	if !reflect.DeepEqual(expectedCriteoRequest.User, result.User) {
-		actualResultJSON, _ := json.Marshal(result)
-		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
-		t.Errorf("getUser was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
-	}
-}
-
-func TestPublisher(t *testing.T) {
-	// Setup:
-	var (
-		dummyPublisherSiteID         = "siteid"
-		dummyPublisherBundleID       = "bundleid"
-		dummyPublisherURL            = "test.com"
-		dummyNetworkID         int64 = 1234567
-	)
-	expectedCriteoRequest := &criteoRequest{
-		Publisher: criteoPublisher{
-			SiteID:    dummyPublisherSiteID,
-			BundleID:  dummyPublisherBundleID,
-			URL:       dummyPublisherURL,
-			NetworkID: &dummyNetworkID,
-		},
-	}
-
-	incomingRequest := &openrtb2.BidRequest{
-		App: &openrtb2.App{
-			Bundle: dummyPublisherBundleID,
-		},
-		Site: &openrtb2.Site{
-			ID:   dummyPublisherSiteID,
-			Page: dummyPublisherURL,
-		},
-	}
-
-	// Execute:
-	result := &criteoRequest{
-		Publisher: newCriteoPublisher(&dummyNetworkID, incomingRequest.App, incomingRequest.Site),
-	}
-
-	// Verify:
-	if !reflect.DeepEqual(expectedCriteoRequest.Publisher, result.Publisher) {
-		actualResultJSON, _ := json.Marshal(result)
-		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
-		t.Errorf("getPublisher was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
-	}
-}
-
-func TestGetRequestSlots(t *testing.T) {
-	// Setup:
-	var (
-		dummySlotImpID  = "fake-imp-id-1"
-		dummySlotZoneID = int64(1)
-		dummySlotID     = "22222222-2222-2222-22222222"
-	)
-
-	fakeSlotIDGenerator := newFakeGuidGenerator(dummySlotID)
-
-	expectedCriteoRequest := &criteoRequest{
-		Slots: []criteoRequestSlot{
-			{
-				SlotID: dummySlotID,
-				ImpID:  dummySlotImpID,
-				ZoneID: &dummySlotZoneID,
-			},
-		},
-	}
-
-	bidderExtJSON, _ := json.Marshal(&openrtb_ext.ExtImpCriteo{
-		ZoneID: dummySlotZoneID,
-	})
-	impExtJSON, _ := json.Marshal(&adapters.ExtImpBidder{
-		Bidder: bidderExtJSON,
-	})
-	incomingRequest := &openrtb2.BidRequest{
-		Imp: []openrtb2.Imp{
-			{
-				ID:  dummySlotImpID,
-				Ext: impExtJSON,
-			},
-		},
-	}
-
-	// Execute:
-	slots, err := newCriteoRequestSlots(fakeSlotIDGenerator, incomingRequest.Imp)
-	result := &criteoRequest{
-		Slots: slots,
-	}
-
-	// Verify:
-	if err != nil {
-		t.Errorf("newCriteoRequestSlots has errors: %s", err)
-	}
-
-	if len(expectedCriteoRequest.Slots) != len(result.Slots) ||
-		!reflect.DeepEqual(expectedCriteoRequest.Slots[0], result.Slots[0]) {
-		actualResultJSON, _ := json.Marshal(result)
-		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
-		t.Errorf("newCriteoRequest was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
-	}
-}
-
-func TestGetRequestMultipleSlots(t *testing.T) {
-	// Setup:
-	dummySlots := []struct {
-		ID     string
-		ZoneID int64
-	}{
-		{"fake-imp-id-1", 1},
-		{"fake-imp-id-2", 2},
-		{"fake-imp-id-3", 3},
-		{"fake-imp-id-4", 4},
-		{"fake-imp-id-5", 5},
-	}
-
-	incomingRequest := &openrtb2.BidRequest{
-		Imp: make([]openrtb2.Imp, len(dummySlots)),
-	}
-	slots := make([]criteoRequestSlot, len(dummySlots))
-
-	for i := range dummySlots {
-		// Build expected slots
-		slots[i] = criteoRequestSlot{
-			ImpID:  dummySlots[i].ID,
-			ZoneID: &dummySlots[i].ZoneID,
-		}
-
-		// Build incoming request imps
-		bidderExtJSON, _ := json.Marshal(&openrtb_ext.ExtImpCriteo{
-			ZoneID: dummySlots[i].ZoneID,
-		})
-		impExtJSON, _ := json.Marshal(&adapters.ExtImpBidder{
-			Bidder: bidderExtJSON,
-		})
-		incomingRequest.Imp[i] = openrtb2.Imp{
-			ID:  dummySlots[i].ID,
-			Ext: impExtJSON,
-		}
-	}
-
-	expectedCriteoRequestSlots, err := newCriteoRequestSlots(newFakeGuidGenerator(""), incomingRequest.Imp)
-	expectedCriteoRequest := &criteoRequest{
-		Slots: expectedCriteoRequestSlots,
-	}
-
-	// Execute:
-	slotsResult, err := newCriteoRequestSlots(newFakeGuidGenerator(""), incomingRequest.Imp)
-	result := &criteoRequest{
-		Slots: slotsResult,
-	}
-
-	// Verify:
-	if err != nil {
-		t.Errorf("newCriteoRequestSlots has errors: %s", err)
-	}
-
-	if len(expectedCriteoRequest.Slots) != len(result.Slots) ||
-		!reflect.DeepEqual(expectedCriteoRequest.Slots, result.Slots) {
-		actualResultJSON, _ := json.Marshal(result)
-		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
-		t.Errorf("newCriteoRequest was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
-	}
-}
diff --git a/adapters/smaato/smaatotest/exemplary/native.json b/adapters/smaato/smaatotest/exemplary/native.json
index 2f3f4823..76534dd4 100644
--- a/adapters/smaato/smaatotest/exemplary/native.json
+++ b/adapters/smaato/smaatotest/exemplary/native.json
@@ -35,6 +35,15 @@
                 "data": {}
             }
         },
+        "regs": {
+            "coppa": 1,
+            "ext": {
+                "gdpr": 1,
+                "us_privacy": "uspConsentString",
+                "gpp": "gppString",
+                "gpp_sid": [7]
+            }
+        },
         "ext": {
             "prebid": {
                 "auctiontimestamp": 1598262728811,
@@ -83,6 +92,15 @@
                             "id": "1100042525"
                         }
                     },
+                    "regs": {
+                        "coppa": 1,
+                        "ext": {
+                            "gdpr": 1,
+                            "us_privacy": "uspConsentString",
+                            "gpp": "gppString",
+                            "gpp_sid": [7]
+                        }
+                    },
                     "ext": {
                         "client": "prebid_server_0.5"
                     }
diff --git a/adapters/smaato/smaatotest/exemplary/simple-banner.json b/adapters/smaato/smaatotest/exemplary/simple-banner.json
index 102af452..f6f1f9d7 100644
--- a/adapters/smaato/smaatotest/exemplary/simple-banner.json
+++ b/adapters/smaato/smaatotest/exemplary/simple-banner.json
@@ -64,7 +64,9 @@
       "coppa": 1,
       "ext": {
         "gdpr": 1,
-        "us_privacy": "uspConsentString"
+        "us_privacy": "uspConsentString",
+        "gpp": "gppString",
+        "gpp_sid": [7]
       }
     }
   },
@@ -118,7 +120,9 @@
             "coppa": 1,
             "ext": {
               "gdpr": 1,
-              "us_privacy": "uspConsentString"
+              "us_privacy": "uspConsentString",
+              "gpp": "gppString",
+              "gpp_sid": [7]
             }
           },
           "site": {
diff --git a/adapters/smaato/smaatotest/exemplary/video.json b/adapters/smaato/smaatotest/exemplary/video.json
index 03654bfb..17af4b36 100644
--- a/adapters/smaato/smaatotest/exemplary/video.json
+++ b/adapters/smaato/smaatotest/exemplary/video.json
@@ -56,6 +56,15 @@
                 "data": {}
             }
         },
+        "regs": {
+            "coppa": 1,
+            "ext": {
+                "gdpr": 1,
+                "us_privacy": "uspConsentString",
+                "gpp": "gppString",
+                "gpp_sid": [7]
+            }
+        },
         "ext": {
             "prebid": {
                 "auctiontimestamp": 1598262728811,
@@ -117,6 +126,15 @@
                         "ext": {
                         }
                     },
+                    "regs": {
+                        "coppa": 1,
+                        "ext": {
+                            "gdpr": 1,
+                            "us_privacy": "uspConsentString",
+                            "gpp": "gppString",
+                            "gpp_sid": [7]
+                        }
+                    },
                     "device": {
                         "ua": "test-user-agent"
                     },
diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go
index 80544874..5b125fc5 100644
--- a/endpoints/openrtb2/amp_auction_test.go
+++ b/endpoints/openrtb2/amp_auction_test.go
@@ -9,13 +9,15 @@ import (
 	"net/http/httptest"
 	"net/url"
 	"os"
-	"reflect"
 	"strconv"
 	"testing"
 	"time"
 
 	"github.com/julienschmidt/httprouter"
 	"github.com/prebid/openrtb/v17/openrtb2"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
 	"github.com/prebid/prebid-server/amp"
 	"github.com/prebid/prebid-server/analytics"
 	analyticsConf "github.com/prebid/prebid-server/analytics/config"
@@ -28,8 +30,6 @@ import (
 	metricsConfig "github.com/prebid/prebid-server/metrics/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
-
-	"github.com/stretchr/testify/assert"
 )
 
 // TestGoodRequests makes sure that the auction runs properly-formatted stored bids correctly.
@@ -1108,30 +1108,88 @@ func TestAmpDebug(t *testing.T) {
 	}
 }
 
-// Prevents #452
-func TestAmpTargetingDefaults(t *testing.T) {
-	req := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{}}
-	if err := defaultRequestExt(req); err != nil {
-		t.Fatalf("Unexpected error defaulting request.ext for AMP: %v", err)
+func TestInitAmpTargetingAndCache(t *testing.T) {
+	trueVal := true
+	emptyTargetingAndCache := &openrtb_ext.ExtRequestPrebid{
+		Targeting: &openrtb_ext.ExtRequestTargeting{},
+		Cache: &openrtb_ext.ExtRequestPrebidCache{
+			Bids: &openrtb_ext.ExtRequestPrebidCacheBids{},
+		},
 	}
 
-	assert.NoError(t, req.RebuildRequest())
-
-	var extRequest openrtb_ext.ExtRequest
-	if err := json.Unmarshal(req.Ext, &extRequest); err != nil {
-		t.Fatalf("Unexpected error unmarshalling defaulted request.ext for AMP: %v", err)
-	}
-	if extRequest.Prebid.Targeting == nil {
-		t.Fatal("AMP defaults should set request.ext.targeting")
-	}
-	if !extRequest.Prebid.Targeting.IncludeWinners {
-		t.Error("AMP defaults should set request.ext.targeting.includewinners to true")
-	}
-	if !extRequest.Prebid.Targeting.IncludeBidderKeys {
-		t.Error("AMP defaults should set request.ext.targeting.includebidderkeys to true")
+	testCases := []struct {
+		name           string
+		request        *openrtb2.BidRequest
+		expectedPrebid *openrtb_ext.ExtRequestPrebid
+		expectedErrs   []string
+	}{
+		{
+			name:         "malformed",
+			request:      &openrtb2.BidRequest{Ext: json.RawMessage("malformed")},
+			expectedErrs: []string{"invalid character 'm' looking for beginning of value"},
+		},
+		{
+			name:           "nil",
+			request:        &openrtb2.BidRequest{},
+			expectedPrebid: emptyTargetingAndCache,
+		},
+		{
+			name:           "empty",
+			request:        &openrtb2.BidRequest{Ext: json.RawMessage(`{"ext":{}}`)},
+			expectedPrebid: emptyTargetingAndCache,
+		},
+		{
+			name:           "missing targeting + cache",
+			request:        &openrtb2.BidRequest{Ext: json.RawMessage(`{"ext":{"prebid":{}}}`)},
+			expectedPrebid: emptyTargetingAndCache,
+		},
+		{
+			name:    "missing targeting",
+			request: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"cache":{"bids":{"returnCreative":true}}}}`)},
+			expectedPrebid: &openrtb_ext.ExtRequestPrebid{
+				Targeting: &openrtb_ext.ExtRequestTargeting{},
+				Cache: &openrtb_ext.ExtRequestPrebidCache{
+					Bids: &openrtb_ext.ExtRequestPrebidCacheBids{
+						ReturnCreative: &trueVal,
+					},
+				},
+			},
+		},
+		{
+			name:    "missing cache",
+			request: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"targeting":{"includewinners":true}}}`)},
+			expectedPrebid: &openrtb_ext.ExtRequestPrebid{
+				Targeting: &openrtb_ext.ExtRequestTargeting{
+					IncludeWinners: &trueVal,
+				},
+				Cache: &openrtb_ext.ExtRequestPrebidCache{
+					Bids: &openrtb_ext.ExtRequestPrebidCacheBids{},
+				},
+			},
+		},
 	}
-	if !reflect.DeepEqual(extRequest.Prebid.Targeting.PriceGranularity, openrtb_ext.PriceGranularityFromString("med")) {
-		t.Error("AMP defaults should set request.ext.targeting.pricegranularity to medium")
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			// setup
+			req := &openrtb_ext.RequestWrapper{BidRequest: tc.request}
+
+			// run
+			actualErrs := initAmpTargetingAndCache(req)
+
+			// assertions
+			require.NoError(t, req.RebuildRequest(), "rebuild request")
+
+			actualErrsMsgs := make([]string, len(actualErrs))
+			for i, v := range actualErrs {
+				actualErrsMsgs[i] = v.Error()
+			}
+			assert.ElementsMatch(t, tc.expectedErrs, actualErrsMsgs, "errors")
+
+			actualReqExt, _ := req.GetRequestExt()
+			actualPrebid := actualReqExt.GetPrebid()
+			assert.Equal(t, tc.expectedPrebid, actualPrebid, "prebid ext")
+		})
 	}
 }
 
@@ -1657,7 +1715,7 @@ func TestBuildAmpObject(t *testing.T) {
 					},
 					AT:   1,
 					TMax: 500,
-					Ext:  json.RawMessage(`{"prebid":{"cache":{"bids":{"returnCreative":null},"vastxml":null},"channel":{"name":"amp","version":""},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true,"includebrandcategory":null,"includeformat":false,"durationrangesec":null,"preferdeals":false}}}`),
+					Ext:  json.RawMessage(`{"prebid":{"cache":{"bids":{}},"channel":{"name":"amp","version":""},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true}}}`),
 				},
 				AuctionResponse: &openrtb2.BidResponse{
 					SeatBid: []openrtb2.SeatBid{{
@@ -1711,7 +1769,7 @@ func TestBuildAmpObject(t *testing.T) {
 					},
 					AT:   1,
 					TMax: 500,
-					Ext:  json.RawMessage(`{"prebid":{"cache":{"bids":{"returnCreative":null},"vastxml":null},"channel":{"name":"amp","version":""},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true,"includebrandcategory":null,"includeformat":false,"durationrangesec":null,"preferdeals":false}}}`),
+					Ext:  json.RawMessage(`{"prebid":{"cache":{"bids":{}},"channel":{"name":"amp","version":""},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true}}}`),
 				},
 				AuctionResponse: &openrtb2.BidResponse{
 					SeatBid: []openrtb2.SeatBid{{
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index 16ae06bb..6449d770 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -36,6 +36,7 @@ import (
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
 	"github.com/prebid/prebid-server/stored_responses"
 	"github.com/prebid/prebid-server/util/iputil"
+	"github.com/prebid/prebid-server/util/ptrutil"
 )
 
 func TestJsonSampleRequests(t *testing.T) {
@@ -135,7 +136,7 @@ func TestJsonSampleRequests(t *testing.T) {
 
 					auctionEndpointHandler, _, mockBidServers, mockCurrencyRatesServer, err := buildTestEndpoint(test, cfg)
 					if assert.NoError(t, err) {
-						runTestCase(t, auctionEndpointHandler, test, fileData, testFile)
+						assert.NotPanics(t, func() { runTestCase(t, auctionEndpointHandler, test, fileData, testFile) }, testFile)
 					}
 
 					// Close servers regardless if the test case was run or not
@@ -1655,49 +1656,54 @@ func TestValidateRequestExt(t *testing.T) {
 			givenRequestExt: nil,
 		},
 		{
-			description:     "empty",
+			description:     "prebid - nil",
 			givenRequestExt: json.RawMessage(`{}`),
 		},
 		{
 			description:     "prebid - empty",
-			givenRequestExt: json.RawMessage(`{"prebid": {}}`),
+			givenRequestExt: json.RawMessage(`{"prebid":{}}`),
 		},
 		{
 			description:     "prebid cache - empty",
-			givenRequestExt: json.RawMessage(`{"prebid": {"cache": {}}}`),
+			givenRequestExt: json.RawMessage(`{"prebid":{"cache":{}}}`),
 			expectedError:   `request.ext is invalid: request.ext.prebid.cache requires one of the "bids" or "vastxml" properties`,
 		},
 		{
 			description:     "prebid cache - bids - null",
-			givenRequestExt: json.RawMessage(`{"prebid": {"cache": {"bids": null}}}`),
+			givenRequestExt: json.RawMessage(`{"prebid":{"cache":{"bids":null}}}`),
 			expectedError:   `request.ext is invalid: request.ext.prebid.cache requires one of the "bids" or "vastxml" properties`,
 		},
 		{
 			description:     "prebid cache - bids - wrong type",
-			givenRequestExt: json.RawMessage(`{"prebid": {"cache": {"bids": true}}}`),
+			givenRequestExt: json.RawMessage(`{"prebid":{"cache":{"bids":true}}}`),
 			expectedError:   `json: cannot unmarshal bool into Go struct field ExtRequestPrebidCache.cache.bids of type openrtb_ext.ExtRequestPrebidCacheBids`,
 		},
 		{
 			description:     "prebid cache - bids - provided",
-			givenRequestExt: json.RawMessage(`{"prebid": {"cache": {"bids": {}}}}`),
+			givenRequestExt: json.RawMessage(`{"prebid":{"cache":{"bids":{}}}}`),
 		},
 		{
 			description:     "prebid cache - vastxml - null",
-			givenRequestExt: json.RawMessage(`{"prebid": {"cache": {"vastxml": null}}}`),
+			givenRequestExt: json.RawMessage(`{"prebid":{"cache":{"vastxml":null}}}`),
 			expectedError:   `request.ext is invalid: request.ext.prebid.cache requires one of the "bids" or "vastxml" properties`,
 		},
 		{
 			description:     "prebid cache - vastxml - wrong type",
-			givenRequestExt: json.RawMessage(`{"prebid": {"cache": {"vastxml": true}}}`),
+			givenRequestExt: json.RawMessage(`{"prebid":{"cache":{"vastxml":true}}}`),
 			expectedError:   `json: cannot unmarshal bool into Go struct field ExtRequestPrebidCache.cache.vastxml of type openrtb_ext.ExtRequestPrebidCacheVAST`,
 		},
 		{
 			description:     "prebid cache - vastxml - provided",
-			givenRequestExt: json.RawMessage(`{"prebid": {"cache": {"vastxml": {}}}}`),
+			givenRequestExt: json.RawMessage(`{"prebid":{"cache":{"vastxml":{}}}}`),
 		},
 		{
 			description:     "prebid cache - bids + vastxml - provided",
-			givenRequestExt: json.RawMessage(`{"prebid": {"cache": {"bids": {}, "vastxml": {}}}}`),
+			givenRequestExt: json.RawMessage(`{"prebid":{"cache":{"bids":{},"vastxml":{}}}}`),
+		},
+		{
+			description:     "prebid targeting", // test integration with validateTargeting
+			givenRequestExt: json.RawMessage(`{"prebid":{"targeting":{}}}`),
+			expectedError:   "ext.prebid.targeting: At least one of includewinners or includebidderkeys must be enabled to enable targeting support",
 		},
 	}
 
@@ -1713,6 +1719,146 @@ func TestValidateRequestExt(t *testing.T) {
 	}
 }
 
+func TestValidateTargeting(t *testing.T) {
+	testCases := []struct {
+		name           string
+		givenTargeting *openrtb_ext.ExtRequestTargeting
+		expectedError  error
+	}{
+		{
+			name:           "nil",
+			givenTargeting: nil,
+			expectedError:  nil,
+		},
+		{
+			name:           "empty",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{},
+			expectedError:  errors.New("ext.prebid.targeting: At least one of includewinners or includebidderkeys must be enabled to enable targeting support"),
+		},
+		{
+			name: "includewinners nil, includebidderkeys false",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeBidderKeys: ptrutil.ToPtr(false),
+			},
+			expectedError: errors.New("ext.prebid.targeting: At least one of includewinners or includebidderkeys must be enabled to enable targeting support"),
+		},
+		{
+			name: "includewinners nil, includebidderkeys true",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeBidderKeys: ptrutil.ToPtr(true),
+			},
+			expectedError: nil,
+		},
+		{
+			name: "includewinners false, includebidderkeys nil",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeWinners: ptrutil.ToPtr(false),
+			},
+			expectedError: errors.New("ext.prebid.targeting: At least one of includewinners or includebidderkeys must be enabled to enable targeting support"),
+		},
+		{
+			name: "includewinners true, includebidderkeys nil",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeWinners: ptrutil.ToPtr(true),
+			},
+			expectedError: nil,
+		},
+		{
+			name: "all false",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeWinners:    ptrutil.ToPtr(false),
+				IncludeBidderKeys: ptrutil.ToPtr(false),
+			},
+			expectedError: errors.New("ext.prebid.targeting: At least one of includewinners or includebidderkeys must be enabled to enable targeting support"),
+		},
+		{
+			name: "includewinners false, includebidderkeys true",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeWinners:    ptrutil.ToPtr(false),
+				IncludeBidderKeys: ptrutil.ToPtr(true),
+			},
+			expectedError: nil,
+		},
+		{
+			name: "includewinners false, includebidderkeys true",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeWinners:    ptrutil.ToPtr(true),
+				IncludeBidderKeys: ptrutil.ToPtr(false),
+			},
+			expectedError: nil,
+		},
+		{
+			name: "includewinners true, includebidderkeys true",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeWinners:    ptrutil.ToPtr(true),
+				IncludeBidderKeys: ptrutil.ToPtr(true),
+			},
+			expectedError: nil,
+		},
+		{
+			name: "price granularity empty",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeWinners:   ptrutil.ToPtr(true),
+				PriceGranularity: &openrtb_ext.PriceGranularity{},
+			},
+			expectedError: errors.New("Price granularity error: precision is required"),
+		},
+		{
+			name: "price granularity negative",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeWinners: ptrutil.ToPtr(true),
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Precision: ptrutil.ToPtr(-1),
+				},
+			},
+			expectedError: errors.New("Price granularity error: precision must be non-negative"),
+		},
+		{
+			name: "price granularity greater than max",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeWinners: ptrutil.ToPtr(true),
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Precision: ptrutil.ToPtr(openrtb_ext.MaxDecimalFigures + 1),
+				},
+			},
+			expectedError: errors.New("Price granularity error: precision of more than 15 significant figures is not supported"),
+		},
+		{
+			name: "price granularity ranges out of order",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeWinners: ptrutil.ToPtr(true),
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Precision: ptrutil.ToPtr(2),
+					Ranges: []openrtb_ext.GranularityRange{
+						{Min: 1.0, Max: 2.0, Increment: 0.2},
+						{Min: 0.0, Max: 1.0, Increment: 0.5},
+					},
+				},
+			},
+			expectedError: errors.New(`Price granularity error: range list must be ordered with increasing "max"`),
+		},
+		{
+			name: "price granularity negative increment",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				IncludeWinners: ptrutil.ToPtr(true),
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Precision: ptrutil.ToPtr(2),
+					Ranges: []openrtb_ext.GranularityRange{
+						{Min: 0.0, Max: 1.0, Increment: -0.1},
+					},
+				},
+			},
+			expectedError: errors.New("Price granularity error: increment must be a nonzero positive number"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			assert.Equal(t, tc.expectedError, validateTargeting(tc.givenTargeting), "Targeting")
+		})
+	}
+}
+
 func TestValidateOrFillChannel(t *testing.T) {
 	testCases := []struct {
 		description           string
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index 4e8fe2f7..2eb3fcd3 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -22,11 +22,13 @@ import (
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/prebid_cache_client"
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
+	"github.com/prebid/prebid-server/util/ptrutil"
 
 	"github.com/prebid/openrtb/v17/adcom1"
 	"github.com/prebid/openrtb/v17/openrtb2"
 	gometrics "github.com/rcrowley/go-metrics"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestVideoEndpointImpressionsNumber(t *testing.T) {
@@ -78,7 +80,8 @@ func TestVideoEndpointImpressionsDuration(t *testing.T) {
 
 	var extData openrtb_ext.ExtRequest
 	json.Unmarshal(ex.lastRequest.Ext, &extData)
-	assert.True(t, extData.Prebid.Targeting.IncludeBidderKeys, "Request ext incorrect: IncludeBidderKeys should be true ")
+	assert.NotNil(t, extData.Prebid.Targeting.IncludeBidderKeys, "Request ext incorrect: IncludeBidderKeys should be true ")
+	assert.True(t, *extData.Prebid.Targeting.IncludeBidderKeys, "Request ext incorrect: IncludeBidderKeys should be true ")
 
 	assert.Len(t, ex.lastRequest.Imp, 22, "Incorrect number of impressions in request")
 	assert.Equal(t, ex.lastRequest.Imp[0].ID, "1_0", "Incorrect impression id in request")
@@ -121,8 +124,8 @@ func TestCreateBidExtension(t *testing.T) {
 			DurationRangeSec:     durationRange,
 			RequireExactDuration: false,
 		},
-		PriceGranularity: openrtb_ext.PriceGranularity{
-			Precision: 2,
+		PriceGranularity: &openrtb_ext.PriceGranularity{
+			Precision: ptrutil.ToPtr(2),
 			Ranges:    priceGranRanges,
 		},
 	}
@@ -138,37 +141,20 @@ func TestCreateBidExtension(t *testing.T) {
 	assert.Equal(t, resExt.Prebid.Targeting.PriceGranularity.Ranges, priceGranRanges, "Price granularity is incorrect")
 }
 
-func TestCreateBidExtensionExactDurTrueNoPriceRange(t *testing.T) {
-	durationRange := make([]int, 0)
-	durationRange = append(durationRange, 15)
-	durationRange = append(durationRange, 30)
+func TestCreateBidExtensionTargeting(t *testing.T) {
+	ex := &mockExchangeVideo{}
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
+	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
+	recorder := httptest.NewRecorder()
 
-	translateCategories := false
-	videoRequest := openrtb_ext.BidRequestVideo{
-		IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{
-			PrimaryAdserver:     1,
-			Publisher:           "",
-			TranslateCategories: &translateCategories,
-		},
-		PodConfig: openrtb_ext.PodConfig{
-			DurationRangeSec:     durationRange,
-			RequireExactDuration: true,
-		},
-		PriceGranularity: openrtb_ext.PriceGranularity{
-			Precision: 0,
-			Ranges:    nil,
-		},
-	}
-	res, err := createBidExtension(&videoRequest)
-	assert.NoError(t, err, "Error should be nil")
+	deps := mockDeps(t, ex)
+	deps.VideoAuctionEndpoint(recorder, req, nil)
 
-	resExt := &openrtb_ext.ExtRequest{}
+	require.NotNil(t, ex.lastRequest, "The request never made it into the Exchange.")
 
-	if err := json.Unmarshal(res, &resExt); err != nil {
-		assert.Fail(t, "Unable to unmarshal bid extension")
-	}
-	assert.Equal(t, resExt.Prebid.Targeting.DurationRangeSec, []int(nil), "Duration range seconds is incorrect")
-	assert.Equal(t, resExt.Prebid.Targeting.PriceGranularity, openrtb_ext.PriceGranularityFromString("med"), "Price granularity is incorrect")
+	// assert targeting set to default
+	expectedRequestExt := `{"prebid":{"cache":{"vastxml":{}},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includebidderkeys":true,"includewinners":true,"includebrandcategory":{"primaryadserver":1,"publisher":"","withcategory":true}}}}`
+	assert.JSONEq(t, expectedRequestExt, string(ex.lastRequest.Ext))
 }
 
 func TestVideoEndpointDebugQueryTrue(t *testing.T) {
@@ -1256,13 +1242,13 @@ func (m *mockAnalyticsModule) LogVideoObject(vo *analytics.VideoObject) {
 	m.videoObjects = append(m.videoObjects, vo)
 }
 
-func (m *mockAnalyticsModule) LogCookieSyncObject(cso *analytics.CookieSyncObject) { return }
+func (m *mockAnalyticsModule) LogCookieSyncObject(cso *analytics.CookieSyncObject) {}
 
-func (m *mockAnalyticsModule) LogSetUIDObject(so *analytics.SetUIDObject) { return }
+func (m *mockAnalyticsModule) LogSetUIDObject(so *analytics.SetUIDObject) {}
 
-func (m *mockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject) { return }
+func (m *mockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject) {}
 
-func (m *mockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent) { return }
+func (m *mockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent) {}
 
 func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 	return &endpointDeps{
@@ -1369,6 +1355,10 @@ type mockExchangeVideo struct {
 }
 
 func (m *mockExchangeVideo) HoldAuction(ctx context.Context, r exchange.AuctionRequest, debugLog *exchange.DebugLog) (*openrtb2.BidResponse, error) {
+	if err := r.BidRequestWrapper.RebuildRequest(); err != nil {
+		return nil, err
+	}
+
 	m.lastRequest = r.BidRequestWrapper.BidRequest
 	if debugLog != nil && debugLog.Enabled {
 		m.cache.called = true
diff --git a/exchange/auction_test.go b/exchange/auction_test.go
index 9f335aa8..6518eda7 100644
--- a/exchange/auction_test.go
+++ b/exchange/auction_test.go
@@ -16,6 +16,7 @@ import (
 	"github.com/prebid/prebid-server/exchange/entities"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/prebid_cache_client"
+	"github.com/prebid/prebid-server/util/ptrutil"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -229,7 +230,7 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) {
 
 	targData := &targetData{
 		priceGranularity: openrtb_ext.PriceGranularity{
-			Precision: 2,
+			Precision: ptrutil.ToPtr(2),
 			Ranges: []openrtb_ext.GranularityRange{
 				{
 					Min:       0,
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 34749ba6..3d374417 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -24,6 +24,7 @@ import (
 	"github.com/prebid/prebid-server/hooks"
 	"github.com/prebid/prebid-server/hooks/hookexecution"
 	"github.com/prebid/prebid-server/hooks/hookstage"
+	"github.com/prebid/prebid-server/util/ptrutil"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 	jsonpatch "gopkg.in/evanphx/json-patch.v4"
@@ -2142,7 +2143,7 @@ func TestExchangeJSON(t *testing.T) {
 			t.Run(fileDisplayName, func(t *testing.T) {
 				specData, err := loadFile(fileName)
 				if assert.NoError(t, err, "Failed to load contents of file %s: %v", fileDisplayName, err) {
-					runSpec(t, fileDisplayName, specData)
+					assert.NotPanics(t, func() { runSpec(t, fileDisplayName, specData) }, fileDisplayName)
 				}
 			})
 		}
@@ -2508,7 +2509,7 @@ func (m *fakeRandomDeduplicateBidBooleanGenerator) Generate() bool {
 
 func newExtRequest() openrtb_ext.ExtRequest {
 	priceGran := openrtb_ext.PriceGranularity{
-		Precision: 2,
+		Precision: ptrutil.ToPtr(2),
 		Ranges: []openrtb_ext.GranularityRange{
 			{
 				Min:       0.0,
@@ -2522,8 +2523,8 @@ func newExtRequest() openrtb_ext.ExtRequest {
 	brandCat := openrtb_ext.ExtIncludeBrandCategory{PrimaryAdServer: 1, WithCategory: true, TranslateCategories: &translateCategories}
 
 	reqExt := openrtb_ext.ExtRequestTargeting{
-		PriceGranularity:     priceGran,
-		IncludeWinners:       true,
+		PriceGranularity:     &priceGran,
+		IncludeWinners:       ptrutil.ToPtr(true),
 		IncludeBrandCategory: &brandCat,
 	}
 
@@ -2536,7 +2537,7 @@ func newExtRequest() openrtb_ext.ExtRequest {
 
 func newExtRequestNoBrandCat() openrtb_ext.ExtRequest {
 	priceGran := openrtb_ext.PriceGranularity{
-		Precision: 2,
+		Precision: ptrutil.ToPtr(2),
 		Ranges: []openrtb_ext.GranularityRange{
 			{
 				Min:       0.0,
@@ -2549,8 +2550,8 @@ func newExtRequestNoBrandCat() openrtb_ext.ExtRequest {
 	brandCat := openrtb_ext.ExtIncludeBrandCategory{WithCategory: false}
 
 	reqExt := openrtb_ext.ExtRequestTargeting{
-		PriceGranularity:     priceGran,
-		IncludeWinners:       true,
+		PriceGranularity:     &priceGran,
+		IncludeWinners:       ptrutil.ToPtr(true),
 		IncludeBrandCategory: &brandCat,
 	}
 
@@ -2571,7 +2572,7 @@ func TestCategoryMapping(t *testing.T) {
 	requestExt := newExtRequest()
 
 	targData := &targetData{
-		priceGranularity: requestExt.Prebid.Targeting.PriceGranularity,
+		priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity,
 		includeWinners:   true,
 	}
 
@@ -2627,7 +2628,7 @@ func TestCategoryMappingNoIncludeBrandCategory(t *testing.T) {
 	requestExt := newExtRequestNoBrandCat()
 
 	targData := &targetData{
-		priceGranularity: requestExt.Prebid.Targeting.PriceGranularity,
+		priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity,
 		includeWinners:   true,
 	}
 	requestExt.Prebid.Targeting.DurationRangeSec = []int{15, 30, 40, 50}
@@ -2682,7 +2683,7 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) {
 	requestExt := newExtRequestTranslateCategories(nil)
 
 	targData := &targetData{
-		priceGranularity: requestExt.Prebid.Targeting.PriceGranularity,
+		priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity,
 		includeWinners:   true,
 	}
 
@@ -2725,7 +2726,7 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) {
 
 func newExtRequestTranslateCategories(translateCategories *bool) openrtb_ext.ExtRequest {
 	priceGran := openrtb_ext.PriceGranularity{
-		Precision: 2,
+		Precision: ptrutil.ToPtr(2),
 		Ranges: []openrtb_ext.GranularityRange{
 			{
 				Min:       0.0,
@@ -2741,8 +2742,8 @@ func newExtRequestTranslateCategories(translateCategories *bool) openrtb_ext.Ext
 	}
 
 	reqExt := openrtb_ext.ExtRequestTargeting{
-		PriceGranularity:     priceGran,
-		IncludeWinners:       true,
+		PriceGranularity:     &priceGran,
+		IncludeWinners:       ptrutil.ToPtr(true),
 		IncludeBrandCategory: &brandCat,
 	}
 
@@ -2764,7 +2765,7 @@ func TestCategoryMappingTranslateCategoriesFalse(t *testing.T) {
 	requestExt := newExtRequestTranslateCategories(&translateCategories)
 
 	targData := &targetData{
-		priceGranularity: requestExt.Prebid.Targeting.PriceGranularity,
+		priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity,
 		includeWinners:   true,
 	}
 
@@ -2815,7 +2816,7 @@ func TestCategoryDedupe(t *testing.T) {
 	requestExt := newExtRequest()
 
 	targData := &targetData{
-		priceGranularity: requestExt.Prebid.Targeting.PriceGranularity,
+		priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity,
 		includeWinners:   true,
 	}
 
@@ -2895,7 +2896,7 @@ func TestNoCategoryDedupe(t *testing.T) {
 	requestExt := newExtRequestNoBrandCat()
 
 	targData := &targetData{
-		priceGranularity: requestExt.Prebid.Targeting.PriceGranularity,
+		priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity,
 		includeWinners:   true,
 	}
 
@@ -2977,7 +2978,7 @@ func TestCategoryMappingBidderName(t *testing.T) {
 	requestExt.Prebid.Targeting.AppendBidderNames = true
 
 	targData := &targetData{
-		priceGranularity: requestExt.Prebid.Targeting.PriceGranularity,
+		priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity,
 		includeWinners:   true,
 	}
 
@@ -3031,7 +3032,7 @@ func TestCategoryMappingBidderNameNoCategories(t *testing.T) {
 	requestExt.Prebid.Targeting.AppendBidderNames = true
 
 	targData := &targetData{
-		priceGranularity: requestExt.Prebid.Targeting.PriceGranularity,
+		priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity,
 		includeWinners:   true,
 	}
 
@@ -3084,7 +3085,7 @@ func TestBidRejectionErrors(t *testing.T) {
 	requestExt.Prebid.Targeting.DurationRangeSec = []int{15, 30, 50}
 
 	targData := &targetData{
-		priceGranularity: requestExt.Prebid.Targeting.PriceGranularity,
+		priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity,
 		includeWinners:   true,
 	}
 
@@ -3191,7 +3192,7 @@ func TestCategoryMappingTwoBiddersOneBidEachNoCategorySamePrice(t *testing.T) {
 	requestExt := newExtRequestTranslateCategories(nil)
 
 	targData := &targetData{
-		priceGranularity: requestExt.Prebid.Targeting.PriceGranularity,
+		priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity,
 		includeWinners:   true,
 	}
 
@@ -3268,7 +3269,7 @@ func TestCategoryMappingTwoBiddersManyBidsEachNoCategorySamePrice(t *testing.T)
 	requestExt := newExtRequestTranslateCategories(nil)
 
 	targData := &targetData{
-		priceGranularity: requestExt.Prebid.Targeting.PriceGranularity,
+		priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity,
 		includeWinners:   true,
 	}
 
diff --git a/exchange/exchangetest/append-bidder-names.json b/exchange/exchangetest/append-bidder-names.json
index 86688ed1..400992ce 100644
--- a/exchange/exchangetest/append-bidder-names.json
+++ b/exchange/exchangetest/append-bidder-names.json
@@ -50,6 +50,18 @@
               "publisher": "",
               "withcategory": true
             },
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true,
             "appendbiddernames": true
           }
         }
@@ -229,6 +241,18 @@
                   "publisher": "",
                   "withcategory": true
                 },
+                "pricegranularity": {
+                  "precision": 2,
+                  "ranges": [
+                    {
+                      "min": 0,
+                      "max": 20,
+                      "increment": 0.1
+                    }
+                  ]
+                },
+                "includewinners": true,
+                "includebidderkeys": true,
                 "appendbiddernames": true
               }
             }
@@ -237,4 +261,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/bid-id-invalid.json b/exchange/exchangetest/bid-id-invalid.json
index e71bb0a5..d7d12619 100644
--- a/exchange/exchangetest/bid-id-invalid.json
+++ b/exchange/exchangetest/bid-id-invalid.json
@@ -37,6 +37,18 @@
               "publisher": "",
               "withcategory": true
             },
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true,
             "appendbiddernames": true
           }
         }
@@ -154,6 +166,18 @@
                   "publisher": "",
                   "withcategory": true
                 },
+                "pricegranularity": {
+                  "precision": 2,
+                  "ranges": [
+                    {
+                      "min": 0,
+                      "max": 20,
+                      "increment": 0.1
+                    }
+                  ]
+                },
+                "includewinners": true,
+                "includebidderkeys": true,
                 "appendbiddernames": true
               }
             }
@@ -170,4 +194,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/bid-id-valid.json b/exchange/exchangetest/bid-id-valid.json
index 85ceb883..397d232a 100644
--- a/exchange/exchangetest/bid-id-valid.json
+++ b/exchange/exchangetest/bid-id-valid.json
@@ -37,6 +37,18 @@
               "publisher": "",
               "withcategory": true
             },
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true,
             "appendbiddernames": true
           }
         }
@@ -150,6 +162,18 @@
           "ext": {
             "prebid": {
               "targeting": {
+                "pricegranularity": {
+                  "precision": 2,
+                  "ranges": [
+                    {
+                      "min": 0,
+                      "max": 20,
+                      "increment": 0.1
+                    }
+                  ]
+                },
+                "includewinners": true,
+                "includebidderkeys": true,
                 "includebrandcategory": {
                   "primaryadserver": 1,
                   "publisher": "",
@@ -163,4 +187,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/debuglog_disabled.json b/exchange/exchangetest/debuglog_disabled.json
index fc3226e3..d803759d 100644
--- a/exchange/exchangetest/debuglog_disabled.json
+++ b/exchange/exchangetest/debuglog_disabled.json
@@ -55,6 +55,18 @@
       "ext": {
         "prebid": {
           "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true,
             "includebrandcategory": {
               "primaryadserver": 1,
               "publisher": "",
@@ -233,6 +245,18 @@
           "ext": {
             "prebid": {
               "targeting": {
+                "pricegranularity": {
+                  "precision": 2,
+                  "ranges": [
+                    {
+                      "min": 0,
+                      "max": 20,
+                      "increment": 0.1
+                    }
+                  ]
+                },
+                "includewinners": true,
+                "includebidderkeys": true,
                 "includebrandcategory": {
                   "primaryadserver": 1,
                   "publisher": "",
@@ -245,4 +269,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/debuglog_enabled.json b/exchange/exchangetest/debuglog_enabled.json
index af77a640..054737e6 100644
--- a/exchange/exchangetest/debuglog_enabled.json
+++ b/exchange/exchangetest/debuglog_enabled.json
@@ -57,6 +57,18 @@
       "ext": {
         "prebid": {
           "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true,
             "includebrandcategory": {
               "primaryadserver": 1,
               "publisher": "",
@@ -235,6 +247,18 @@
           "ext": {
             "prebid": {
               "targeting": {
+                "pricegranularity": {
+                  "precision": 2,
+                  "ranges": [
+                    {
+                      "min": 0,
+                      "max": 20,
+                      "increment": 0.1
+                    }
+                  ]
+                },
+                "includewinners": true,
+                "includebidderkeys": true,
                 "includebrandcategory": {
                   "primaryadserver": 1,
                   "publisher": "",
@@ -247,4 +271,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/debuglog_enabled_no_bids.json b/exchange/exchangetest/debuglog_enabled_no_bids.json
index f0ecffae..cf2a821d 100644
--- a/exchange/exchangetest/debuglog_enabled_no_bids.json
+++ b/exchange/exchangetest/debuglog_enabled_no_bids.json
@@ -56,6 +56,18 @@
       "ext": {
         "prebid": {
           "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": false,
+            "includebidderkeys": true,
             "includebrandcategory": {
               "primaryadserver": 1,
               "publisher": "",
diff --git a/exchange/exchangetest/events-vast-account-off-request-off.json b/exchange/exchangetest/events-vast-account-off-request-off.json
index ac9b0c9d..a94546d2 100644
--- a/exchange/exchangetest/events-vast-account-off-request-off.json
+++ b/exchange/exchangetest/events-vast-account-off-request-off.json
@@ -29,7 +29,17 @@
         "prebid": {
           "targeting": {
             "includewinners": true,
-            "includebidderkeys": false
+            "includebidderkeys": false,
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            }
           }
         }
       }
@@ -167,4 +177,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/events-vast-account-off-request-on.json b/exchange/exchangetest/events-vast-account-off-request-on.json
index 23d1924a..275b8aef 100644
--- a/exchange/exchangetest/events-vast-account-off-request-on.json
+++ b/exchange/exchangetest/events-vast-account-off-request-on.json
@@ -32,6 +32,16 @@
       "ext": {
         "prebid": {
           "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
             "includewinners": true,
             "includebidderkeys": false
           },
@@ -178,4 +188,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/events-vast-account-on-request-off.json b/exchange/exchangetest/events-vast-account-on-request-off.json
index e75e1ec6..e271ecf5 100644
--- a/exchange/exchangetest/events-vast-account-on-request-off.json
+++ b/exchange/exchangetest/events-vast-account-on-request-off.json
@@ -28,6 +28,16 @@
       "ext": {
         "prebid": {
           "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
             "includewinners": true,
             "includebidderkeys": false
           },
@@ -170,4 +180,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/extra-bids-with-aliases-adaptercode.json b/exchange/exchangetest/extra-bids-with-aliases-adaptercode.json
index 7b49cb80..b46f54c6 100644
--- a/exchange/exchangetest/extra-bids-with-aliases-adaptercode.json
+++ b/exchange/exchangetest/extra-bids-with-aliases-adaptercode.json
@@ -44,6 +44,16 @@
       "ext": {
         "prebid": {
           "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
             "includewinners": true,
             "includebidderkeys": true
           },
@@ -244,4 +254,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/extra-bids.json b/exchange/exchangetest/extra-bids.json
index 67a9b085..096a26d2 100644
--- a/exchange/exchangetest/extra-bids.json
+++ b/exchange/exchangetest/extra-bids.json
@@ -50,6 +50,16 @@
       "ext": {
         "prebid": {
           "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
             "includewinners": true,
             "includebidderkeys": true
           },
@@ -458,4 +468,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/include-brand-category.json b/exchange/exchangetest/include-brand-category.json
index 81b44b4c..9d537f74 100644
--- a/exchange/exchangetest/include-brand-category.json
+++ b/exchange/exchangetest/include-brand-category.json
@@ -44,6 +44,18 @@
       "ext": {
         "prebid": {
           "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true,
             "includebrandcategory": {
               "primaryadserver": 1,
               "publisher": "",
@@ -176,4 +188,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/request-multi-bidders-debug-info.json b/exchange/exchangetest/request-multi-bidders-debug-info.json
index cb3c3456..368c6e45 100644
--- a/exchange/exchangetest/request-multi-bidders-debug-info.json
+++ b/exchange/exchangetest/request-multi-bidders-debug-info.json
@@ -55,6 +55,18 @@
               15,
               30
             ],
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true,
             "includebrandcategory": {
               "primaryadserver": 1,
               "publisher": "",
@@ -249,6 +261,18 @@
                   15,
                   30
                 ],
+                "pricegranularity": {
+                  "precision": 2,
+                  "ranges": [
+                    {
+                      "min": 0,
+                      "max": 20,
+                      "increment": 0.1
+                    }
+                  ]
+                },
+                "includewinners": true,
+                "includebidderkeys": true,
                 "includebrandcategory": {
                   "primaryadserver": 1,
                   "publisher": "",
@@ -262,4 +286,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/request-multi-bidders-one-no-resp.json b/exchange/exchangetest/request-multi-bidders-one-no-resp.json
index b5a1da82..8ec9034a 100644
--- a/exchange/exchangetest/request-multi-bidders-one-no-resp.json
+++ b/exchange/exchangetest/request-multi-bidders-one-no-resp.json
@@ -54,6 +54,18 @@
               15,
               30
             ],
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true,
             "includebrandcategory": {
               "primaryadserver": 1,
               "publisher": "",
@@ -137,4 +149,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/targeting-cache-vast-banner.json b/exchange/exchangetest/targeting-cache-vast-banner.json
index 39f9a005..43b65e59 100644
--- a/exchange/exchangetest/targeting-cache-vast-banner.json
+++ b/exchange/exchangetest/targeting-cache-vast-banner.json
@@ -28,7 +28,20 @@
           "cache": {
             "vastxml": {}
           },
-          "targeting": {}
+          "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true
+          }
         }
       }
     }
@@ -95,4 +108,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/targeting-cache-vast.json b/exchange/exchangetest/targeting-cache-vast.json
index 53742086..2f59ba30 100644
--- a/exchange/exchangetest/targeting-cache-vast.json
+++ b/exchange/exchangetest/targeting-cache-vast.json
@@ -29,7 +29,20 @@
           "cache": {
             "vastxml": {}
           },
-          "targeting": {}
+          "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includebidderkeys": true,
+            "includewinners": true
+          }
         }
       }
     }
@@ -106,4 +119,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/targeting-cache-zero.json b/exchange/exchangetest/targeting-cache-zero.json
index 2d422d6d..ea062fbe 100644
--- a/exchange/exchangetest/targeting-cache-zero.json
+++ b/exchange/exchangetest/targeting-cache-zero.json
@@ -31,7 +31,20 @@
             "bids": {},
             "vastxml": {}
           },
-          "targeting": {}
+          "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true
+          }
         }
       }
     }
@@ -110,4 +123,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/targeting-mobile.json b/exchange/exchangetest/targeting-mobile.json
index deea901a..914a557b 100644
--- a/exchange/exchangetest/targeting-mobile.json
+++ b/exchange/exchangetest/targeting-mobile.json
@@ -49,7 +49,20 @@
       ],
       "ext": {
         "prebid": {
-          "targeting": {}
+          "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true
+          }
         }
       }
     }
@@ -231,4 +244,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/targeting-no-winners.json b/exchange/exchangetest/targeting-no-winners.json
index 729575ee..6ab69fb4 100644
--- a/exchange/exchangetest/targeting-no-winners.json
+++ b/exchange/exchangetest/targeting-no-winners.json
@@ -50,7 +50,18 @@
       "ext": {
         "prebid": {
           "targeting": {
-            "includewinners": false
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": false,
+            "includebidderkeys": true
           }
         }
       }
@@ -218,4 +229,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/targeting-only-winners.json b/exchange/exchangetest/targeting-only-winners.json
index 900ea1fd..f9f96515 100644
--- a/exchange/exchangetest/targeting-only-winners.json
+++ b/exchange/exchangetest/targeting-only-winners.json
@@ -50,6 +50,17 @@
       "ext": {
         "prebid": {
           "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
             "includebidderkeys": false
           }
         }
@@ -211,4 +222,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/exchangetest/targeting-with-winners.json b/exchange/exchangetest/targeting-with-winners.json
index ca1b1afe..d1a0f49f 100644
--- a/exchange/exchangetest/targeting-with-winners.json
+++ b/exchange/exchangetest/targeting-with-winners.json
@@ -49,7 +49,20 @@
       ],
       "ext": {
         "prebid": {
-          "targeting": {}
+          "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": true
+          }
         }
       }
     }
@@ -226,4 +239,4 @@
       ]
     }
   }
-}
\ No newline at end of file
+}
diff --git a/exchange/price_granularity_test.go b/exchange/price_granularity_test.go
index 6349713e..50e83778 100644
--- a/exchange/price_granularity_test.go
+++ b/exchange/price_granularity_test.go
@@ -5,17 +5,19 @@ import (
 	"testing"
 
 	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/util/ptrutil"
 	"github.com/stretchr/testify/assert"
 )
 
 func TestGetPriceBucketString(t *testing.T) {
-	low := openrtb_ext.PriceGranularityFromString("low")
-	medium := openrtb_ext.PriceGranularityFromString("medium")
-	high := openrtb_ext.PriceGranularityFromString("high")
-	auto := openrtb_ext.PriceGranularityFromString("auto")
-	dense := openrtb_ext.PriceGranularityFromString("dense")
+	low, _ := openrtb_ext.NewPriceGranularityFromLegacyID("low")
+	medium, _ := openrtb_ext.NewPriceGranularityFromLegacyID("medium")
+	high, _ := openrtb_ext.NewPriceGranularityFromLegacyID("high")
+	auto, _ := openrtb_ext.NewPriceGranularityFromLegacyID("auto")
+	dense, _ := openrtb_ext.NewPriceGranularityFromLegacyID("dense")
+
 	custom1 := openrtb_ext.PriceGranularity{
-		Precision: 2,
+		Precision: ptrutil.ToPtr(2),
 		Ranges: []openrtb_ext.GranularityRange{
 			{
 				Min:       0.0,
@@ -31,7 +33,7 @@ func TestGetPriceBucketString(t *testing.T) {
 	}
 
 	custom2 := openrtb_ext.PriceGranularity{
-		Precision: 2,
+		Precision: ptrutil.ToPtr(2),
 		Ranges: []openrtb_ext.GranularityRange{
 			{
 				Min:       0.0,
@@ -97,17 +99,17 @@ func TestGetPriceBucketString(t *testing.T) {
 			testCases: []aTest{
 				{
 					"Negative precision defaults to number of digits already in CPM float",
-					openrtb_ext.PriceGranularity{Precision: -1, Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 0.05}}},
+					openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(-1), Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 0.05}}},
 					"1.85",
 				},
 				{
 					"Precision value equals zero, we expect to round up to the nearest integer",
-					openrtb_ext.PriceGranularity{Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 0.05}}},
+					openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(0), Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 0.05}}},
 					"2",
 				},
 				{
 					"Largest precision value PBS supports 15",
-					openrtb_ext.PriceGranularity{Precision: 15, Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 0.05}}},
+					openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(15), Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 0.05}}},
 					"1.850000000000000",
 				},
 			},
@@ -118,17 +120,17 @@ func TestGetPriceBucketString(t *testing.T) {
 			testCases: []aTest{
 				{
 					"Negative increment, return empty string",
-					openrtb_ext.PriceGranularity{Precision: 2, Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: -0.05}}},
+					openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: -0.05}}},
 					"",
 				},
 				{
 					"Zero increment, return empty string",
-					openrtb_ext.PriceGranularity{Precision: 2, Ranges: []openrtb_ext.GranularityRange{{Max: 5}}},
+					openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{{Max: 5}}},
 					"",
 				},
 				{
 					"Increment value is greater than CPM itself, return zero float value",
-					openrtb_ext.PriceGranularity{Precision: 2, Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 1.877}}},
+					openrtb_ext.PriceGranularity{Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{{Max: 5, Increment: 1.877}}},
 					"0.00",
 				},
 			},
@@ -151,9 +153,9 @@ func TestGetPriceBucketString(t *testing.T) {
 	}
 
 	for _, testGroup := range testGroups {
-		for _, test := range testGroup.testCases {
-			priceBucket := GetPriceBucket(testGroup.cpm, test.granularity)
-
+		for i, test := range testGroup.testCases {
+			var priceBucket string
+			assert.NotPanics(t, func() { priceBucket = GetPriceBucket(testGroup.cpm, test.granularity) }, "Group: %s Granularity: %d", testGroup.groupDesc, i)
 			assert.Equal(t, test.expectedPriceBucket, priceBucket, "Group: %s Granularity: %s :: Expected %s, got %s from %f", testGroup.groupDesc, test.granularityId, test.expectedPriceBucket, priceBucket, testGroup.cpm)
 		}
 	}
diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go
index 809b58af..19b6ea5e 100644
--- a/exchange/targeting_test.go
+++ b/exchange/targeting_test.go
@@ -158,13 +158,13 @@ func buildAdapterMap(bids map[openrtb_ext.BidderName][]*openrtb2.Bid, mockServer
 func buildTargetingExt(includeCache bool, includeWinners bool, includeBidderKeys bool) json.RawMessage {
 	var targeting string
 	if includeWinners && includeBidderKeys {
-		targeting = "{}"
+		targeting = `{"pricegranularity":{"precision":2,"ranges": [{"min": 0,"max": 20,"increment": 0.1}]},"includewinners": true, "includebidderkeys": true}`
 	} else if !includeWinners && includeBidderKeys {
-		targeting = `{"includewinners": false}`
+		targeting = `{"precision":2,"includewinners": false}`
 	} else if includeWinners && !includeBidderKeys {
-		targeting = `{"includebidderkeys": false}`
+		targeting = `{"precision":2,"includebidderkeys": false}`
 	} else {
-		targeting = `{"includewinners": false, "includebidderkeys": false}`
+		targeting = `{"precision":2,"includewinners": false, "includebidderkeys": false}`
 	}
 
 	if includeCache {
@@ -286,15 +286,23 @@ var bid084 *openrtb2.Bid = &openrtb2.Bid{
 	Price: 0.84,
 }
 
-var truncateTargetAttrValue10 int = 10
-var truncateTargetAttrValue5 int = 5
-var truncateTargetAttrValue25 int = 25
-var truncateTargetAttrValueNegative int = -1
+var (
+	truncateTargetAttrValue10       int = 10
+	truncateTargetAttrValue5        int = 5
+	truncateTargetAttrValue25       int = 25
+	truncateTargetAttrValueNegative int = -1
+)
+
+func lookupPriceGranularity(v string) openrtb_ext.PriceGranularity {
+	priceGranularity, _ := openrtb_ext.NewPriceGranularityFromLegacyID(v)
+	return priceGranularity
+}
+
 var TargetingTests []TargetingTestData = []TargetingTestData{
 	{
 		Description: "Targeting winners only (most basic targeting example)",
 		TargetData: targetData{
-			priceGranularity: openrtb_ext.PriceGranularityFromString("med"),
+			priceGranularity: lookupPriceGranularity("med"),
 			includeWinners:   true,
 		},
 		Auction: auction{
@@ -325,7 +333,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 	{
 		Description: "Targeting on bidders only",
 		TargetData: targetData{
-			priceGranularity:  openrtb_ext.PriceGranularityFromString("med"),
+			priceGranularity:  lookupPriceGranularity("med"),
 			includeBidderKeys: true,
 		},
 		Auction: auction{
@@ -359,7 +367,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 	{
 		Description: "Full basic targeting with hd_format",
 		TargetData: targetData{
-			priceGranularity:  openrtb_ext.PriceGranularityFromString("med"),
+			priceGranularity:  lookupPriceGranularity("med"),
 			includeWinners:    true,
 			includeBidderKeys: true,
 			includeFormat:     true,
@@ -400,7 +408,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 	{
 		Description: "Cache and deal targeting test",
 		TargetData: targetData{
-			priceGranularity:  openrtb_ext.PriceGranularityFromString("med"),
+			priceGranularity:  lookupPriceGranularity("med"),
 			includeBidderKeys: true,
 			cacheHost:         "cache.prebid.com",
 			cachePath:         "cache",
@@ -447,7 +455,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 	{
 		Description: "bidder with no dealID should not have deal targeting",
 		TargetData: targetData{
-			priceGranularity:  openrtb_ext.PriceGranularityFromString("med"),
+			priceGranularity:  lookupPriceGranularity("med"),
 			includeBidderKeys: true,
 		},
 		Auction: auction{
@@ -473,7 +481,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 	{
 		Description: "Truncate Targeting Attribute value is given and is less than const MaxKeyLength",
 		TargetData: targetData{
-			priceGranularity:  openrtb_ext.PriceGranularityFromString("med"),
+			priceGranularity:  lookupPriceGranularity("med"),
 			includeBidderKeys: true,
 		},
 		Auction: auction{
@@ -507,7 +515,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 	{
 		Description: "Truncate Targeting Attribute value is given and is greater than const MaxKeyLength",
 		TargetData: targetData{
-			priceGranularity:  openrtb_ext.PriceGranularityFromString("med"),
+			priceGranularity:  lookupPriceGranularity("med"),
 			includeBidderKeys: true,
 		},
 		Auction: auction{
@@ -541,7 +549,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 	{
 		Description: "Truncate Targeting Attribute value is given and is negative",
 		TargetData: targetData{
-			priceGranularity:  openrtb_ext.PriceGranularityFromString("med"),
+			priceGranularity:  lookupPriceGranularity("med"),
 			includeBidderKeys: true,
 		},
 		Auction: auction{
@@ -575,7 +583,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 	{
 		Description: "Check that key gets truncated properly when value is smaller than key",
 		TargetData: targetData{
-			priceGranularity: openrtb_ext.PriceGranularityFromString("med"),
+			priceGranularity: lookupPriceGranularity("med"),
 			includeWinners:   true,
 		},
 		Auction: auction{
@@ -606,7 +614,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 	{
 		Description: "Check that key gets truncated properly when value is greater than key",
 		TargetData: targetData{
-			priceGranularity: openrtb_ext.PriceGranularityFromString("med"),
+			priceGranularity: lookupPriceGranularity("med"),
 			includeWinners:   true,
 		},
 		Auction: auction{
@@ -637,7 +645,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 	{
 		Description: "Check that key gets truncated properly when value is negative",
 		TargetData: targetData{
-			priceGranularity: openrtb_ext.PriceGranularityFromString("med"),
+			priceGranularity: lookupPriceGranularity("med"),
 			includeWinners:   true,
 		},
 		Auction: auction{
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 257449bd..4a2d5fe7 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -15,6 +15,7 @@ import (
 	"github.com/prebid/prebid-server/gdpr"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/util/ptrutil"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 )
@@ -1756,12 +1757,12 @@ func TestGetExtTargetData(t *testing.T) {
 				requestExt: &openrtb_ext.ExtRequest{
 					Prebid: openrtb_ext.ExtRequestPrebid{
 						Targeting: &openrtb_ext.ExtRequestTargeting{
-							PriceGranularity: openrtb_ext.PriceGranularity{
-								Precision: 2,
+							PriceGranularity: &openrtb_ext.PriceGranularity{
+								Precision: ptrutil.ToPtr(2),
 								Ranges:    []openrtb_ext.GranularityRange{{Min: 0.00, Max: 5.00, Increment: 1.00}},
 							},
-							IncludeWinners:    true,
-							IncludeBidderKeys: true,
+							IncludeWinners:    ptrutil.ToPtr(true),
+							IncludeBidderKeys: ptrutil.ToPtr(true),
 						},
 					},
 				},
@@ -1773,7 +1774,7 @@ func TestGetExtTargetData(t *testing.T) {
 			outTest{
 				targetData: &targetData{
 					priceGranularity: openrtb_ext.PriceGranularity{
-						Precision: 2,
+						Precision: ptrutil.ToPtr(2),
 						Ranges:    []openrtb_ext.GranularityRange{{Min: 0.00, Max: 5.00, Increment: 1.00}},
 					},
 					includeWinners:    true,
diff --git a/openrtb_ext/convert_down_test.go b/openrtb_ext/convert_down_test.go
index 44981546..bad07da1 100644
--- a/openrtb_ext/convert_down_test.go
+++ b/openrtb_ext/convert_down_test.go
@@ -4,19 +4,20 @@ import (
 	"encoding/json"
 	"testing"
 
+	"github.com/prebid/openrtb/v17/adcom1"
 	"github.com/prebid/openrtb/v17/openrtb2"
 	"github.com/stretchr/testify/assert"
 )
 
 func TestConvertDownTo25(t *testing.T) {
 	testCases := []struct {
-		description     string
+		name            string
 		givenRequest    openrtb2.BidRequest
 		expectedRequest openrtb2.BidRequest
 		expectedErr     string
 	}{
 		{
-			description: "2.6 -> 2.5",
+			name: "2.6-to-2.5",
 			givenRequest: openrtb2.BidRequest{
 				ID:     "anyID",
 				Imp:    []openrtb2.Imp{{Rwdd: 1}},
@@ -33,7 +34,30 @@ func TestConvertDownTo25(t *testing.T) {
 			},
 		},
 		{
-			description: "2.6 -> 2.5 + Other Ext Fields",
+			name: "2.6-dropped", // integration with clear26Fields
+			givenRequest: openrtb2.BidRequest{
+				ID:     "anyID",
+				CatTax: adcom1.CatTaxIABContent10,
+				Device: &openrtb2.Device{LangB: "anyLang"},
+			},
+			expectedRequest: openrtb2.BidRequest{
+				ID:     "anyID",
+				Device: &openrtb2.Device{},
+			},
+		},
+		{
+			name: "2.6-202211-dropped", // integration with clear202211Fields
+			givenRequest: openrtb2.BidRequest{
+				ID:  "anyID",
+				App: &openrtb2.App{InventoryPartnerDomain: "anyDomain"},
+			},
+			expectedRequest: openrtb2.BidRequest{
+				ID:  "anyID",
+				App: &openrtb2.App{},
+			},
+		},
+		{
+			name: "2.6-to-2.5-OtherExtFields",
 			givenRequest: openrtb2.BidRequest{
 				ID:     "anyID",
 				Imp:    []openrtb2.Imp{{Rwdd: 1, Ext: json.RawMessage(`{"other":"otherImp"}`)}},
@@ -52,7 +76,7 @@ func TestConvertDownTo25(t *testing.T) {
 			},
 		},
 		{
-			description: "Malformed - SChain",
+			name: "malformed-shhain",
 			givenRequest: openrtb2.BidRequest{
 				ID:     "anyID",
 				Source: &openrtb2.Source{SChain: &openrtb2.SupplyChain{Complete: 1, Nodes: []openrtb2.SupplyChainNode{}, Ver: "2"}, Ext: json.RawMessage(`malformed`)},
@@ -60,7 +84,7 @@ func TestConvertDownTo25(t *testing.T) {
 			expectedErr: "invalid character 'm' looking for beginning of value",
 		},
 		{
-			description: "Malformed - GDPR",
+			name: "malformed-gdpr",
 			givenRequest: openrtb2.BidRequest{
 				ID:   "anyID",
 				Regs: &openrtb2.Regs{GDPR: openrtb2.Int8Ptr(1), Ext: json.RawMessage(`malformed`)},
@@ -68,7 +92,7 @@ func TestConvertDownTo25(t *testing.T) {
 			expectedErr: "invalid character 'm' looking for beginning of value",
 		},
 		{
-			description: "Malformed - Consent",
+			name: "malformed-consent",
 			givenRequest: openrtb2.BidRequest{
 				ID:   "anyID",
 				User: &openrtb2.User{Consent: "1", Ext: json.RawMessage(`malformed`)},
@@ -76,7 +100,7 @@ func TestConvertDownTo25(t *testing.T) {
 			expectedErr: "invalid character 'm' looking for beginning of value",
 		},
 		{
-			description: "Malformed - USPrivacy",
+			name: "malformed-usprivacy",
 			givenRequest: openrtb2.BidRequest{
 				ID:   "anyID",
 				Regs: &openrtb2.Regs{USPrivacy: "3", Ext: json.RawMessage(`malformed`)},
@@ -84,7 +108,7 @@ func TestConvertDownTo25(t *testing.T) {
 			expectedErr: "invalid character 'm' looking for beginning of value",
 		},
 		{
-			description: "Malformed - EID",
+			name: "malformed-eid",
 			givenRequest: openrtb2.BidRequest{
 				ID:   "anyID",
 				User: &openrtb2.User{EIDs: []openrtb2.EID{{Source: "42"}}, Ext: json.RawMessage(`malformed`)},
@@ -92,7 +116,7 @@ func TestConvertDownTo25(t *testing.T) {
 			expectedErr: "invalid character 'm' looking for beginning of value",
 		},
 		{
-			description: "Malformed - Imp",
+			name: "malformed-imp",
 			givenRequest: openrtb2.BidRequest{
 				ID:  "anyID",
 				Imp: []openrtb2.Imp{{Rwdd: 1, Ext: json.RawMessage(`malformed`)}},
@@ -102,14 +126,17 @@ func TestConvertDownTo25(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		w := &RequestWrapper{BidRequest: &test.givenRequest}
-		err := ConvertDownTo25(w)
-		if len(test.expectedErr) > 0 {
-			assert.EqualError(t, err, test.expectedErr, test.description)
-		} else {
-			assert.NoError(t, w.RebuildRequest(), test.description)
-			assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
-		}
+		t.Run(test.name, func(t *testing.T) {
+			w := &RequestWrapper{BidRequest: &test.givenRequest}
+			err := ConvertDownTo25(w)
+
+			if len(test.expectedErr) > 0 {
+				assert.EqualError(t, err, test.expectedErr, "error")
+			} else {
+				assert.NoError(t, w.RebuildRequest(), "error")
+				assert.Equal(t, test.expectedRequest, *w.BidRequest, "result")
+			}
+		})
 	}
 }
 
@@ -121,189 +148,197 @@ func TestMoveSupplyChainFrom26To25(t *testing.T) {
 	)
 
 	testCases := []struct {
-		description     string
+		name            string
 		givenRequest    openrtb2.BidRequest
 		expectedRequest openrtb2.BidRequest
 		expectedErr     string
 	}{
 		{
-			description:     "Not Present - Source",
+			name:            "notpresent-source",
 			givenRequest:    openrtb2.BidRequest{},
 			expectedRequest: openrtb2.BidRequest{},
 		},
 		{
-			description:     "Not Present - Source Schain",
+			name:            "notpresent-source-schain",
 			givenRequest:    openrtb2.BidRequest{Source: &openrtb2.Source{}},
 			expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{}},
 		},
 		{
-			description:     "2.6 Migrated To 2.5",
+			name:            "2.6-migratedto-2.5",
 			givenRequest:    openrtb2.BidRequest{Source: &openrtb2.Source{SChain: schain1}},
 			expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: schain1Json}},
 		},
 		{
-			description:     "2.5 Overwritten",
+			name:            "2.5-overwritten",
 			givenRequest:    openrtb2.BidRequest{Source: &openrtb2.Source{SChain: schain1, Ext: schain2Json}},
 			expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: schain1Json}},
 		},
 		{
-			description:  "Malformed",
+			name:         "malformed",
 			givenRequest: openrtb2.BidRequest{Source: &openrtb2.Source{SChain: schain1, Ext: json.RawMessage(`malformed`)}},
 			expectedErr:  "invalid character 'm' looking for beginning of value",
 		},
 	}
 
 	for _, test := range testCases {
-		w := &RequestWrapper{BidRequest: &test.givenRequest}
-		err := moveSupplyChainFrom26To25(w)
-
-		if len(test.expectedErr) > 0 {
-			assert.EqualError(t, err, test.expectedErr, test.description)
-		} else {
-			assert.NoError(t, w.RebuildRequest(), test.description)
-			assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
-		}
+		t.Run(test.name, func(t *testing.T) {
+			w := &RequestWrapper{BidRequest: &test.givenRequest}
+			err := moveSupplyChainFrom26To25(w)
+
+			if len(test.expectedErr) > 0 {
+				assert.EqualError(t, err, test.expectedErr, "error")
+			} else {
+				assert.NoError(t, w.RebuildRequest(), "error")
+				assert.Equal(t, test.expectedRequest, *w.BidRequest, "result")
+			}
+		})
 	}
 }
 
 func TestMoveGDPRFrom26To25(t *testing.T) {
 	testCases := []struct {
-		description     string
+		name            string
 		givenRequest    openrtb2.BidRequest
 		expectedRequest openrtb2.BidRequest
 		expectedErr     string
 	}{
 		{
-			description:     "Not Present - Regs",
+			name:            "notpresent-regs",
 			givenRequest:    openrtb2.BidRequest{},
 			expectedRequest: openrtb2.BidRequest{},
 		},
 		{
-			description:     "Not Present - Regs GDPR",
+			name:            "notpresent-regs-gdpr",
 			givenRequest:    openrtb2.BidRequest{Regs: &openrtb2.Regs{}},
 			expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{}},
 		},
 		{
-			description:     "2.6 Migrated To 2.5",
+			name:            "2.6-migratedto-2.5",
 			givenRequest:    openrtb2.BidRequest{Regs: &openrtb2.Regs{GDPR: openrtb2.Int8Ptr(0)}},
 			expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gdpr":0}`)}},
 		},
 		{
-			description:     "2.5 Overwritten",
+			name:            "2.5-overwritten",
 			givenRequest:    openrtb2.BidRequest{Regs: &openrtb2.Regs{GDPR: openrtb2.Int8Ptr(0), Ext: json.RawMessage(`{"gdpr":1}`)}},
 			expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gdpr":0}`)}},
 		},
 		{
-			description:  "Malformed",
+			name:         "malformed",
 			givenRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{GDPR: openrtb2.Int8Ptr(0), Ext: json.RawMessage(`malformed`)}},
 			expectedErr:  "invalid character 'm' looking for beginning of value",
 		},
 	}
 
 	for _, test := range testCases {
-		w := &RequestWrapper{BidRequest: &test.givenRequest}
-		err := moveGDPRFrom26To25(w)
-
-		if len(test.expectedErr) > 0 {
-			assert.EqualError(t, err, test.expectedErr, test.description)
-		} else {
-			assert.NoError(t, w.RebuildRequest(), test.description)
-			assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
-		}
+		t.Run(test.name, func(t *testing.T) {
+			w := &RequestWrapper{BidRequest: &test.givenRequest}
+			err := moveGDPRFrom26To25(w)
+
+			if len(test.expectedErr) > 0 {
+				assert.EqualError(t, err, test.expectedErr, "error")
+			} else {
+				assert.NoError(t, w.RebuildRequest(), "error")
+				assert.Equal(t, test.expectedRequest, *w.BidRequest, "result")
+			}
+		})
 	}
 }
 
 func TestMoveConsentFrom26To25(t *testing.T) {
 	testCases := []struct {
-		description     string
+		name            string
 		givenRequest    openrtb2.BidRequest
 		expectedRequest openrtb2.BidRequest
 		expectedErr     string
 	}{
 		{
-			description:     "Not Present - User",
+			name:            "notpresent-user",
 			givenRequest:    openrtb2.BidRequest{},
 			expectedRequest: openrtb2.BidRequest{},
 		},
 		{
-			description:     "Not Present - User Consent",
+			name:            "notpresent-user-consent",
 			givenRequest:    openrtb2.BidRequest{User: &openrtb2.User{}},
 			expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}},
 		},
 		{
-			description:     "2.6 Migrated To 2.5",
+			name:            "2.6-migratedto-2.5",
 			givenRequest:    openrtb2.BidRequest{User: &openrtb2.User{Consent: "1"}},
 			expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"1"}`)}},
 		},
 		{
-			description:     "2.5 Overwritten",
+			name:            "2.5-overwritten",
 			givenRequest:    openrtb2.BidRequest{User: &openrtb2.User{Consent: "1", Ext: json.RawMessage(`{"consent":"2"}`)}},
 			expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"1"}`)}},
 		},
 		{
-			description:  "Malformed",
+			name:         "malformed",
 			givenRequest: openrtb2.BidRequest{User: &openrtb2.User{Consent: "1", Ext: json.RawMessage(`malformed`)}},
 			expectedErr:  "invalid character 'm' looking for beginning of value",
 		},
 	}
 
 	for _, test := range testCases {
-		w := &RequestWrapper{BidRequest: &test.givenRequest}
-		err := moveConsentFrom26To25(w)
-
-		if len(test.expectedErr) > 0 {
-			assert.EqualError(t, err, test.expectedErr, test.description)
-		} else {
-			assert.NoError(t, w.RebuildRequest(), test.description)
-			assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
-		}
+		t.Run(test.name, func(t *testing.T) {
+			w := &RequestWrapper{BidRequest: &test.givenRequest}
+			err := moveConsentFrom26To25(w)
+
+			if len(test.expectedErr) > 0 {
+				assert.EqualError(t, err, test.expectedErr, "error")
+			} else {
+				assert.NoError(t, w.RebuildRequest(), "error")
+				assert.Equal(t, test.expectedRequest, *w.BidRequest, "result")
+			}
+		})
 	}
 }
 
 func TestMoveUSPrivacyFrom26To25(t *testing.T) {
 	testCases := []struct {
-		description     string
+		name            string
 		givenRequest    openrtb2.BidRequest
 		expectedRequest openrtb2.BidRequest
 		expectedErr     string
 	}{
 		{
-			description:     "Not Present - Regs",
+			name:            "notpresent-regs",
 			givenRequest:    openrtb2.BidRequest{},
 			expectedRequest: openrtb2.BidRequest{},
 		},
 		{
-			description:     "Not Present - Regs USPrivacy",
+			name:            "notpresent-regs-usprivacy",
 			givenRequest:    openrtb2.BidRequest{Regs: &openrtb2.Regs{}},
 			expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{}},
 		},
 		{
-			description:     "2.6 Migrated To 2.5",
+			name:            "2.6-migratedto-2.5",
 			givenRequest:    openrtb2.BidRequest{Regs: &openrtb2.Regs{USPrivacy: "1"}},
 			expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"1"}`)}},
 		},
 		{
-			description:     "2.5 Overwritten",
+			name:            "2.5-overwritten",
 			givenRequest:    openrtb2.BidRequest{Regs: &openrtb2.Regs{USPrivacy: "1", Ext: json.RawMessage(`{"us_privacy":"2"}`)}},
 			expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"1"}`)}},
 		},
 		{
-			description:  "Malformed",
+			name:         "malformed",
 			givenRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{USPrivacy: "1", Ext: json.RawMessage(`malformed`)}},
 			expectedErr:  "invalid character 'm' looking for beginning of value",
 		},
 	}
 
 	for _, test := range testCases {
-		w := &RequestWrapper{BidRequest: &test.givenRequest}
-		err := moveUSPrivacyFrom26To25(w)
-
-		if len(test.expectedErr) > 0 {
-			assert.EqualError(t, err, test.expectedErr, test.description)
-		} else {
-			assert.NoError(t, w.RebuildRequest(), test.description)
-			assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
-		}
+		t.Run(test.name, func(t *testing.T) {
+			w := &RequestWrapper{BidRequest: &test.givenRequest}
+			err := moveUSPrivacyFrom26To25(w)
+
+			if len(test.expectedErr) > 0 {
+				assert.EqualError(t, err, test.expectedErr, "error")
+			} else {
+				assert.NoError(t, w.RebuildRequest(), "error")
+				assert.Equal(t, test.expectedRequest, *w.BidRequest, "result")
+			}
+		})
 	}
 }
 
@@ -315,94 +350,317 @@ func TestMoveEIDFrom26To25(t *testing.T) {
 	)
 
 	testCases := []struct {
-		description     string
+		name            string
 		givenRequest    openrtb2.BidRequest
 		expectedRequest openrtb2.BidRequest
 		expectedErr     string
 	}{
 		{
-			description:     "Not Present - User",
+			name:            "notpresent-user",
 			givenRequest:    openrtb2.BidRequest{},
 			expectedRequest: openrtb2.BidRequest{},
 		},
 		{
-			description:     "Not Present - User EIDs",
+			name:            "notpresent-user-eids",
 			givenRequest:    openrtb2.BidRequest{User: &openrtb2.User{}},
 			expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}},
 		},
 		{
-			description:     "2.6 Migrated To 2.5",
+			name:            "2.6-migratedto-2.5",
 			givenRequest:    openrtb2.BidRequest{User: &openrtb2.User{EIDs: eid1}},
 			expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: eid1Json}},
 		},
 		{
-			description:     "2.6 Migrated To 2.5 - Empty",
+			name:            "2.6-migratedto-2.5-empty",
 			givenRequest:    openrtb2.BidRequest{User: &openrtb2.User{EIDs: []openrtb2.EID{}}},
 			expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{}},
 		},
 		{
-			description:     "2.5 Overwritten",
+			name:            "2.5-overwritten",
 			givenRequest:    openrtb2.BidRequest{User: &openrtb2.User{EIDs: eid1, Ext: eid2Json}},
 			expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: eid1Json}},
 		},
 		{
-			description:  "Malformed",
+			name:         "malformed",
 			givenRequest: openrtb2.BidRequest{User: &openrtb2.User{EIDs: eid1, Ext: json.RawMessage(`malformed`)}},
 			expectedErr:  "invalid character 'm' looking for beginning of value",
 		},
 	}
 
 	for _, test := range testCases {
-		w := &RequestWrapper{BidRequest: &test.givenRequest}
-		err := moveEIDFrom26To25(w)
-
-		if len(test.expectedErr) > 0 {
-			assert.EqualError(t, err, test.expectedErr, test.description)
-		} else {
-			assert.NoError(t, w.RebuildRequest(), test.description)
-			assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
-		}
+		t.Run(test.name, func(t *testing.T) {
+			w := &RequestWrapper{BidRequest: &test.givenRequest}
+			err := moveEIDFrom26To25(w)
+
+			if len(test.expectedErr) > 0 {
+				assert.EqualError(t, err, test.expectedErr, "error")
+			} else {
+				assert.NoError(t, w.RebuildRequest(), "error")
+				assert.Equal(t, test.expectedRequest, *w.BidRequest, "result")
+			}
+		})
 	}
 }
 
 func TestMoveRewardedFrom26ToPrebidExt(t *testing.T) {
 	testCases := []struct {
-		description string
+		name        string
 		givenImp    openrtb2.Imp
 		expectedImp openrtb2.Imp
 		expectedErr string
 	}{
 		{
-			description: "Not Present",
+			name:        "notpresent-prebid",
 			givenImp:    openrtb2.Imp{},
 			expectedImp: openrtb2.Imp{},
 		},
 		{
-			description: "2.6 Migrated To Prebid Ext",
+			name:        "2.6-migratedto-2.5",
 			givenImp:    openrtb2.Imp{Rwdd: 1},
 			expectedImp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`)},
 		},
 		{
-			description: "Prebid Ext Overwritten",
+			name:        "2.5-overwritten",
 			givenImp:    openrtb2.Imp{Rwdd: 1, Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":2}}`)},
 			expectedImp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`)},
 		},
 		{
-			description: "Malformed",
+			name:        "Malformed",
 			givenImp:    openrtb2.Imp{Rwdd: 1, Ext: json.RawMessage(`malformed`)},
 			expectedErr: "invalid character 'm' looking for beginning of value",
 		},
 	}
 
 	for _, test := range testCases {
-		w := &ImpWrapper{Imp: &test.givenImp}
-		err := moveRewardedFrom26ToPrebidExt(w)
-
-		if len(test.expectedErr) > 0 {
-			assert.EqualError(t, err, test.expectedErr, test.description)
-		} else {
-			assert.NoError(t, w.RebuildImp(), test.description)
-			assert.Equal(t, test.expectedImp, *w.Imp, test.description)
-		}
+		t.Run(test.name, func(t *testing.T) {
+			w := &ImpWrapper{Imp: &test.givenImp}
+			err := moveRewardedFrom26ToPrebidExt(w)
+
+			if len(test.expectedErr) > 0 {
+				assert.EqualError(t, err, test.expectedErr, "error")
+			} else {
+				assert.NoError(t, w.RebuildImp(), "error")
+				assert.Equal(t, test.expectedImp, *w.Imp, "result")
+			}
+		})
+	}
+}
+
+func TestClear26Fields(t *testing.T) {
+	var int8_1 int8 = 1
+
+	given := &openrtb2.BidRequest{
+		ID:     "anyID",
+		WLangB: []string{"anyLang"},
+		CatTax: adcom1.CatTaxIABAudience11,
+		App: &openrtb2.App{
+			CatTax:  adcom1.CatTaxIABAudience11,
+			KwArray: []string{"anyKeyword"},
+			Content: &openrtb2.Content{
+				ID:      "anyContent",
+				CatTax:  adcom1.CatTaxIABAudience11,
+				KwArray: []string{"anyKeyword"},
+				LangB:   "anyLang",
+				Network: &openrtb2.Network{
+					ID: "anyNetwork",
+				},
+				Channel: &openrtb2.Channel{
+					ID: "anyChannel",
+				},
+				Producer: &openrtb2.Producer{
+					ID:     "anyProcedure",
+					CatTax: adcom1.CatTaxIABAudience11,
+				},
+			},
+			Publisher: &openrtb2.Publisher{
+				ID:     "anyPublisher",
+				CatTax: adcom1.CatTaxIABAudience11,
+			},
+		},
+		Site: &openrtb2.Site{
+			CatTax:  adcom1.CatTaxIABAudience11,
+			KwArray: []string{"anyKeyword"},
+			Content: &openrtb2.Content{
+				ID:      "anyContent",
+				CatTax:  adcom1.CatTaxIABAudience11,
+				KwArray: []string{"anyKeyword"},
+				LangB:   "anyLang",
+				Network: &openrtb2.Network{
+					ID: "anyNetwork",
+				},
+				Channel: &openrtb2.Channel{
+					ID: "anyChannel",
+				},
+				Producer: &openrtb2.Producer{
+					ID:     "anyProcedure",
+					CatTax: adcom1.CatTaxIABAudience11,
+				},
+			},
+			Publisher: &openrtb2.Publisher{
+				ID:     "anyPublisher",
+				CatTax: adcom1.CatTaxIABAudience11,
+			},
+		},
+		Device: &openrtb2.Device{
+			IP:    "1.2.3.4",
+			LangB: "anyLang",
+			SUA: &openrtb2.UserAgent{
+				Model: "PBS 2000",
+			},
+		},
+		Regs: &openrtb2.Regs{
+			COPPA:     1,
+			GDPR:      &int8_1,
+			USPrivacy: "anyCCPA",
+		},
+		Source: &openrtb2.Source{
+			TID: "anyTransactionID",
+			SChain: &openrtb2.SupplyChain{
+				Complete: 1,
+			},
+		},
+		User: &openrtb2.User{
+			ID:      "anyUser",
+			KwArray: []string{"anyKeyword"},
+			Consent: "anyConsent",
+			EIDs:    []openrtb2.EID{{Source: "anySource"}},
+		},
+		Imp: []openrtb2.Imp{{
+			ID:   "imp1",
+			Rwdd: 1,
+			SSAI: openrtb2.AdInsertServer,
+			Audio: &openrtb2.Audio{
+				MIMEs:        []string{"any/audio"},
+				PodDur:       30,
+				RqdDurs:      []int64{15, 60},
+				PodID:        1,
+				PodSeq:       adcom1.PodSeqFirst,
+				SlotInPod:    adcom1.SlotPosFirst,
+				MinCPMPerSec: 100.0,
+			},
+			Video: &openrtb2.Video{
+				MIMEs:        []string{"any/video"},
+				MaxSeq:       30,
+				PodDur:       30,
+				PodID:        1,
+				PodSeq:       adcom1.PodSeqFirst,
+				RqdDurs:      []int64{15, 60},
+				SlotInPod:    adcom1.SlotPosFirst,
+				MinCPMPerSec: 100.0,
+			},
+		}},
+	}
+
+	expected := &openrtb2.BidRequest{
+		ID: "anyID",
+		App: &openrtb2.App{
+			Content: &openrtb2.Content{
+				ID: "anyContent",
+				Producer: &openrtb2.Producer{
+					ID: "anyProcedure",
+				},
+			},
+			Publisher: &openrtb2.Publisher{
+				ID: "anyPublisher",
+			},
+		},
+		Site: &openrtb2.Site{
+			Content: &openrtb2.Content{
+				ID: "anyContent",
+				Producer: &openrtb2.Producer{
+					ID: "anyProcedure",
+				},
+			},
+			Publisher: &openrtb2.Publisher{
+				ID: "anyPublisher",
+			},
+		},
+		Device: &openrtb2.Device{
+			IP: "1.2.3.4",
+		},
+		Regs: &openrtb2.Regs{
+			COPPA: 1,
+		},
+		Source: &openrtb2.Source{
+			TID: "anyTransactionID",
+		},
+		User: &openrtb2.User{
+			ID: "anyUser",
+		},
+		Imp: []openrtb2.Imp{{
+			ID: "imp1",
+			Audio: &openrtb2.Audio{
+				MIMEs: []string{"any/audio"},
+			},
+			Video: &openrtb2.Video{
+				MIMEs: []string{"any/video"},
+			},
+		}},
+	}
+
+	r := &RequestWrapper{BidRequest: given}
+	clear26Fields(r)
+	assert.Equal(t, expected, r.BidRequest)
+
+}
+
+func TestClear202211Fields(t *testing.T) {
+	testCases := []struct {
+		name     string
+		given    openrtb2.BidRequest
+		expected openrtb2.BidRequest
+	}{
+		{
+			name: "app",
+			given: openrtb2.BidRequest{
+				ID:   "anyID",
+				App:  &openrtb2.App{InventoryPartnerDomain: "anyDomain"},
+				Imp:  []openrtb2.Imp{{ID: "imp1", Qty: &openrtb2.Qty{Multiplier: 2.0}, DT: 42}},
+				Regs: &openrtb2.Regs{GPP: "anyGPP", GPPSID: []int8{1, 2, 3}},
+			},
+			expected: openrtb2.BidRequest{
+				ID:   "anyID",
+				App:  &openrtb2.App{},
+				Imp:  []openrtb2.Imp{{ID: "imp1"}},
+				Regs: &openrtb2.Regs{},
+			},
+		},
+		{
+			name: "site",
+			given: openrtb2.BidRequest{
+				ID:   "anyID",
+				Site: &openrtb2.Site{InventoryPartnerDomain: "anyDomain"},
+				Imp:  []openrtb2.Imp{{ID: "imp1", Qty: &openrtb2.Qty{Multiplier: 2.0}, DT: 42}},
+				Regs: &openrtb2.Regs{GPP: "anyGPP", GPPSID: []int8{1, 2, 3}},
+			},
+			expected: openrtb2.BidRequest{
+				ID:   "anyID",
+				Site: &openrtb2.Site{},
+				Imp:  []openrtb2.Imp{{ID: "imp1"}},
+				Regs: &openrtb2.Regs{},
+			},
+		},
+		{
+			name: "dooh",
+			given: openrtb2.BidRequest{
+				ID:   "anyID",
+				DOOH: &openrtb2.DOOH{ID: "anyDOOH"},
+				Imp:  []openrtb2.Imp{{ID: "imp1", Qty: &openrtb2.Qty{Multiplier: 2.0}, DT: 42}},
+				Regs: &openrtb2.Regs{GPP: "anyGPP", GPPSID: []int8{1, 2, 3}},
+			},
+			expected: openrtb2.BidRequest{
+				ID:   "anyID",
+				Imp:  []openrtb2.Imp{{ID: "imp1"}},
+				Regs: &openrtb2.Regs{},
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			r := &RequestWrapper{BidRequest: &test.given}
+			clear202211Fields(r)
+			assert.Equal(t, &test.expected, r.BidRequest)
+		})
 	}
 }
diff --git a/openrtb_ext/request_test.go b/openrtb_ext/request_test.go
index 979c2edb..8d1ecee2 100644
--- a/openrtb_ext/request_test.go
+++ b/openrtb_ext/request_test.go
@@ -2,87 +2,62 @@ package openrtb_ext
 
 import (
 	"encoding/json"
-	"reflect"
+	"fmt"
 	"testing"
 
+	"github.com/prebid/prebid-server/util/ptrutil"
 	"github.com/stretchr/testify/assert"
 )
 
-// Test the unmarshalling of the prebid extensions and setting default Price Granularity
-func TestExtRequestTargeting(t *testing.T) {
-	extRequest := &ExtRequest{}
-	err := json.Unmarshal([]byte(ext1), extRequest)
-	if err != nil {
-		t.Errorf("ext1 Unmarshall failure: %s", err.Error())
-	}
-	if extRequest.Prebid.Targeting != nil {
-		t.Error("ext1 Targeting is not nil")
-	}
-
-	extRequest = &ExtRequest{}
-	err = json.Unmarshal([]byte(ext2), extRequest)
-	if err != nil {
-		t.Errorf("ext2 Unmarshall failure: %s", err.Error())
-	}
-	if extRequest.Prebid.Targeting == nil {
-		t.Error("ext2 Targeting is nil")
-	} else {
-		pgDense := PriceGranularityFromString("dense")
-		if !reflect.DeepEqual(extRequest.Prebid.Targeting.PriceGranularity, pgDense) {
-			t.Errorf("ext2 expected Price granularity \"dense\" (%v), found \"%v\"", pgDense, extRequest.Prebid.Targeting.PriceGranularity)
-		}
-	}
-
-	extRequest = &ExtRequest{}
-	err = json.Unmarshal([]byte(ext3), extRequest)
-	if err != nil {
-		t.Errorf("ext3 Unmarshall failure: %s", err.Error())
-	}
-	if extRequest.Prebid.Targeting == nil {
-		t.Error("ext3 Targeting is nil")
-	} else {
-		pgMed := PriceGranularityFromString("medium")
-		if !reflect.DeepEqual(extRequest.Prebid.Targeting.PriceGranularity, pgMed) {
-			t.Errorf("ext3 expected Price granularity \"medium\", found \"%v\"", extRequest.Prebid.Targeting.PriceGranularity)
-		}
-	}
-}
-
-const ext1 = `{
-	"prebid": {
-		"non_target": "some junk"
-	}
-}
-`
-
-const ext2 = `{
-	"prebid": {
-		"targeting": {
-			"pricegranularity": "dense"
-		}
-	}
-}`
-
-const ext3 = `{
-	"prebid": {
-		"targeting": { }
-	}
-}`
-
 type granularityTestData struct {
 	json   []byte
 	target PriceGranularity
 }
 
 func TestGranularityUnmarshal(t *testing.T) {
-	for _, test := range validGranularityTests {
-		var resolved PriceGranularity
-		err := json.Unmarshal(test.json, &resolved)
-		if err != nil {
-			t.Errorf("Failed to Unmarshall granularity: %s", err.Error())
-		}
-		if !reflect.DeepEqual(test.target, resolved) {
-			t.Errorf("Granularity unmarshal failed, the unmarshalled JSON did not match the target\nExpected: %v\nActual  : %v", test.target, resolved)
+	testGroups := []struct {
+		desc        string
+		in          []granularityTestData
+		expectError bool
+	}{
+		{
+			desc:        "Unmarshal without error",
+			in:          validGranularityTests,
+			expectError: false,
+		},
+		{
+			desc: "Malformed json. Expect unmarshall error",
+			in: []granularityTestData{
+				{json: []byte(`[]`)},
+			},
+			expectError: true,
+		},
+	}
+	for _, tg := range testGroups {
+		for i, tc := range tg.in {
+			var resolved PriceGranularity
+			err := json.Unmarshal(tc.json, &resolved)
+
+			// Assert validation error
+			if tg.expectError && !assert.Errorf(t, err, "%s test case %d", tg.desc, i) {
+				continue
+			}
+
+			// Assert Targeting.Precision
+			assert.Equal(t, tc.target.Precision, resolved.Precision, "%s test case %d", tg.desc, i)
+
+			// Assert Targeting.Ranges
+			if assert.Len(t, resolved.Ranges, len(tc.target.Ranges), "%s test case %d", tg.desc, i) {
+				expected := make(map[string]struct{}, len(tc.target.Ranges))
+				for _, r := range tc.target.Ranges {
+					expected[fmt.Sprintf("%2.2f-%2.2f-%2.2f", r.Min, r.Max, r.Increment)] = struct{}{}
+				}
+				for _, actualRange := range resolved.Ranges {
+					targetRange := fmt.Sprintf("%2.2f-%2.2f-%2.2f", actualRange.Min, actualRange.Max, actualRange.Increment)
+					_, exists := expected[targetRange]
+					assert.True(t, exists, "%s test case %d target.range %s not found", tg.desc, i, targetRange)
+				}
+			}
 		}
 	}
 }
@@ -91,7 +66,7 @@ var validGranularityTests []granularityTestData = []granularityTestData{
 	{
 		json: []byte(`{"precision": 4, "ranges": [{"min": 0, "max": 5, "increment": 0.1}, {"min": 5, "max":10, "increment":0.5}, {"min":10, "max":20, "increment":1}]}`),
 		target: PriceGranularity{
-			Precision: 4,
+			Precision: ptrutil.ToPtr(4),
 			Ranges: []GranularityRange{
 				{Min: 0.0, Max: 5.0, Increment: 0.1},
 				{Min: 5.0, Max: 10.0, Increment: 0.5},
@@ -102,99 +77,67 @@ var validGranularityTests []granularityTestData = []granularityTestData{
 	{
 		json: []byte(`{"ranges":[{ "max":5, "increment": 0.05}, {"max": 10, "increment": 0.25}, {"max": 20, "increment": 0.5}]}`),
 		target: PriceGranularity{
-			Precision: 2,
 			Ranges: []GranularityRange{
 				{Min: 0.0, Max: 5.0, Increment: 0.05},
-				{Min: 5.0, Max: 10.0, Increment: 0.25},
-				{Min: 10.0, Max: 20.0, Increment: 0.5},
+				{Min: 0.0, Max: 10.0, Increment: 0.25},
+				{Min: 0.0, Max: 20.0, Increment: 0.5},
 			},
 		},
 	},
 	{
-		json:   []byte(`"medium"`),
-		target: priceGranularityMed,
+		json: []byte(`"medium"`),
+		target: PriceGranularity{
+			Precision: ptrutil.ToPtr(2),
+			Ranges: []GranularityRange{{
+				Min:       0,
+				Max:       20,
+				Increment: 0.1}},
+		},
 	},
 	{
 		json: []byte(`{ "precision": 3, "ranges": [{"max":20, "increment":0.005}]}`),
 		target: PriceGranularity{
-			Precision: 3,
+			Precision: ptrutil.ToPtr(3),
 			Ranges:    []GranularityRange{{Min: 0.0, Max: 20.0, Increment: 0.005}},
 		},
 	},
 	{
 		json: []byte(`{"precision": 0, "ranges": [{"max":5, "increment": 1}, {"max": 10, "increment": 2}, {"max": 20, "increment": 5}]}`),
 		target: PriceGranularity{
-			Precision: 0,
+			Precision: ptrutil.ToPtr(0),
 			Ranges: []GranularityRange{
 				{Min: 0.0, Max: 5.0, Increment: 1.0},
-				{Min: 5.0, Max: 10.0, Increment: 2.0},
-				{Min: 10.0, Max: 20.0, Increment: 5.0},
+				{Min: 0.0, Max: 10.0, Increment: 2.0},
+				{Min: 0.0, Max: 20.0, Increment: 5.0},
 			},
 		},
 	},
 	{
 		json: []byte(`{"precision": 2, "ranges": [{"min": 0.5, "max":5, "increment": 0.1}, {"min": 54, "max": 10, "increment": 1}, {"min": -42, "max": 20, "increment": 5}]}`),
 		target: PriceGranularity{
-			Precision: 2,
+			Precision: ptrutil.ToPtr(2),
 			Ranges: []GranularityRange{
-				{Min: 0.0, Max: 5.0, Increment: 0.1},
-				{Min: 5.0, Max: 10.0, Increment: 1.0},
-				{Min: 10.0, Max: 20.0, Increment: 5.0},
+				{Min: 0.5, Max: 5.0, Increment: 0.1},
+				{Min: 54.0, Max: 10.0, Increment: 1.0},
+				{Min: -42.0, Max: 20.0, Increment: 5.0},
 			},
 		},
 	},
 	{
 		json:   []byte(`{}`),
-		target: priceGranularityMed,
+		target: PriceGranularity{},
 	},
 	{
-		json:   []byte(`{"precision": 2}`),
-		target: priceGranularityMed,
+		json: []byte(`{"precision": 2}`),
+		target: PriceGranularity{
+			Precision: ptrutil.ToPtr(2),
+		},
 	},
 	{
-		json:   []byte(`{"precision": 2, "ranges":[]}`),
-		target: priceGranularityMed,
-	},
-}
-
-func TestGranularityUnmarshalBad(t *testing.T) {
-	testCases := []struct {
-		description          string
-		jsonPriceGranularity []byte
-	}{
-		{
-			"Malformed",
-			[]byte(`[]`),
-		},
-		{
-			"Negative precision",
-			[]byte(`{"precision": -1, "ranges": [{"max":20, "increment":0.5}]}`),
-		},
-		{
-			"Precision greater than MaxDecimalFigures supported",
-			[]byte(`{"precision": 16, "ranges": [{"max":20, "increment":0.5}]}`),
-		},
-		{
-			"Negative increment",
-			[]byte(`{"ranges":[{"max":20, "increment": -1}]}`),
-		},
-		{
-			"Range with non float64 max value",
-			[]byte(`{"ranges":[{"max":"20", "increment": "0.1"}]}`),
-		},
-		{
-			"Ranges in decreasing order",
-			[]byte(`{"ranges":[{"max":20, "increment":0.1}. {"max":10, "increment":0.02}]}`),
-		},
-		{
-			"Max equal to previous max",
-			[]byte(`{"ranges":[{"max":1.0, "increment": 0.07}, {"max" 1.0, "increment": 0.03}]}`),
+		json: []byte(`{"precision": 2, "ranges":[]}`),
+		target: PriceGranularity{
+			Precision: ptrutil.ToPtr(2),
+			Ranges:    []GranularityRange{},
 		},
-	}
-
-	for _, test := range testCases {
-		resolved := PriceGranularity{}
-		err := json.Unmarshal(test.jsonPriceGranularity, &resolved)
-		assert.Errorf(t, err, "Invalid granularity unmarshalled without error.\nJSON was: %s\n Resolved to: %v. Test: %s", string(test.jsonPriceGranularity), resolved, test.description)
-	}
+	},
 }
diff --git a/ortb/default_test.go b/ortb/default_test.go
new file mode 100644
index 00000000..ad353ccb
--- /dev/null
+++ b/ortb/default_test.go
@@ -0,0 +1,331 @@
+package ortb
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/openrtb/v17/openrtb2"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/util/ptrutil"
+)
+
+func TestSetDefaults(t *testing.T) {
+	testCases := []struct {
+		name            string
+		givenRequest    openrtb2.BidRequest
+		expectedRequest openrtb2.BidRequest
+		expectedErr     string
+	}{
+		{
+			name:            "empty",
+			givenRequest:    openrtb2.BidRequest{},
+			expectedRequest: openrtb2.BidRequest{},
+		},
+		{
+			name:            "malformed request.ext",
+			givenRequest:    openrtb2.BidRequest{Ext: json.RawMessage(`malformed`)},
+			expectedRequest: openrtb2.BidRequest{Ext: json.RawMessage(`malformed`)},
+			expectedErr:     "invalid character 'm' looking for beginning of value",
+		},
+		{
+			name:            "targeting", // tests integration with setDefaultsTargeting
+			givenRequest:    openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"targeting":{}}}`)},
+			expectedRequest: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true}}}`)},
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			wrapper := &openrtb_ext.RequestWrapper{BidRequest: &test.givenRequest}
+
+			// run
+			err := SetDefaults(wrapper)
+
+			// assert error
+			if len(test.expectedErr) > 0 {
+				assert.EqualError(t, err, test.expectedErr, "Error")
+			}
+
+			// rebuild request
+			require.NoError(t, wrapper.RebuildRequest(), "Rebuild Request")
+
+			// assert
+			if len(test.expectedErr) > 0 {
+				assert.EqualError(t, err, test.expectedErr, "Error")
+				assert.Equal(t, &test.expectedRequest, wrapper.BidRequest, "Request")
+			} else {
+				// assert request as json to ignore order in ext fields
+				expectedRequestJSON, err := json.Marshal(test.expectedRequest)
+				require.NoError(t, err, "Marshal Expected Request")
+
+				actualRequestJSON, err := json.Marshal(wrapper.BidRequest)
+				require.NoError(t, err, "Marshal Actual Request")
+
+				assert.JSONEq(t, string(expectedRequestJSON), string(actualRequestJSON), "Request")
+			}
+		})
+	}
+}
+
+func TestSetDefaultsTargeting(t *testing.T) {
+	defaultGranularity := openrtb_ext.PriceGranularity{
+		Precision: ptrutil.ToPtr(DefaultPriceGranularityPrecision),
+		Ranges:    []openrtb_ext.GranularityRange{{Min: 0, Max: 20, Increment: 0.1}},
+	}
+
+	testCases := []struct {
+		name              string
+		givenTargeting    *openrtb_ext.ExtRequestTargeting
+		expectedTargeting *openrtb_ext.ExtRequestTargeting
+		expectedModified  bool
+	}{
+		{
+			name:              "nil",
+			givenTargeting:    nil,
+			expectedTargeting: nil,
+		},
+		{
+			name:           "empty-targeting",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{},
+			expectedTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity:  &defaultGranularity,
+				IncludeWinners:    ptrutil.ToPtr(DefaultTargetingIncludeWinners),
+				IncludeBidderKeys: ptrutil.ToPtr(DefaultTargetingIncludeBidderKeys),
+			},
+			expectedModified: true,
+		},
+		{
+			name: "populated-partial", // precision and includewinners defaults set
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Ranges: []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+				},
+				IncludeBidderKeys: ptrutil.ToPtr(false),
+			},
+			expectedTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Precision: ptrutil.ToPtr(DefaultPriceGranularityPrecision),
+					Ranges:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+				},
+				IncludeWinners:    ptrutil.ToPtr(DefaultTargetingIncludeWinners),
+				IncludeBidderKeys: ptrutil.ToPtr(false),
+			},
+			expectedModified: true,
+		},
+		{
+			name: "populated-no-granularity",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity:  &openrtb_ext.PriceGranularity{},
+				IncludeWinners:    ptrutil.ToPtr(false),
+				IncludeBidderKeys: ptrutil.ToPtr(false),
+			},
+			expectedTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity:  &defaultGranularity,
+				IncludeWinners:    ptrutil.ToPtr(false),
+				IncludeBidderKeys: ptrutil.ToPtr(false),
+			},
+			expectedModified: true,
+		},
+		{
+			name: "populated-ranges-nil",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Precision: ptrutil.ToPtr(4),
+					Ranges:    nil,
+				},
+			},
+			expectedTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity:  &defaultGranularity,
+				IncludeWinners:    ptrutil.ToPtr(DefaultTargetingIncludeWinners),
+				IncludeBidderKeys: ptrutil.ToPtr(DefaultTargetingIncludeBidderKeys),
+			},
+			expectedModified: true,
+		},
+		{
+			name: "populated-ranges-empty",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Precision: ptrutil.ToPtr(4),
+					Ranges:    []openrtb_ext.GranularityRange{},
+				},
+			},
+			expectedTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity:  &defaultGranularity,
+				IncludeWinners:    ptrutil.ToPtr(DefaultTargetingIncludeWinners),
+				IncludeBidderKeys: ptrutil.ToPtr(DefaultTargetingIncludeBidderKeys),
+			},
+			expectedModified: true,
+		},
+		{
+			name: "populated-full", // no defaults set
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Precision: ptrutil.ToPtr(4),
+					Ranges:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+				},
+				IncludeWinners:    ptrutil.ToPtr(false),
+				IncludeBidderKeys: ptrutil.ToPtr(false),
+			},
+			expectedTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Precision: ptrutil.ToPtr(4),
+					Ranges:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+				},
+				IncludeWinners:    ptrutil.ToPtr(false),
+				IncludeBidderKeys: ptrutil.ToPtr(false),
+			},
+			expectedModified: false,
+		},
+		{
+			name: "setDefaultsPriceGranularity-integration",
+			givenTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Precision: ptrutil.ToPtr(4),
+					Ranges:    []openrtb_ext.GranularityRange{{Min: 5, Max: 10, Increment: 1}},
+				},
+				IncludeWinners:    ptrutil.ToPtr(false),
+				IncludeBidderKeys: ptrutil.ToPtr(false),
+			},
+			expectedTargeting: &openrtb_ext.ExtRequestTargeting{
+				PriceGranularity: &openrtb_ext.PriceGranularity{
+					Precision: ptrutil.ToPtr(4),
+					Ranges:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+				},
+				IncludeWinners:    ptrutil.ToPtr(false),
+				IncludeBidderKeys: ptrutil.ToPtr(false),
+			},
+			expectedModified: true,
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			actualModified := setDefaultsTargeting(test.givenTargeting)
+			assert.Equal(t, test.expectedModified, actualModified)
+			assert.Equal(t, test.expectedTargeting, test.givenTargeting)
+		})
+	}
+}
+
+func TestSetDefaultsPriceGranularity(t *testing.T) {
+	testCases := []struct {
+		name                string
+		givenGranularity    *openrtb_ext.PriceGranularity
+		expectedGranularity *openrtb_ext.PriceGranularity
+		expectedModified    bool
+	}{
+		{
+			name: "no-precision",
+			givenGranularity: &openrtb_ext.PriceGranularity{
+				Precision: nil,
+				Ranges:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+			},
+			expectedGranularity: &openrtb_ext.PriceGranularity{
+				Precision: ptrutil.ToPtr(2),
+				Ranges:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+			},
+			expectedModified: true,
+		},
+		{
+			name: "incomplete-range",
+			givenGranularity: &openrtb_ext.PriceGranularity{
+				Precision: ptrutil.ToPtr(2),
+				Ranges:    []openrtb_ext.GranularityRange{{Min: 5, Max: 10, Increment: 1}},
+			},
+			expectedGranularity: &openrtb_ext.PriceGranularity{
+				Precision: ptrutil.ToPtr(2),
+				Ranges:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+			},
+			expectedModified: true,
+		},
+		{
+			name: "no-precision+incomplete-range",
+			givenGranularity: &openrtb_ext.PriceGranularity{
+				Precision: nil,
+				Ranges:    []openrtb_ext.GranularityRange{{Min: 5, Max: 10, Increment: 1}},
+			},
+			expectedGranularity: &openrtb_ext.PriceGranularity{
+				Precision: ptrutil.ToPtr(2),
+				Ranges:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+			},
+			expectedModified: true,
+		},
+		{
+			name: "all-set",
+			givenGranularity: &openrtb_ext.PriceGranularity{
+				Precision: ptrutil.ToPtr(2),
+				Ranges:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+			},
+			expectedGranularity: &openrtb_ext.PriceGranularity{
+				Precision: ptrutil.ToPtr(2),
+				Ranges:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+			},
+			expectedModified: false,
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			actualModified := setDefaultsPriceGranularity(test.givenGranularity)
+			assert.Equal(t, test.expectedModified, actualModified)
+			assert.Equal(t, test.expectedGranularity, test.givenGranularity)
+		})
+	}
+}
+
+func TestSetDefaultsPriceGranularityRange(t *testing.T) {
+	testCases := []struct {
+		name             string
+		givenRange       []openrtb_ext.GranularityRange
+		expectedRange    []openrtb_ext.GranularityRange
+		expectedModified bool
+	}{
+		{
+			name:             "nil",
+			givenRange:       nil,
+			expectedRange:    nil,
+			expectedModified: false,
+		},
+		{
+			name:             "empty",
+			givenRange:       []openrtb_ext.GranularityRange{},
+			expectedRange:    []openrtb_ext.GranularityRange{},
+			expectedModified: false,
+		},
+		{
+			name:             "one-ok",
+			givenRange:       []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+			expectedRange:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+			expectedModified: false,
+		},
+		{
+			name:             "one-fixed",
+			givenRange:       []openrtb_ext.GranularityRange{{Min: 5, Max: 10, Increment: 1}},
+			expectedRange:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}},
+			expectedModified: true,
+		},
+		{
+			name:             "many-ok",
+			givenRange:       []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}, {Min: 10, Max: 20, Increment: 1}},
+			expectedRange:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}, {Min: 10, Max: 20, Increment: 1}},
+			expectedModified: false,
+		},
+		{
+			name:             "many-fixed",
+			givenRange:       []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}, {Min: 15, Max: 20, Increment: 1}},
+			expectedRange:    []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}, {Min: 10, Max: 20, Increment: 1}},
+			expectedModified: true,
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			actualModified := setDefaultsPriceGranularityRange(test.givenRange)
+			assert.Equal(t, test.expectedModified, actualModified)
+			assert.Equal(t, test.expectedRange, test.givenRange)
+		})
+	}
+}