Repository: prebid/prebid-server
Pull request #1591 (2020-11-18 05:43:48)

Fix GDPR consent assumption when gdpr req signal is unambiguous and s…
…et to 1 and consent string is blank

To address this issue, I pulled `UserSyncIfAmbiguous` out of the `extractGDPR` method so that method's sole responsibility now is to extract the GDPR field from the request. The logic that determines whether GDPR should be enforced by considering `UserSyncIfAmbiguous` has been moved into `CleanOpenRTBRequests`. If GDPR should be enforced and it’s enabled at either the host or account level, we check if personal info is allowed by considering the GDPR flag in the request along with the consent string.

Most of the test changes are conversions to a table-based format with a couple of new test cases.

stale[bot]: This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

SyntaxNode: Removing stale label to end of year holidays.

Patch:
diff --git a/adapters/ix/ix.go b/adapters/ix/ix.go
index 7052d5ac..96cd988d 100644
--- a/adapters/ix/ix.go
+++ b/adapters/ix/ix.go
@@ -8,26 +8,22 @@ import (
 	"io/ioutil"
 	"net/http"
 
-	"github.com/prebid/prebid-server/openrtb_ext"
-
-	"github.com/prebid/prebid-server/pbs"
-
 	"golang.org/x/net/context/ctxhttp"
 
 	"github.com/mxmCherry/openrtb"
 	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/pbs"
 )
 
-// maximum number of bid requests
-const requestLimit = 20
-
 type IxAdapter struct {
-	http *adapters.HTTPAdapter
-	URI  string
+	http        *adapters.HTTPAdapter
+	URI         string
+	maxRequests int
 }
 
-// Name is used for cookies and such
 func (a *IxAdapter) Name() string {
 	return string(openrtb_ext.BidderIx)
 }
@@ -52,34 +48,21 @@ type callOneObject struct {
 	requestJSON bytes.Buffer
 	width       uint64
 	height      uint64
-}
-
-func isValidIXSize(f openrtb.Format, s [2]uint64) bool {
-	if f.W != s[0] || f.H != s[1] {
-		return false
-	}
-	return true
+	bidType     string
 }
 
 func (a *IxAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder *pbs.PBSBidder) (pbs.PBSBidSlice, error) {
 	var prioritizedRequests, requests []callOneObject
 
-	if req.App != nil {
-		return nil, &errortypes.BadInput{
-			Message: "Index doesn't support apps",
-		}
-	}
-	mediaTypes := []pbs.MediaType{pbs.MEDIA_TYPE_BANNER}
+	mediaTypes := []pbs.MediaType{pbs.MEDIA_TYPE_BANNER, pbs.MEDIA_TYPE_VIDEO}
 	indexReq, err := adapters.MakeOpenRTBGeneric(req, bidder, a.Name(), mediaTypes)
-
 	if err != nil {
 		return nil, err
 	}
 
 	indexReqImp := indexReq.Imp
 	for i, unit := range bidder.AdUnits {
-
-		// Fixes some segfaults. Since this is legacy code, I'm not looking into it too deeply
+		// Supposedly fixes some segfaults
 		if len(indexReqImp) <= i {
 			break
 		}
@@ -99,29 +82,35 @@ func (a *IxAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder *pbs.P
 		}
 
 		for sizeIndex, format := range unit.Sizes {
-			// Only grab this ad unit
-			// Not supporting multi-media-type adunit yet
+			// Only grab this ad unit. Not supporting multi-media-type adunit yet.
 			thisImp := indexReqImp[i]
 
 			thisImp.TagID = unit.Code
-			thisImp.Banner.Format = []openrtb.Format{format}
-			thisImp.Banner.W = &format.W
-			thisImp.Banner.H = &format.H
+			if thisImp.Banner != nil {
+				thisImp.Banner.Format = []openrtb.Format{format}
+				thisImp.Banner.W = &format.W
+				thisImp.Banner.H = &format.H
+			}
 			indexReq.Imp = []openrtb.Imp{thisImp}
 			// Index spec says "adunit path representing ad server inventory" but we don't have this
 			// ext is DFP div ID and KV pairs if avail
 			//indexReq.Imp[i].Ext = json.RawMessage("{}")
 
-			// Any objects pointed to by indexReq *must not be mutated*, or we will get race conditions.
-			siteCopy := *indexReq.Site
-			siteCopy.Publisher = &openrtb.Publisher{ID: params.SiteID}
-			indexReq.Site = &siteCopy
+			if indexReq.Site != nil {
+				// Any objects pointed to by indexReq *must not be mutated*, or we will get race conditions.
+				siteCopy := *indexReq.Site
+				siteCopy.Publisher = &openrtb.Publisher{ID: params.SiteID}
+				indexReq.Site = &siteCopy
+			}
 
-			// spec also asks for publisher id if set
-			// ext object on request for prefetch
+			bidType := ""
+			if thisImp.Banner != nil {
+				bidType = string(openrtb_ext.BidTypeBanner)
+			} else if thisImp.Video != nil {
+				bidType = string(openrtb_ext.BidTypeVideo)
+			}
 			j, _ := json.Marshal(indexReq)
-
-			request := callOneObject{requestJSON: *bytes.NewBuffer(j), width: format.W, height: format.H}
+			request := callOneObject{requestJSON: *bytes.NewBuffer(j), width: format.W, height: format.H, bidType: bidType}
 
 			// prioritize slots over sizes
 			if sizeIndex == 0 {
@@ -132,10 +121,10 @@ func (a *IxAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder *pbs.P
 		}
 	}
 
-	// cap the number of requests to requestLimit
+	// cap the number of requests to maxRequests
 	requests = append(prioritizedRequests, requests...)
-	if len(requests) > requestLimit {
-		requests = requests[:requestLimit]
+	if len(requests) > a.maxRequests {
+		requests = requests[:a.maxRequests]
 	}
 
 	if len(requests) == 0 {
@@ -155,6 +144,7 @@ func (a *IxAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder *pbs.P
 				result.Bid.BidID = bidder.LookupBidID(result.Bid.AdUnitCode)
 				result.Bid.Width = request.width
 				result.Bid.Height = request.height
+				result.Bid.CreativeMediaType = request.bidType
 
 				if result.Bid.BidID == "" {
 					result.Error = &errortypes.BadServerResponse{
@@ -247,23 +237,195 @@ func (a *IxAdapter) callOne(ctx context.Context, reqJSON bytes.Buffer) (ixBidRes
 	}
 	bid := bidResp.SeatBid[0].Bid[0]
 
-	result.Bid = &pbs.PBSBid{
-		AdUnitCode:        bid.ImpID,
-		Price:             bid.Price,
-		Adm:               bid.AdM,
-		Creative_id:       bid.CrID,
-		Width:             bid.W,
-		Height:            bid.H,
-		DealId:            bid.DealID,
-		CreativeMediaType: string(openrtb_ext.BidTypeBanner),
+	pbid := pbs.PBSBid{
+		AdUnitCode:  bid.ImpID,
+		Price:       bid.Price,
+		Adm:         bid.AdM,
+		Creative_id: bid.CrID,
+		Width:       bid.W,
+		Height:      bid.H,
+		DealId:      bid.DealID,
 	}
+
+	result.Bid = &pbid
 	return result, nil
 }
 
-func NewIxLegacyAdapter(config *adapters.HTTPAdapterConfig, uri string) *IxAdapter {
-	a := adapters.NewHTTPAdapter(config)
+func (a *IxAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	nImp := len(request.Imp)
+	if nImp > a.maxRequests {
+		request.Imp = request.Imp[:a.maxRequests]
+		nImp = a.maxRequests
+	}
+
+	// Multi-size banner imps are split into single-size requests.
+	// The first size imp requests are added to the first slice.
+	// Additional size requests are added to the second slice and are merged with the first at the end.
+	// Preallocate the max possible size to avoid reallocating arrays.
+	requests := make([]*adapters.RequestData, 0, a.maxRequests)
+	multiSizeRequests := make([]*adapters.RequestData, 0, a.maxRequests-nImp)
+	errs := make([]error, 0, 1)
+
+	headers := http.Header{
+		"Content-Type": {"application/json;charset=utf-8"},
+		"Accept":       {"application/json"}}
+
+	imps := request.Imp
+	for iImp := range imps {
+		request.Imp = imps[iImp : iImp+1]
+		if request.Site != nil {
+			if err := setSitePublisherId(request, iImp); err != nil {
+				errs = append(errs, err)
+				continue
+			}
+		}
+
+		if request.Imp[0].Banner != nil {
+			banner := *request.Imp[0].Banner
+			request.Imp[0].Banner = &banner
+			formats := getBannerFormats(&banner)
+			for iFmt := range formats {
+				banner.Format = formats[iFmt : iFmt+1]
+				banner.W = openrtb.Uint64Ptr(banner.Format[0].W)
+				banner.H = openrtb.Uint64Ptr(banner.Format[0].H)
+				if requestData, err := createRequestData(a, request, &headers); err == nil {
+					if iFmt == 0 {
+						requests = append(requests, requestData)
+					} else {
+						multiSizeRequests = append(multiSizeRequests, requestData)
+					}
+				} else {
+					errs = append(errs, err)
+				}
+				if len(multiSizeRequests) == cap(multiSizeRequests) {
+					break
+				}
+			}
+		} else if requestData, err := createRequestData(a, request, &headers); err == nil {
+			requests = append(requests, requestData)
+		} else {
+			errs = append(errs, err)
+		}
+	}
+	request.Imp = imps
+
+	return append(requests, multiSizeRequests...), errs
+}
+
+func setSitePublisherId(request *openrtb.BidRequest, iImp int) error {
+	if iImp == 0 {
+		// first impression - create a site and pub copy
+		site := *request.Site
+		if site.Publisher == nil {
+			site.Publisher = &openrtb.Publisher{}
+		} else {
+			publisher := *site.Publisher
+			site.Publisher = &publisher
+		}
+		request.Site = &site
+	}
+
+	var bidderExt adapters.ExtImpBidder
+	if err := json.Unmarshal(request.Imp[0].Ext, &bidderExt); err != nil {
+		return err
+	}
+
+	var ixExt openrtb_ext.ExtImpIx
+	if err := json.Unmarshal(bidderExt.Bidder, &ixExt); err != nil {
+		return err
+	}
+
+	request.Site.Publisher.ID = ixExt.SiteId
+	return nil
+}
+
+func getBannerFormats(banner *openrtb.Banner) []openrtb.Format {
+	if len(banner.Format) == 0 && banner.W != nil && banner.H != nil {
+		banner.Format = []openrtb.Format{{W: *banner.W, H: *banner.H}}
+	}
+	return banner.Format
+}
+
+func createRequestData(a *IxAdapter, request *openrtb.BidRequest, headers *http.Header) (*adapters.RequestData, error) {
+	body, err := json.Marshal(request)
+	return &adapters.RequestData{
+		Method:  "POST",
+		Uri:     a.URI,
+		Body:    body,
+		Headers: *headers,
+	}, err
+}
+
+func (a *IxAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	switch {
+	case response.StatusCode == http.StatusNoContent:
+		return nil, nil
+	case response.StatusCode == http.StatusBadRequest:
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
+		}}
+	case 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),
+		}}
+	}
+
+	var bidResponse openrtb.BidResponse
+	if err := json.Unmarshal(response.Body, &bidResponse); err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("JSON parsing error: %v", err),
+		}}
+	}
+
+	// Until the time we support multi-format ad units, we'll use a bid request impression media type
+	// as a bid response bid type. They are linked by the impression id.
+	impMediaType := map[string]openrtb_ext.BidType{}
+	for _, imp := range internalRequest.Imp {
+		if imp.Banner != nil {
+			impMediaType[imp.ID] = openrtb_ext.BidTypeBanner
+		} else if imp.Video != nil {
+			impMediaType[imp.ID] = openrtb_ext.BidTypeVideo
+		} else if imp.Native != nil {
+			impMediaType[imp.ID] = openrtb_ext.BidTypeNative
+		} else if imp.Audio != nil {
+			impMediaType[imp.ID] = openrtb_ext.BidTypeAudio
+		}
+	}
+
+	bidderResponse := adapters.NewBidderResponseWithBidsCapacity(5)
+	bidderResponse.Currency = bidResponse.Cur
+
+	var errs []error
+
+	for _, seatBid := range bidResponse.SeatBid {
+		for _, bid := range seatBid.Bid {
+			bidType, ok := impMediaType[bid.ImpID]
+			if !ok {
+				errs = append(errs, fmt.Errorf("Unmatched impression id: %s.", bid.ImpID))
+			}
+			bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
+				Bid:     &bid,
+				BidType: bidType,
+			})
+		}
+	}
+
+	return bidderResponse, errs
+}
+
+func NewIxLegacyAdapter(config *adapters.HTTPAdapterConfig, endpoint string) *IxAdapter {
 	return &IxAdapter{
-		http: a,
-		URI:  uri,
+		http:        adapters.NewHTTPAdapter(config),
+		URI:         endpoint,
+		maxRequests: 20,
+	}
+}
+
+// Builder builds a new instance of the Ix adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	bidder := &IxAdapter{
+		URI:         config.Endpoint,
+		maxRequests: 20,
 	}
+	return bidder, nil
 }
diff --git a/adapters/mobfoxpb/mobfoxpb.go b/adapters/mobfoxpb/mobfoxpb.go
new file mode 100644
index 00000000..5369c082
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpb.go
@@ -0,0 +1,131 @@
+package mobfoxpb
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/buger/jsonparser"
+	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	URI string
+}
+
+// Builder builds a new instance of the Mobfox adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	bidder := &adapter{
+		URI: config.Endpoint,
+	}
+	return bidder, nil
+}
+
+// MakeRequests create bid request for mobfoxpb demand
+func (a *adapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var errs []error
+	var err error
+	var tagID string
+
+	var adapterRequests []*adapters.RequestData
+
+	reqCopy := *request
+	imp := request.Imp[0]
+	tagID, err = jsonparser.GetString(imp.Ext, "bidder", "TagID")
+	if err != nil {
+		errs = append(errs, err)
+		return nil, errs
+	}
+	imp.TagID = tagID
+	reqCopy.Imp = []openrtb.Imp{imp}
+	adapterReq, err := a.makeRequest(&reqCopy)
+	if err != nil {
+		errs = append(errs, err)
+	}
+	if adapterReq != nil {
+		adapterRequests = append(adapterRequests, adapterReq)
+	}
+	return adapterRequests, errs
+}
+
+func (a *adapter) makeRequest(request *openrtb.BidRequest) (*adapters.RequestData, error) {
+	reqJSON, err := json.Marshal(request)
+
+	if err != nil {
+		return nil, err
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	return &adapters.RequestData{
+		Method:  "POST",
+		Uri:     a.URI,
+		Body:    reqJSON,
+		Headers: headers,
+	}, nil
+}
+
+// MakeBids makes the bids
+func (a *adapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	var errs []error
+
+	if response.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),
+		}}
+	}
+
+	var bidResp openrtb.BidResponse
+
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
+
+	for _, sb := range bidResp.SeatBid {
+		for _, bid := range sb.Bid {
+			bidType, err := getMediaTypeForImp(bid.ImpID, internalRequest.Imp)
+			if err != nil {
+				errs = append(errs, err)
+			} else {
+				b := &adapters.TypedBid{
+					Bid:     &bid,
+					BidType: bidType,
+				}
+				bidResponse.Bids = append(bidResponse.Bids, b)
+			}
+		}
+	}
+	return bidResponse, errs
+}
+
+func getMediaTypeForImp(impID string, imps []openrtb.Imp) (openrtb_ext.BidType, error) {
+	mediaType := openrtb_ext.BidTypeBanner
+	for _, imp := range imps {
+		if imp.ID == impID {
+			if imp.Banner != nil {
+				mediaType = openrtb_ext.BidTypeBanner
+			} else if imp.Video != nil {
+				mediaType = openrtb_ext.BidTypeVideo
+			} else if imp.Native != nil {
+				mediaType = openrtb_ext.BidTypeNative
+			}
+			return mediaType, nil
+		}
+	}
+
+	// This shouldnt happen. Lets handle it just incase by returning an error.
+	return "", &errortypes.BadServerResponse{
+		Message: fmt.Sprintf("Failed to find impression \"%s\" ", impID),
+	}
+}
diff --git a/config/config.go b/config/config.go
index c49de91a..f720d841 100755
--- a/config/config.go
+++ b/config/config.go
@@ -834,7 +834,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.gumgum.endpoint", "https://g2.gumgum.com/providers/prbds2s/bid")
 	v.SetDefault("adapters.improvedigital.endpoint", "http://ad.360yield.com/pbs")
 	v.SetDefault("adapters.inmobi.endpoint", "https://api.w.inmobi.com/showad/openrtb/bidder/prebid")
-	v.SetDefault("adapters.ix.endpoint", "http://appnexus-us-east.lb.indexww.com/transbidder?p=184932")
+	v.SetDefault("adapters.ix.endpoint", "http://exchange.indexww.com/pbs?p=192919")
 	v.SetDefault("adapters.krushmedia.endpoint", "http://ads4.krushmedia.com/?c=rtb&m=req&key={{.AccountID}}")
 	v.SetDefault("adapters.invibes.endpoint", "https://{{.Host}}/bid/ServerBidAdContent")
 	v.SetDefault("adapters.kidoz.endpoint", "http://prebid-adapter.kidoz.net/openrtb2/auction?src=prebid-server")
@@ -846,6 +846,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.marsmedia.endpoint", "https://bid306.rtbsrv.com/bidder/?bid=f3xtet")
 	v.SetDefault("adapters.mgid.endpoint", "https://prebid.mgid.com/prebid/")
 	v.SetDefault("adapters.mobilefuse.endpoint", "http://mfx.mobilefuse.com/openrtb?pub_id={{.PublisherID}}")
+	v.SetDefault("adapters.mobfoxpb.endpoint", "http://bes.mobfox.com/?c=o&m=ortb")
 	v.SetDefault("adapters.nanointeractive.endpoint", "https://ad.audiencemanager.de/hbs")
 	v.SetDefault("adapters.ninthdecimal.endpoint", "http://rtb.ninthdecimal.com/xp/get?pubid={{.PublisherID}}")
 	v.SetDefault("adapters.nobid.endpoint", "https://ads.servenobid.com/ortb_adreq?tek=pbs&ver=1")
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 2d73b823..9095ec1e 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -46,6 +46,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/improvedigital"
 	"github.com/prebid/prebid-server/adapters/inmobi"
 	"github.com/prebid/prebid-server/adapters/invibes"
+	"github.com/prebid/prebid-server/adapters/ix"
 	"github.com/prebid/prebid-server/adapters/kidoz"
 	"github.com/prebid/prebid-server/adapters/krushmedia"
 	"github.com/prebid/prebid-server/adapters/kubient"
@@ -54,6 +55,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/lunamedia"
 	"github.com/prebid/prebid-server/adapters/marsmedia"
 	"github.com/prebid/prebid-server/adapters/mgid"
+	"github.com/prebid/prebid-server/adapters/mobfoxpb"
 	"github.com/prebid/prebid-server/adapters/mobilefuse"
 	"github.com/prebid/prebid-server/adapters/nanointeractive"
 	"github.com/prebid/prebid-server/adapters/ninthdecimal"
@@ -142,6 +144,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderImprovedigital:   improvedigital.Builder,
 		openrtb_ext.BidderInMobi:           inmobi.Builder,
 		openrtb_ext.BidderInvibes:          invibes.Builder,
+		openrtb_ext.BidderIx:               ix.Builder,
 		openrtb_ext.BidderKidoz:            kidoz.Builder,
 		openrtb_ext.BidderKrushmedia:       krushmedia.Builder,
 		openrtb_ext.BidderKubient:          kubient.Builder,
@@ -150,6 +153,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderLunaMedia:        lunamedia.Builder,
 		openrtb_ext.BidderMarsmedia:        marsmedia.Builder,
 		openrtb_ext.BidderMgid:             mgid.Builder,
+		openrtb_ext.BidderMobfoxpb:         mobfoxpb.Builder,
 		openrtb_ext.BidderMobileFuse:       mobilefuse.Builder,
 		openrtb_ext.BidderNanoInteractive:  nanointeractive.Builder,
 		openrtb_ext.BidderNinthDecimal:     ninthdecimal.Builder,
diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go
index f361508e..6e12ed00 100644
--- a/exchange/adapter_util.go
+++ b/exchange/adapter_util.go
@@ -7,7 +7,6 @@ import (
 	"github.com/prebid/prebid-server/metrics"
 
 	"github.com/prebid/prebid-server/adapters"
-	"github.com/prebid/prebid-server/adapters/ix"
 	"github.com/prebid/prebid-server/adapters/lifestreet"
 	"github.com/prebid/prebid-server/adapters/pulsepoint"
 	"github.com/prebid/prebid-server/config"
@@ -59,7 +58,7 @@ func buildBidders(adapterConfig map[string]config.Adapter, infos adapters.Bidder
 		}
 
 		// Ignore Legacy Bidders
-		if bidderName == openrtb_ext.BidderIx || bidderName == openrtb_ext.BidderLifestreet || bidderName == openrtb_ext.BidderPulsepoint {
+		if bidderName == openrtb_ext.BidderLifestreet || bidderName == openrtb_ext.BidderPulsepoint {
 			continue
 		}
 
@@ -92,13 +91,7 @@ func buildBidders(adapterConfig map[string]config.Adapter, infos adapters.Bidder
 }
 
 func buildExchangeBiddersLegacy(adapterConfig map[string]config.Adapter, infos adapters.BidderInfos) map[openrtb_ext.BidderName]adaptedBidder {
-	bidders := make(map[openrtb_ext.BidderName]adaptedBidder, 3)
-
-	// Index
-	if infos[string(openrtb_ext.BidderIx)].Status == adapters.StatusActive {
-		adapter := ix.NewIxLegacyAdapter(adapters.DefaultHTTPAdapterConfig, adapterConfig[string(openrtb_ext.BidderIx)].Endpoint)
-		bidders[openrtb_ext.BidderIx] = adaptLegacyAdapter(adapter)
-	}
+	bidders := make(map[openrtb_ext.BidderName]adaptedBidder, 2)
 
 	// Lifestreet
 	if infos[string(openrtb_ext.BidderLifestreet)].Status == adapters.StatusActive {
diff --git a/exchange/gdpr.go b/exchange/gdpr.go
index 2e2343d6..f464cac2 100644
--- a/exchange/gdpr.go
+++ b/exchange/gdpr.go
@@ -4,25 +4,21 @@ import (
 	"encoding/json"
 
 	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/gdpr"
 )
 
 // ExtractGDPR will pull the gdpr flag from an openrtb request
-func extractGDPR(bidRequest *openrtb.BidRequest, usersyncIfAmbiguous bool) (gdpr int) {
+func extractGDPR(bidRequest *openrtb.BidRequest) gdpr.Signal {
 	var re regsExt
 	var err error
 	if bidRequest.Regs != nil {
 		err = json.Unmarshal(bidRequest.Regs.Ext, &re)
 	}
 	if re.GDPR == nil || err != nil {
-		if usersyncIfAmbiguous {
-			gdpr = 0
-		} else {
-			gdpr = 1
-		}
+		return gdpr.SignalAmbiguous
 	} else {
-		gdpr = *re.GDPR
+		return gdpr.Signal(*re.GDPR)
 	}
-	return
 }
 
 // ExtractConsent will pull the consent string from an openrtb request
diff --git a/exchange/utils.go b/exchange/utils.go
index a803e4e2..21c31a29 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -75,8 +75,9 @@ func cleanOpenRTBRequests(ctx context.Context,
 		return
 	}
 
-	gdpr := extractGDPR(req.BidRequest, usersyncIfAmbiguous)
+	gdprSignal := extractGDPR(req.BidRequest)
 	consent := extractConsent(req.BidRequest)
+	gdprEnforced := gdprSignal == gdpr.SignalYes || (gdprSignal == gdpr.SignalAmbiguous && !usersyncIfAmbiguous)
 
 	ccpaEnforcer, err := extractCCPA(req.BidRequest, privacyConfig, &req.Account, aliases, integrationTypeMap[req.LegacyLabels.RType])
 	if err != nil {
@@ -97,9 +98,9 @@ func cleanOpenRTBRequests(ctx context.Context,
 	privacyLabels.COPPAEnforced = privacyEnforcement.COPPA
 	privacyLabels.LMTEnforced = lmtEnforcer.ShouldEnforce(unknownBidder)
 
-	gdprEnabled := gdprEnabled(&req.Account, privacyConfig, integrationTypeMap[req.LegacyLabels.RType])
+	gdprEnforced = gdprEnforced && gdprEnabled(&req.Account, privacyConfig, integrationTypeMap[req.LegacyLabels.RType])
 
-	if gdpr == 1 && gdprEnabled {
+	if gdprEnforced {
 		privacyLabels.GDPREnforced = true
 		parsedConsent, err := vendorconsent.ParseString(consent)
 		if err == nil {
@@ -114,9 +115,9 @@ func cleanOpenRTBRequests(ctx context.Context,
 		privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String())
 
 		// GDPR
-		if gdpr == 1 && gdprEnabled {
+		if gdprEnforced {
 			var publisherID = req.LegacyLabels.PubID
-			_, geo, id, err := gDPR.PersonalInfoAllowed(ctx, bidderRequest.BidderCoreName, publisherID, consent)
+			_, geo, id, err := gDPR.PersonalInfoAllowed(ctx, bidderRequest.BidderCoreName, publisherID, gdprSignal, consent)
 			privacyEnforcement.GDPRGeo = !geo && err == nil
 			privacyEnforcement.GDPRID = !id && err == nil
 		} else {
diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go
index 60a7cc1e..eea7fa5a 100644
--- a/gdpr/gdpr.go
+++ b/gdpr/gdpr.go
@@ -23,7 +23,7 @@ type Permissions interface {
 	// Determines whether or not to send PI information to a bidder, or mask it out.
 	//
 	// If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent.
-	PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error)
+	PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string) (bool, bool, bool, error)
 }
 
 // Versions of the GDPR TCF technical specification.
diff --git a/gdpr/impl.go b/gdpr/impl.go
index 5b3b86fe..e1822ece 100644
--- a/gdpr/impl.go
+++ b/gdpr/impl.go
@@ -19,6 +19,14 @@ import (
 //
 // Nothing in this file is exported. Public APIs can be found in gdpr.go
 
+type Signal int
+
+const (
+	SignalAmbiguous Signal = -1
+	SignalNo        Signal = 0
+	SignalYes       Signal = 1
+)
+
 type permissionsImpl struct {
 	cfg             config.GDPR
 	vendorIDs       map[openrtb_ext.BidderName]uint16
@@ -42,21 +50,35 @@ func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	return false, nil
 }
 
-func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) {
-	_, ok := p.cfg.NonStandardPublisherMap[PublisherID]
-	if ok {
+func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string) (allowPI bool, allowGeo bool, allowID bool, err error) {
+	if _, ok := p.cfg.NonStandardPublisherMap[PublisherID]; ok {
 		return true, true, true, nil
 	}
 
-	id, ok := p.vendorIDs[bidder]
-	if ok {
-		return p.allowPI(ctx, id, consent)
+	if gdprSignal == SignalAmbiguous {
+		if p.cfg.UsersyncIfAmbiguous {
+			gdprSignal = SignalNo
+		} else {
+			gdprSignal = SignalYes
+		}
 	}
 
-	if consent == "" {
-		return p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, nil
+	if gdprSignal == SignalNo {
+		return true, true, true, nil
+	}
+
+	if consent == "" && gdprSignal == SignalYes {
+		return false, false, false, nil
+	}
+
+	if id, ok := p.vendorIDs[bidder]; ok {
+		return p.allowPI(ctx, id, consent)
 	}
 
+	return p.defaultVendorPermissions()
+}
+
+func (p *permissionsImpl) defaultVendorPermissions() (allowPI bool, allowGeo bool, allowID bool, err error) {
 	return false, false, false, nil
 }
 
@@ -95,11 +117,6 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, consen
 }
 
 func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent string) (bool, bool, bool, error) {
-	// If we're not given a consent string, respect the preferences in the app config.
-	if consent == "" {
-		return p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, nil
-	}
-
 	parsedConsent, vendor, err := p.parseVendor(ctx, vendorID, consent)
 	if err != nil {
 		return false, false, false, err
@@ -217,7 +234,7 @@ func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.B
 	return true, nil
 }
 
-func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) {
+func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string) (bool, bool, bool, error) {
 	return true, true, true, nil
 }
 
@@ -232,6 +249,6 @@ func (a AlwaysFail) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.Bi
 	return false, nil
 }
 
-func (a AlwaysFail) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) {
+func (a AlwaysFail) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string) (bool, bool, bool, error) {
 	return false, false, false, nil
 }
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 6f426c52..f7f83125 100755
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -94,6 +94,7 @@ const (
 	BidderLunaMedia        BidderName = "lunamedia"
 	BidderMarsmedia        BidderName = "marsmedia"
 	BidderMgid             BidderName = "mgid"
+	BidderMobfoxpb         BidderName = "mobfoxpb"
 	BidderMobileFuse       BidderName = "mobilefuse"
 	BidderNanoInteractive  BidderName = "nanointeractive"
 	BidderNinthDecimal     BidderName = "ninthdecimal"
@@ -190,6 +191,7 @@ func CoreBidderNames() []BidderName {
 		BidderLunaMedia,
 		BidderMarsmedia,
 		BidderMgid,
+		BidderMobfoxpb,
 		BidderMobileFuse,
 		BidderNanoInteractive,
 		BidderNinthDecimal,
diff --git a/openrtb_ext/imp_ix.go b/openrtb_ext/imp_ix.go
new file mode 100644
index 00000000..99cd3a21
--- /dev/null
+++ b/openrtb_ext/imp_ix.go
@@ -0,0 +1,7 @@
+package openrtb_ext
+
+// ExtImpIx defines the contract for bidrequest.imp[i].ext.ix
+type ExtImpIx struct {
+	SiteId string `json:"siteId"`
+	Size   []int  `json:"size"`
+}
diff --git a/static/bidder-info/ix.yaml b/static/bidder-info/ix.yaml
index 326989ae..2f00ceb9 100644
--- a/static/bidder-info/ix.yaml
+++ b/static/bidder-info/ix.yaml
@@ -1,6 +1,15 @@
 maintainer:
   email: "pdu-supply-prebid@indexexchange.com"
 capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+      - audio
   site:
     mediaTypes:
       - banner
+      - video
+      - native
+      - audio
diff --git a/static/bidder-info/mobfoxpb.yaml b/static/bidder-info/mobfoxpb.yaml
new file mode 100644
index 00000000..ba3bb60d
--- /dev/null
+++ b/static/bidder-info/mobfoxpb.yaml
@@ -0,0 +1,11 @@
+maintainer:
+  email: "platform@mobfox.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+  site:
+    mediaTypes:
+      - banner
+      - video
diff --git a/static/bidder-params/mobfoxpb.json b/static/bidder-params/mobfoxpb.json
new file mode 100644
index 00000000..0cc7a16c
--- /dev/null
+++ b/static/bidder-params/mobfoxpb.json
@@ -0,0 +1,14 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Mobfox Adapter Params",
+  "description": "A schema which validates params accepted by the Mobfox adapter",
+  "type": "object",
+  "properties": {
+    "TagID": {
+      "type": "string",
+      "minLength": 1,
+      "description": "An ID which identifies the mobfox ad tag"
+    }
+  },
+  "required" : [ "TagID" ]
+}


Test patch:
diff --git a/adapters/ix/ix_test.go b/adapters/ix/ix_test.go
index 1a9abd24..8a75e685 100644
--- a/adapters/ix/ix_test.go
+++ b/adapters/ix/ix_test.go
@@ -13,10 +13,26 @@ import (
 
 	"github.com/mxmCherry/openrtb"
 	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/pbs"
 )
 
-const url string = "http://appnexus-us-east.lb.indexww.com/bidder?p=184932"
+const endpoint string = "http://host/endpoint"
+
+func TestJsonSamples(t *testing.T) {
+	if bidder, err := Builder(openrtb_ext.BidderIx, config.Adapter{Endpoint: endpoint}); err == nil {
+		ixBidder := bidder.(*IxAdapter)
+		ixBidder.maxRequests = 2
+		adapterstest.RunJSONBidderTest(t, "ixtest", bidder)
+	} else {
+		t.Fatalf("Builder returned unexpected error %v", err)
+	}
+}
+
+// Tests for the legacy, non-openrtb code.
+// They can be removed after the legacy interface is deprecated.
 
 func getAdUnit() pbs.PBSAdUnit {
 	return pbs.PBSAdUnit{
@@ -33,22 +49,50 @@ func getAdUnit() pbs.PBSAdUnit {
 	}
 }
 
+func getVideoAdUnit() pbs.PBSAdUnit {
+	return pbs.PBSAdUnit{
+		Code:       "unitCodeVideo",
+		MediaTypes: []pbs.MediaType{pbs.MEDIA_TYPE_VIDEO},
+		BidID:      "bididvideo",
+		Sizes: []openrtb.Format{
+			{
+				W: 100,
+				H: 75,
+			},
+		},
+		Video: pbs.PBSVideo{
+			Mimes:          []string{"video/mp4"},
+			Minduration:    15,
+			Maxduration:    30,
+			Startdelay:     5,
+			Skippable:      0,
+			PlaybackMethod: 1,
+			Protocols:      []int8{2, 3},
+		},
+		Params: json.RawMessage("{\"siteId\":\"12\"}"),
+	}
+}
+
 func getOpenRTBBid(i openrtb.Imp) openrtb.Bid {
 	return openrtb.Bid{
-		ID:     fmt.Sprintf("%d", rand.Intn(1000)),
-		ImpID:  i.ID,
-		Price:  1.0,
-		AdM:    "Content",
-		CrID:   fmt.Sprintf("%d", rand.Intn(1000)),
-		W:      *i.Banner.W,
-		H:      *i.Banner.H,
-		DealID: "5",
+		ID:    fmt.Sprintf("%d", rand.Intn(1000)),
+		ImpID: i.ID,
+		Price: 1.0,
+		AdM:   "Content",
 	}
 }
 
+func newAdapter(endpoint string) *IxAdapter {
+	return NewIxLegacyAdapter(adapters.DefaultHTTPAdapterConfig, endpoint)
+}
+
 func dummyIXServer(w http.ResponseWriter, r *http.Request) {
 	defer r.Body.Close()
 	body, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
 
 	var breq openrtb.BidRequest
 	err = json.Unmarshal(body, &breq)
@@ -78,43 +122,36 @@ func dummyIXServer(w http.ResponseWriter, r *http.Request) {
 	w.Write(js)
 }
 
-func TestIxInvalidCall(t *testing.T) {
-
-	an := NewIxLegacyAdapter(adapters.DefaultHTTPAdapterConfig, url)
-	an.URI = "blah"
+func TestIxSkipNoCookies(t *testing.T) {
+	if newAdapter(endpoint).SkipNoCookies() {
+		t.Fatalf("SkipNoCookies must return false")
+	}
+}
 
+func TestIxInvalidCall(t *testing.T) {
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	pbBidder := pbs.PBSBidder{}
-	_, err := an.Call(ctx, &pbReq, &pbBidder)
+	_, err := newAdapter(endpoint).Call(ctx, &pbReq, &pbBidder)
 	if err == nil {
 		t.Fatalf("No error received for invalid request")
 	}
 }
 
 func TestIxInvalidCallReqAppNil(t *testing.T) {
-
-	an := NewIxLegacyAdapter(adapters.DefaultHTTPAdapterConfig, url)
-	an.URI = "blah"
-
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{
 		App: &openrtb.App{},
 	}
-
 	pbBidder := pbs.PBSBidder{}
-	_, err := an.Call(ctx, &pbReq, &pbBidder)
 
+	_, err := newAdapter(endpoint).Call(ctx, &pbReq, &pbBidder)
 	if err == nil {
 		t.Fatalf("No error received for invalid request")
 	}
 }
 
 func TestIxInvalidCallMissingSiteID(t *testing.T) {
-
-	an := NewIxLegacyAdapter(adapters.DefaultHTTPAdapterConfig, url)
-	an.URI = "blah"
-
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	adUnit := getAdUnit()
@@ -126,7 +163,7 @@ func TestIxInvalidCallMissingSiteID(t *testing.T) {
 			adUnit,
 		},
 	}
-	_, err := an.Call(ctx, &pbReq, &pbBidder)
+	_, err := newAdapter(endpoint).Call(ctx, &pbReq, &pbBidder)
 	if err == nil {
 		t.Fatalf("No error received for request with missing siteId")
 	}
@@ -141,8 +178,6 @@ func TestIxTimeout(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx, cancel := context.WithTimeout(context.Background(), 0)
 	defer cancel()
 
@@ -153,7 +188,7 @@ func TestIxTimeout(t *testing.T) {
 			getAdUnit(),
 		},
 	}
-	_, err := an.Call(ctx, &pbReq, &pbBidder)
+	_, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder)
 	if err == nil || err != context.DeadlineExceeded {
 		t.Fatalf("Invalid timeout error received")
 	}
@@ -207,8 +242,6 @@ func TestIxTimeoutMultipleSlots(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	pbReq := pbs.PBSRequest{}
 
 	adUnit1 := getAdUnit()
@@ -227,7 +260,7 @@ func TestIxTimeoutMultipleSlots(t *testing.T) {
 			adUnit2,
 		},
 	}
-	bids, err := an.Call(ctx, &pbReq, &pbBidder)
+	bids, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder)
 
 	if err != nil {
 		t.Fatalf("Should not have gotten an error: %v", err)
@@ -252,8 +285,6 @@ func TestIxInvalidJsonResponse(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	pbBidder := pbs.PBSBidder{
@@ -262,7 +293,7 @@ func TestIxInvalidJsonResponse(t *testing.T) {
 			getAdUnit(),
 		},
 	}
-	_, err := an.Call(ctx, &pbReq, &pbBidder)
+	_, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder)
 	if err == nil {
 		t.Fatalf("No error received for invalid request")
 	}
@@ -278,17 +309,15 @@ func TestIxInvalidStatusCode(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx := context.TODO()
-	pbReq := pbs.PBSRequest{}
+	pbReq := pbs.PBSRequest{IsDebug: true}
 	pbBidder := pbs.PBSBidder{
 		BidderCode: "bannerCode",
 		AdUnits: []pbs.PBSAdUnit{
 			getAdUnit(),
 		},
 	}
-	_, err := an.Call(ctx, &pbReq, &pbBidder)
+	_, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder)
 	if err == nil {
 		t.Fatalf("No error received for invalid request")
 	}
@@ -304,8 +333,6 @@ func TestIxBadRequest(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	pbBidder := pbs.PBSBidder{
@@ -314,7 +341,7 @@ func TestIxBadRequest(t *testing.T) {
 			getAdUnit(),
 		},
 	}
-	_, err := an.Call(ctx, &pbReq, &pbBidder)
+	_, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder)
 	if err == nil {
 		t.Fatalf("No error received for bad request")
 	}
@@ -330,8 +357,6 @@ func TestIxNoContent(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	pbBidder := pbs.PBSBidder{
@@ -341,7 +366,7 @@ func TestIxNoContent(t *testing.T) {
 		},
 	}
 
-	bids, err := an.Call(ctx, &pbReq, &pbBidder)
+	bids, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder)
 	if err != nil || bids != nil {
 		t.Fatalf("Must return nil for no content")
 	}
@@ -354,8 +379,6 @@ func TestIxInvalidCallMissingSize(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	adUnit := getAdUnit()
@@ -366,7 +389,7 @@ func TestIxInvalidCallMissingSize(t *testing.T) {
 			adUnit,
 		},
 	}
-	if _, err := an.Call(ctx, &pbReq, &pbBidder); err == nil {
+	if _, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder); err == nil {
 		t.Fatalf("Should not have gotten an error for missing/invalid size: %v", err)
 	}
 }
@@ -378,8 +401,6 @@ func TestIxInvalidCallEmptyBidIDResponse(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	adUnit := getAdUnit()
@@ -390,7 +411,7 @@ func TestIxInvalidCallEmptyBidIDResponse(t *testing.T) {
 			adUnit,
 		},
 	}
-	if _, err := an.Call(ctx, &pbReq, &pbBidder); err == nil {
+	if _, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder); err == nil {
 		t.Fatalf("Should have gotten an error for unknown adunit code")
 	}
 }
@@ -414,14 +435,12 @@ func TestIxMismatchUnitCode(t *testing.T) {
 					{
 						Bid: []openrtb.Bid{
 							{
-								ID:     fmt.Sprintf("%d", rand.Intn(1000)),
-								ImpID:  "unitCode_bogus",
-								Price:  1.0,
-								AdM:    "Content",
-								CrID:   "567",
-								W:      10,
-								H:      12,
-								DealID: "5",
+								ID:    fmt.Sprintf("%d", rand.Intn(1000)),
+								ImpID: "unitCode_bogus",
+								Price: 1.0,
+								AdM:   "Content",
+								W:     10,
+								H:     12,
 							},
 						},
 					},
@@ -439,8 +458,6 @@ func TestIxMismatchUnitCode(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	pbBidder := pbs.PBSBidder{
@@ -449,11 +466,93 @@ func TestIxMismatchUnitCode(t *testing.T) {
 			getAdUnit(),
 		},
 	}
-	if _, err := an.Call(ctx, &pbReq, &pbBidder); err == nil {
+	if _, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder); err == nil {
 		t.Fatalf("Should have gotten an error for unknown adunit code")
 	}
 }
 
+func TestNoSeatBid(t *testing.T) {
+	server := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			defer r.Body.Close()
+			body, err := ioutil.ReadAll(r.Body)
+
+			var breq openrtb.BidRequest
+			err = json.Unmarshal(body, &breq)
+			if err != nil {
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
+			}
+
+			resp := openrtb.BidResponse{}
+
+			js, err := json.Marshal(resp)
+			if err != nil {
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
+			}
+			w.Header().Set("Content-Type", "application/json")
+			w.Write(js)
+		}),
+	)
+	defer server.Close()
+
+	ctx := context.TODO()
+	pbReq := pbs.PBSRequest{}
+	pbBidder := pbs.PBSBidder{
+		BidderCode: "bannerCode",
+		AdUnits: []pbs.PBSAdUnit{
+			getAdUnit(),
+		},
+	}
+	if _, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder); err != nil {
+		t.Fatalf("Should not have gotten an error: %v", err)
+	}
+}
+
+func TestNoSeatBidBid(t *testing.T) {
+	server := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			defer r.Body.Close()
+			body, err := ioutil.ReadAll(r.Body)
+
+			var breq openrtb.BidRequest
+			err = json.Unmarshal(body, &breq)
+			if err != nil {
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
+			}
+
+			resp := openrtb.BidResponse{
+				SeatBid: []openrtb.SeatBid{
+					{},
+				},
+			}
+
+			js, err := json.Marshal(resp)
+			if err != nil {
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
+			}
+			w.Header().Set("Content-Type", "application/json")
+			w.Write(js)
+		}),
+	)
+	defer server.Close()
+
+	ctx := context.TODO()
+	pbReq := pbs.PBSRequest{}
+	pbBidder := pbs.PBSBidder{
+		BidderCode: "bannerCode",
+		AdUnits: []pbs.PBSAdUnit{
+			getAdUnit(),
+		},
+	}
+	if _, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder); err != nil {
+		t.Fatalf("Should not have gotten an error: %v", err)
+	}
+}
+
 func TestIxInvalidParam(t *testing.T) {
 
 	server := httptest.NewServer(
@@ -461,8 +560,6 @@ func TestIxInvalidParam(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	adUnit := getAdUnit()
@@ -473,7 +570,7 @@ func TestIxInvalidParam(t *testing.T) {
 			adUnit,
 		},
 	}
-	if _, err := an.Call(ctx, &pbReq, &pbBidder); err == nil {
+	if _, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder); err == nil {
 		t.Fatalf("Should have gotten an error for unrecognized params")
 	}
 }
@@ -485,8 +582,6 @@ func TestIxSingleSlotSingleValidSize(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	pbBidder := pbs.PBSBidder{
@@ -495,7 +590,7 @@ func TestIxSingleSlotSingleValidSize(t *testing.T) {
 			getAdUnit(),
 		},
 	}
-	bids, err := an.Call(ctx, &pbReq, &pbBidder)
+	bids, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder)
 	if err != nil {
 		t.Fatalf("Should not have gotten an error: %v", err)
 	}
@@ -512,19 +607,10 @@ func TestIxTwoSlotValidSize(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	adUnit1 := getAdUnit()
-	adUnit2 := getAdUnit()
-	adUnit2.Code = "unitCode2"
-	adUnit2.Sizes = []openrtb.Format{
-		{
-			W: 8,
-			H: 10,
-		},
-	}
+	adUnit2 := getVideoAdUnit()
 	adUnit2.Params = json.RawMessage("{\"siteId\":\"1111\"}")
 
 	pbBidder := pbs.PBSBidder{
@@ -534,7 +620,7 @@ func TestIxTwoSlotValidSize(t *testing.T) {
 			adUnit2,
 		},
 	}
-	bids, err := an.Call(ctx, &pbReq, &pbBidder)
+	bids, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder)
 	if err != nil {
 		t.Fatalf("Should not have gotten an error: %v", err)
 	}
@@ -561,8 +647,6 @@ func TestIxTwoSlotMultiSizeOnlyValidIXSizeResponse(t *testing.T) {
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	adUnit := getAdUnit()
@@ -574,8 +658,7 @@ func TestIxTwoSlotMultiSizeOnlyValidIXSizeResponse(t *testing.T) {
 			adUnit,
 		},
 	}
-	bids, err := an.Call(ctx, &pbReq, &pbBidder)
-
+	bids, err := newAdapter(server.URL).Call(ctx, &pbReq, &pbBidder)
 	if err != nil {
 		t.Fatalf("Should not have gotten an error: %v", err)
 	}
@@ -609,20 +692,19 @@ func findBidByAdUnitCode(bids pbs.PBSBidSlice, c string) *pbs.PBSBid {
 	return &pbs.PBSBid{}
 }
 
-func TestIxRequestLimit(t *testing.T) {
+func TestIxMaxRequests(t *testing.T) {
 
 	server := httptest.NewServer(
 		http.HandlerFunc(dummyIXServer),
 	)
 	defer server.Close()
 
-	conf := *adapters.DefaultHTTPAdapterConfig
-	an := NewIxLegacyAdapter(&conf, server.URL)
+	adapter := newAdapter(server.URL)
 	ctx := context.TODO()
 	pbReq := pbs.PBSRequest{}
 	adUnits := []pbs.PBSAdUnit{}
 
-	for i := 0; i < requestLimit+1; i++ {
+	for i := 0; i < adapter.maxRequests+1; i++ {
 		adUnits = append(adUnits, getAdUnit())
 	}
 
@@ -631,13 +713,12 @@ func TestIxRequestLimit(t *testing.T) {
 		AdUnits:    adUnits,
 	}
 
-	bids, err := an.Call(ctx, &pbReq, &pbBidder)
-
+	bids, err := adapter.Call(ctx, &pbReq, &pbBidder)
 	if err != nil {
 		t.Fatalf("Should not have gotten an error: %v", err)
 	}
 
-	if len(bids) != requestLimit {
-		t.Fatalf("Should have received %d bid", requestLimit)
+	if len(bids) != adapter.maxRequests {
+		t.Fatalf("Should have received %d bid", adapter.maxRequests)
 	}
 }
diff --git a/adapters/ix/ixtest/exemplary/additional-consent.json b/adapters/ix/ixtest/exemplary/additional-consent.json
new file mode 100644
index 00000000..697c8697
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/additional-consent.json
@@ -0,0 +1,124 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "w": 300,
+                    "h": 250
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ],
+        "user": {
+            "ext": {
+                "consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw",
+                "consented_providers_settings": {
+                    "consented_providers": [1]
+                }
+            }
+        }
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ],
+                    "user": {
+                        "ext": {
+                            "consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw",
+                            "consented_providers_settings": {
+                                "consented_providers": [1]
+                            }
+                        }
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/exemplary/banner-no-format.json b/adapters/ix/ixtest/exemplary/banner-no-format.json
new file mode 100644
index 00000000..84f8499f
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/banner-no-format.json
@@ -0,0 +1,108 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "w": 300,
+                    "h": 250
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/exemplary/max-requests.json b/adapters/ix/ixtest/exemplary/max-requests.json
new file mode 100644
index 00000000..46d9ec5d
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/max-requests.json
@@ -0,0 +1,255 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id-1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            },
+            {
+                "id": "test-imp-id-2",
+                "video": {
+                    "mimes": [
+                        "video/mp4"
+                    ],
+                    "minduration": 15,
+                    "maxduration": 30,
+                    "protocols": [
+                        2,
+                        3,
+                        5,
+                        6,
+                        7,
+                        8
+                    ],
+                    "w": 940,
+                    "h": 560
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            },
+            {
+                "id": "test-imp-id-3",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id-1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id-1",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        },
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id-2",
+                            "video": {
+                                "mimes": [
+                                    "video/mp4"
+                                ],
+                                "minduration": 15,
+                                "maxduration": 30,
+                                "protocols": [
+                                    2,
+                                    3,
+                                    5,
+                                    6,
+                                    7,
+                                    8
+                                ],
+                                "w": 940,
+                                "h": 560
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id-2",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "cat": [
+                                        "IAB9-1"
+                                    ],
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id-1",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        },
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id-2",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "cat": [
+                            "IAB9-1"
+                        ],
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "video"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/exemplary/no-pub-id.json b/adapters/ix/ixtest/exemplary/no-pub-id.json
new file mode 100644
index 00000000..838d3c5e
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/no-pub-id.json
@@ -0,0 +1,121 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "w": 300,
+                    "h": 250
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ],
+        "site": {
+            "page": "https://www.example.com/",
+            "publisher": {
+                "name": "publisher-name"
+            }
+        }
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ],
+                    "site": {
+                        "page": "https://www.example.com/",
+                        "publisher": {
+                            "id": "569749",
+                            "name": "publisher-name"
+                        }
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/exemplary/no-pub.json b/adapters/ix/ixtest/exemplary/no-pub.json
new file mode 100644
index 00000000..bc113468
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/no-pub.json
@@ -0,0 +1,117 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "w": 300,
+                    "h": 250
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ],
+        "site": {
+            "page": "https://www.example.com/"
+        }
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ],
+                    "site": {
+                        "page": "https://www.example.com/",
+                        "publisher": {
+                            "id": "569749"
+                        }
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/exemplary/simple-audio.json b/adapters/ix/ixtest/exemplary/simple-audio.json
new file mode 100644
index 00000000..6bfe62a4
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/simple-audio.json
@@ -0,0 +1,102 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "audio": {
+                    "mimes": [
+                        "audio/mp4"
+                    ],
+                    "protocols": [
+                        9,
+                        10
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "audio": {
+                                "mimes": [
+                                    "audio/mp4"
+                                ],
+                                "protocols": [
+                                    9,
+                                    10
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110"
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.5,
+                        "adid": "29681110",
+                        "adm": "some-test-ad",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110"
+                    },
+                    "type": "audio"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/exemplary/simple-banner-multi-size.json b/adapters/ix/ixtest/exemplary/simple-banner-multi-size.json
new file mode 100644
index 00000000..5e0a311a
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/simple-banner-multi-size.json
@@ -0,0 +1,201 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        },
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 600
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 600,
+                                    "w": 300,
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        },
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 600,
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/exemplary/simple-native.json b/adapters/ix/ixtest/exemplary/simple-native.json
new file mode 100644
index 00000000..845ba7cd
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/simple-native.json
@@ -0,0 +1,104 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "native": {
+                    "request": "{}",
+                    "ver": "1.1"
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpcalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "native": {
+                                "request": "{}",
+                                "ver": "1.1"
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "cat": [
+                                        "IAB3-1"
+                                    ],
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.5,
+                        "adid": "29681110",
+                        "adm": "some-test-ad",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "cat": [
+                            "IAB3-1"
+                        ],
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "native"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/exemplary/simple-video.json b/adapters/ix/ixtest/exemplary/simple-video.json
new file mode 100644
index 00000000..051eaa98
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/simple-video.json
@@ -0,0 +1,134 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "video": {
+                    "mimes": [
+                        "video/mp4"
+                    ],
+                    "minduration": 15,
+                    "maxduration": 30,
+                    "protocols": [
+                        2,
+                        3,
+                        5,
+                        6,
+                        7,
+                        8
+                    ],
+                    "w": 940,
+                    "h": 560
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "video": {
+                                "mimes": [
+                                    "video/mp4"
+                                ],
+                                "minduration": 15,
+                                "maxduration": 30,
+                                "protocols": [
+                                    2,
+                                    3,
+                                    5,
+                                    6,
+                                    7,
+                                    8
+                                ],
+                                "w": 940,
+                                "h": 560
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "cat": [
+                                        "IAB9-1"
+                                    ],
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "cat": [
+                            "IAB9-1"
+                        ],
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "video"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/params/race/audio.json b/adapters/ix/ixtest/params/race/audio.json
new file mode 100644
index 00000000..703df52e
--- /dev/null
+++ b/adapters/ix/ixtest/params/race/audio.json
@@ -0,0 +1,4 @@
+{
+  "siteId": "569749",
+  "size": [300, 250]
+}
diff --git a/adapters/ix/ixtest/params/race/banner.json b/adapters/ix/ixtest/params/race/banner.json
index e90050ef..703df52e 100644
--- a/adapters/ix/ixtest/params/race/banner.json
+++ b/adapters/ix/ixtest/params/race/banner.json
@@ -1,3 +1,4 @@
 {
-  "siteId": 500
+  "siteId": "569749",
+  "size": [300, 250]
 }
diff --git a/adapters/ix/ixtest/params/race/native.json b/adapters/ix/ixtest/params/race/native.json
new file mode 100644
index 00000000..703df52e
--- /dev/null
+++ b/adapters/ix/ixtest/params/race/native.json
@@ -0,0 +1,4 @@
+{
+  "siteId": "569749",
+  "size": [300, 250]
+}
diff --git a/adapters/ix/ixtest/params/race/video.json b/adapters/ix/ixtest/params/race/video.json
index e90050ef..993f0f6a 100644
--- a/adapters/ix/ixtest/params/race/video.json
+++ b/adapters/ix/ixtest/params/race/video.json
@@ -1,3 +1,4 @@
 {
-  "siteId": 500
+  "siteId": "569749",
+  "size": [940, 560]
 }
diff --git a/adapters/ix/ixtest/supplemental/bad-ext-bidder.json b/adapters/ix/ixtest/supplemental/bad-ext-bidder.json
new file mode 100644
index 00000000..e08da1e1
--- /dev/null
+++ b/adapters/ix/ixtest/supplemental/bad-ext-bidder.json
@@ -0,0 +1,22 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "ext": {
+                    "prebid": 1
+                }
+            }
+        ],
+        "site": {
+            "page": "https://www.example.com/"
+        }
+    },
+    "expectedMakeRequestsErrors": [
+        {
+            "value": "json: cannot unmarshal number into Go struct field ExtImpBidder.prebid of type openrtb_ext.ExtImpPrebid",
+            "comparison": "literal"
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/supplemental/bad-ext-ix.json b/adapters/ix/ixtest/supplemental/bad-ext-ix.json
new file mode 100644
index 00000000..a9a0383b
--- /dev/null
+++ b/adapters/ix/ixtest/supplemental/bad-ext-ix.json
@@ -0,0 +1,21 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "ext": {
+                }
+            }
+        ],
+        "site": {
+            "page": "https://www.example.com/"
+        }
+    },
+    "expectedMakeRequestsErrors": [
+        {
+            "value": "unexpected end of JSON input",
+            "comparison": "literal"
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/supplemental/bad-imp-id.json b/adapters/ix/ixtest/supplemental/bad-imp-id.json
new file mode 100644
index 00000000..0b852c85
--- /dev/null
+++ b/adapters/ix/ixtest/supplemental/bad-imp-id.json
@@ -0,0 +1,118 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "7706636740145184841",
+                                    "impid": "bad-imp-id",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "7706636740145184841",
+                        "impid": "bad-imp-id",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": ""
+                }
+            ]
+        }
+    ],
+    "expectedMakeBidsErrors": [
+        {
+            "value": "Unmatched impression id: bad-imp-id.",
+            "comparison": "literal"
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/supplemental/bad-request.json b/adapters/ix/ixtest/supplemental/bad-request.json
new file mode 100644
index 00000000..f520d785
--- /dev/null
+++ b/adapters/ix/ixtest/supplemental/bad-request.json
@@ -0,0 +1,63 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 400,
+                "body": {}
+            }
+        }
+    ],
+    "expectedMakeBidsErrors": [
+        {
+            "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+            "comparison": "literal"
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/supplemental/bad-response-body.json b/adapters/ix/ixtest/supplemental/bad-response-body.json
new file mode 100644
index 00000000..80c2860e
--- /dev/null
+++ b/adapters/ix/ixtest/supplemental/bad-response-body.json
@@ -0,0 +1,65 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": 1
+                }
+            }
+        }
+    ],
+    "expectedMakeBidsErrors": [
+        {
+            "value": "JSON parsing error: json: cannot unmarshal number into Go struct field BidResponse.id of type string",
+            "comparison": "literal"
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/supplemental/no-content.json b/adapters/ix/ixtest/supplemental/no-content.json
new file mode 100644
index 00000000..fcdf02cb
--- /dev/null
+++ b/adapters/ix/ixtest/supplemental/no-content.json
@@ -0,0 +1,57 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 204,
+                "body": {}
+            }
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/supplemental/no-imp.json b/adapters/ix/ixtest/supplemental/no-imp.json
new file mode 100644
index 00000000..308decd2
--- /dev/null
+++ b/adapters/ix/ixtest/supplemental/no-imp.json
@@ -0,0 +1,6 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": []
+    }
+}
diff --git a/adapters/ix/ixtest/supplemental/not-found.json b/adapters/ix/ixtest/supplemental/not-found.json
new file mode 100644
index 00000000..980bd942
--- /dev/null
+++ b/adapters/ix/ixtest/supplemental/not-found.json
@@ -0,0 +1,63 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 404,
+                "body": {}
+            }
+        }
+    ],
+    "expectedMakeBidsErrors": [
+        {
+            "value": "Unexpected status code: 404. Run with request.debug = 1 for more info",
+            "comparison": "literal"
+        }
+    ]
+}
diff --git a/adapters/mobfoxpb/mobfoxpb_test.go b/adapters/mobfoxpb/mobfoxpb_test.go
new file mode 100644
index 00000000..271d30a9
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpb_test.go
@@ -0,0 +1,18 @@
+package mobfoxpb
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderMobfoxpb, config.Adapter{
+		Endpoint: "http://example.com/?c=o&m=ortb"})
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+	adapterstest.RunJSONBidderTest(t, "mobfoxpbtest", bidder)
+}
diff --git a/adapters/mobfoxpb/mobfoxpbtest/exemplary/simple-banner.json b/adapters/mobfoxpb/mobfoxpbtest/exemplary/simple-banner.json
new file mode 100644
index 00000000..b1936661
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/exemplary/simple-banner.json
@@ -0,0 +1,132 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+        {
+            "id": "test-imp-id",
+            "banner": {
+                "format": [
+                    {
+                      "w": 300,
+                      "h": 250
+                    },
+                    {
+                      "w": 300,
+                      "h": 600
+                    }
+                ]
+            },
+            "tagid": "6",
+            "ext": {
+                "bidder": {
+                  "TagID": "6"
+                }
+            }
+        }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    }
+},
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+              {
+                "id": "test-imp-id",
+                "banner": {
+                  "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+                "tagid": "6",
+                "ext": {
+                  "bidder": {
+                  "TagID": "6"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+              {
+                "bid": [
+                  {
+                    "id": "test_bid_id",
+                    "impid": "test-imp-id",
+                    "price": 0.27543,
+                    "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"https://bes.mobfox.com/?c=o&m=adm&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                    "cid": "test_cid",
+                    "crid": "test_crid",
+                    "dealid": "test_dealid",
+                    "w": 300,
+                    "h": 250,
+                    "ext": {
+                      "prebid": {
+                        "type": "banner"
+                      }
+                    }
+                  }
+                ]
+              }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"https://bes.mobfox.com/?c=o&m=adm&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/mobfoxpb/mobfoxpbtest/exemplary/simple-video.json b/adapters/mobfoxpb/mobfoxpbtest/exemplary/simple-video.json
new file mode 100644
index 00000000..6cdcdc5a
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/exemplary/simple-video.json
@@ -0,0 +1,119 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    },
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": ["video/mp4"],
+          "protocols": [2, 5],
+          "w": 1024,
+          "h": 576
+        },
+        "ext": {
+          "bidder": {
+            "TagID": "7"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+          },
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "video": {
+                "mimes": ["video/mp4"],
+                "protocols": [2, 5],
+                "w": 1024,
+                "h": 576
+              },
+              "tagid": "7",
+              "ext": {
+                "bidder": {
+                  "TagID": "7"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+          {
+            "bid": [
+              {
+                "id": "test_bid_id",
+                "impid": "test-imp-id",
+                "price": 0.27543,
+                "adm": "<VAST version=\"3.0\"><Ad id=\"20001\" sequence=\"1\"><InLine><AdSystem version=\"4.0\"><![CDATA[iabtechlab]]></AdSystem><AdTitle><![CDATA[Inline Simple Ad]]></AdTitle><Impression><![CDATA[]]></Impression><Creatives><Creative id=\"5480\" sequence=\"1\"><Linear><Duration>00:01:00</Duration><MediaFiles><MediaFile id=\"5246\" delivery=\"progressive\" type=\"video/mp4\" bitrate=\"600\" width=\"640\" height=\"360\" minBitrate=\"500\" maxBitrate=\"700\" scalable=\"1\" maintainAspectRatio=\"1\" codec=\"0\"><![CDATA[https://s0.2mdn.net/4253510/google_ddm_animation_480P.mp4]]></MediaFile></MediaFiles><VideoClicks><ClickThrough id=\"blog\"><![CDATA[https://example.com]]></ClickThrough></VideoClicks></Linear></Creative></Creatives></InLine></Ad></VAST>",
+                "cid": "test_cid",
+                "crid": "test_crid",
+                "dealid": "test_dealid",
+                "ext": {
+                  "prebid": {
+                    "type": "video"
+                  }
+                }
+              }
+            ],
+            "seat": "mobfoxpb"
+          }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "<VAST version=\"3.0\"><Ad id=\"20001\" sequence=\"1\"><InLine><AdSystem version=\"4.0\"><![CDATA[iabtechlab]]></AdSystem><AdTitle><![CDATA[Inline Simple Ad]]></AdTitle><Impression><![CDATA[]]></Impression><Creatives><Creative id=\"5480\" sequence=\"1\"><Linear><Duration>00:01:00</Duration><MediaFiles><MediaFile id=\"5246\" delivery=\"progressive\" type=\"video/mp4\" bitrate=\"600\" width=\"640\" height=\"360\" minBitrate=\"500\" maxBitrate=\"700\" scalable=\"1\" maintainAspectRatio=\"1\" codec=\"0\"><![CDATA[https://s0.2mdn.net/4253510/google_ddm_animation_480P.mp4]]></MediaFile></MediaFiles><VideoClicks><ClickThrough id=\"blog\"><![CDATA[https://example.com]]></ClickThrough></VideoClicks></Linear></Creative></Creatives></InLine></Ad></VAST>",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/mobfoxpb/mobfoxpbtest/exemplary/simple-web-banner.json b/adapters/mobfoxpb/mobfoxpbtest/exemplary/simple-web-banner.json
new file mode 100644
index 00000000..bba728ac
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/exemplary/simple-web-banner.json
@@ -0,0 +1,130 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "tagid": "8",
+        "ext": {
+          "bidder": {
+            "TagID": "8"
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "1",
+      "domain": "test.com"
+    },
+    "device": {
+      "ip": "123.123.123.123"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "tagid": "8",
+              "ext": {
+                "bidder": {
+                  "TagID": "8"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "1",
+            "domain": "test.com"
+          },
+          "device": {
+            "ip": "123.123.123.123"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test_bid_id",
+                  "impid": "test-imp-id",
+                  "price": 0.27543,
+                  "adm": "<iframe id=\"adm-banner-1\" width=\"468\" height=\"60\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"https://bes.mobfox.com/?c=o&m=adm&k=bc2d316f39931a07d9a8dd249bf85fc0\"></iframe>",
+                  "cid": "test_cid",
+                  "crid": "test_crid",
+                  "dealid": "test_dealid",
+                  "w": 468,
+                  "h": 60,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }
+              ],
+              "seat": "mobfoxpb"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "test_bid_id",
+            "impid": "test-imp-id",
+            "price": 0.27543,
+            "adm": "<iframe id=\"adm-banner-1\" width=\"468\" height=\"60\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"https://bes.mobfox.com/?c=o&m=adm&k=bc2d316f39931a07d9a8dd249bf85fc0\"></iframe>",
+            "cid": "test_cid",
+            "crid": "test_crid",
+            "dealid": "test_dealid",
+            "w": 468,
+            "h": 60,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/mobfoxpb/mobfoxpbtest/params/race/banner.json b/adapters/mobfoxpb/mobfoxpbtest/params/race/banner.json
new file mode 100644
index 00000000..dbdac1ad
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/params/race/banner.json
@@ -0,0 +1,3 @@
+{
+  "TagID": "6"
+}
\ No newline at end of file
diff --git a/adapters/mobfoxpb/mobfoxpbtest/params/race/video.json b/adapters/mobfoxpb/mobfoxpbtest/params/race/video.json
new file mode 100644
index 00000000..6e2e0b38
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/params/race/video.json
@@ -0,0 +1,3 @@
+{
+  "TagID": "7"
+}
\ No newline at end of file
diff --git a/adapters/mobfoxpb/mobfoxpbtest/supplemental/bad-imp-ext.json b/adapters/mobfoxpb/mobfoxpbtest/supplemental/bad-imp-ext.json
new file mode 100644
index 00000000..ac3dce59
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/supplemental/bad-imp-ext.json
@@ -0,0 +1,42 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "tagid": "6",
+        "ext": {
+          "mobfoxpb": {
+            "TagID": "6"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    }
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Key path not found",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/mobfoxpb/mobfoxpbtest/supplemental/bad_response.json b/adapters/mobfoxpb/mobfoxpbtest/supplemental/bad_response.json
new file mode 100644
index 00000000..2f834c92
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/supplemental/bad_response.json
@@ -0,0 +1,87 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "tagid": "17",
+        "ext": {
+          "bidder": {
+            "TagID": "17"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "tagid": "17",
+              "ext": {
+                "bidder": {
+                  "TagID": "17"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": ""
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "json: cannot unmarshal string into Go value of type openrtb.BidResponse",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/mobfoxpb/mobfoxpbtest/supplemental/bad_status_code.json b/adapters/mobfoxpb/mobfoxpbtest/supplemental/bad_status_code.json
new file mode 100644
index 00000000..96d3a649
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/supplemental/bad_status_code.json
@@ -0,0 +1,81 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "tagid": "100000000",
+        "ext": {
+          "bidder": {
+            "TagID": "100000000"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {}
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "tagid": "100000000",
+              "ext": {
+                "bidder": {
+                  "TagID": "100000000"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {}
+        }
+      },
+      "mockResponse": {
+        "status": 400,
+        "body": {}
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/mobfoxpb/mobfoxpbtest/supplemental/imp_ext_empty_object.json b/adapters/mobfoxpb/mobfoxpbtest/supplemental/imp_ext_empty_object.json
new file mode 100644
index 00000000..cc56fa25
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/supplemental/imp_ext_empty_object.json
@@ -0,0 +1,38 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "tagid": "6",
+        "ext": {}
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    }
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Key path not found",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/mobfoxpb/mobfoxpbtest/supplemental/imp_ext_string.json b/adapters/mobfoxpb/mobfoxpbtest/supplemental/imp_ext_string.json
new file mode 100644
index 00000000..464c9e31
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/supplemental/imp_ext_string.json
@@ -0,0 +1,38 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "tagid": "6",
+        "ext": ""
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
+    }
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Key path not found",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/mobfoxpb/mobfoxpbtest/supplemental/status-204.json b/adapters/mobfoxpb/mobfoxpbtest/supplemental/status-204.json
new file mode 100644
index 00000000..c1091969
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/supplemental/status-204.json
@@ -0,0 +1,82 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "tagid": "17",
+        "ext": {
+          "bidder": {
+            "TagID": "17"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "tagid": "17",
+              "ext": {
+                "bidder": {
+                  "TagID": "17"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+          }
+        }
+      },
+      "expectedBidResponses": [],
+      "mockResponse": {
+        "status": 204,
+        "body": {}
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/mobfoxpb/mobfoxpbtest/supplemental/status-404.json b/adapters/mobfoxpb/mobfoxpbtest/supplemental/status-404.json
new file mode 100644
index 00000000..d9ef7108
--- /dev/null
+++ b/adapters/mobfoxpb/mobfoxpbtest/supplemental/status-404.json
@@ -0,0 +1,87 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "tagid": "100000000",
+        "ext": {
+          "bidder": {
+            "TagID": "100000000"
+          }
+        }
+      }
+    ],
+    "app": {
+      "id": "1",
+      "bundle": "com.wls.testwlsapplication"
+    },
+    "device": {
+      "ip": "123.123.123.123",
+      "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://example.com/?c=o&m=ortb",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "tagid": "100000000",
+              "ext": {
+                "bidder": {
+                  "TagID": "100000000"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+          },
+          "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 404,
+        "body": {}
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 404. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/mobfoxpb/params_test.go b/adapters/mobfoxpb/params_test.go
new file mode 100644
index 00000000..59b9ec38
--- /dev/null
+++ b/adapters/mobfoxpb/params_test.go
@@ -0,0 +1,47 @@
+package mobfoxpb
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// TestValidParams makes sure that the mobfoxpb schema accepts all imp.ext fields which we intend to support.
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderMobfoxpb, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected mobfoxpb params: %s", validParam)
+		}
+	}
+}
+
+// TestInvalidParams makes sure that the mobfoxpb schema rejects all the imp.ext fields we don't support.
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderMobfoxpb, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"TagID": "6"}`,
+}
+
+var invalidParams = []string{
+	`{"id": "123"}`,
+	`{"tagid": "123"}`,
+	`{"TagID": 16}`,
+	`{"TagID": ""}`,
+}
diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go
index b5f0989e..6a237e39 100644
--- a/endpoints/auction_test.go
+++ b/endpoints/auction_test.go
@@ -420,7 +420,7 @@ func (m *auctionMockPermissions) BidderSyncAllowed(ctx context.Context, bidder o
 	return m.allowBidderSync, nil
 }
 
-func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) {
+func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string) (bool, bool, bool, error) {
 	return m.allowPI, m.allowGeo, m.allowID, nil
 }
 
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index ee29125b..c790fcd9 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -254,6 +254,6 @@ func (g *gdprPerms) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.Bi
 	return ok, nil
 }
 
-func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) {
+func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string) (bool, bool, bool, error) {
 	return true, true, true, nil
 }
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 1f2c4040..60285958 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/gdpr"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/usersync"
@@ -437,7 +438,7 @@ func (g *mockPermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	return false, nil
 }
 
-func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) {
+func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string) (bool, bool, bool, error) {
 	return g.allowPI, g.allowPI, g.allowPI, nil
 }
 
diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go
index f0010922..d7059648 100644
--- a/exchange/adapter_util_test.go
+++ b/exchange/adapter_util_test.go
@@ -9,7 +9,6 @@ import (
 	"github.com/mxmCherry/openrtb"
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/adapters/appnexus"
-	"github.com/prebid/prebid-server/adapters/ix"
 	"github.com/prebid/prebid-server/adapters/lifestreet"
 	"github.com/prebid/prebid-server/adapters/pulsepoint"
 	"github.com/prebid/prebid-server/adapters/rubicon"
@@ -29,12 +28,12 @@ var (
 func TestBuildAdaptersSuccess(t *testing.T) {
 	client := &http.Client{}
 	cfg := &config.Configuration{Adapters: map[string]config.Adapter{
-		"appnexus": {},
-		"ix":       {Endpoint: "anyEndpoint"},
+		"appnexus":   {},
+		"lifestreet": {Endpoint: "anyEndpoint"},
 	}}
 	infos := map[string]adapters.BidderInfo{
-		"appnexus": infoActive,
-		"ix":       infoActive,
+		"appnexus":   infoActive,
+		"lifestreet": infoActive,
 	}
 	metricEngine := &metrics.DummyMetricsEngine{}
 
@@ -45,12 +44,12 @@ func TestBuildAdaptersSuccess(t *testing.T) {
 	appnexusBidderAdapted := adaptBidder(appnexusBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderAppnexus)
 	appnexusBidderValidated := addValidatedBidderMiddleware(appnexusBidderAdapted)
 
-	idLegacyAdapted := &adaptedAdapter{ix.NewIxLegacyAdapter(adapters.DefaultHTTPAdapterConfig, "anyEndpoint")}
+	idLegacyAdapted := &adaptedAdapter{lifestreet.NewLifestreetLegacyAdapter(adapters.DefaultHTTPAdapterConfig, "anyEndpoint")}
 	idLegacyValidated := addValidatedBidderMiddleware(idLegacyAdapted)
 
 	expectedBidders := map[openrtb_ext.BidderName]adaptedBidder{
-		openrtb_ext.BidderAppnexus: appnexusBidderValidated,
-		openrtb_ext.BidderIx:       idLegacyValidated,
+		openrtb_ext.BidderAppnexus:   appnexusBidderValidated,
+		openrtb_ext.BidderLifestreet: idLegacyValidated,
 	}
 
 	assert.Equal(t, expectedBidders, bidders)
@@ -215,9 +214,9 @@ func TestBuildBidders(t *testing.T) {
 		},
 		{
 			description:   "Success - Ignores Legacy",
-			adapterConfig: map[string]config.Adapter{"appnexus": {}, "ix": {}, "lifestreet": {}, "pulsepoint": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive, "ix": infoActive, "lifestreet": infoActive, "pulsepoint": infoActive},
-			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder, openrtb_ext.BidderIx: inconsequentialBuilder, openrtb_ext.BidderLifestreet: inconsequentialBuilder, openrtb_ext.BidderPulsepoint: inconsequentialBuilder},
+			adapterConfig: map[string]config.Adapter{"appnexus": {}, "lifestreet": {}, "pulsepoint": {}},
+			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive, "lifestreet": infoActive, "pulsepoint": infoActive},
+			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder, openrtb_ext.BidderLifestreet: inconsequentialBuilder, openrtb_ext.BidderPulsepoint: inconsequentialBuilder},
 			expectedBidders: map[openrtb_ext.BidderName]adapters.Bidder{
 				openrtb_ext.BidderAppnexus: adapters.EnforceBidderInfo(appnexusBidder, infoActive),
 			},
@@ -267,7 +266,6 @@ func TestBuildBidders(t *testing.T) {
 func TestBuildExchangeBiddersLegacy(t *testing.T) {
 	cfg := config.Adapter{Endpoint: "anyEndpoint"}
 
-	expectedIx := &adaptedAdapter{ix.NewIxLegacyAdapter(adapters.DefaultHTTPAdapterConfig, "anyEndpoint")}
 	expectedLifestreet := &adaptedAdapter{lifestreet.NewLifestreetLegacyAdapter(adapters.DefaultHTTPAdapterConfig, "anyEndpoint")}
 	expectedPulsepoint := &adaptedAdapter{pulsepoint.NewPulsePointLegacyAdapter(adapters.DefaultHTTPAdapterConfig, "anyEndpoint")}
 
@@ -279,27 +277,27 @@ func TestBuildExchangeBiddersLegacy(t *testing.T) {
 	}{
 		{
 			description:   "All Active",
-			adapterConfig: map[string]config.Adapter{"ix": cfg, "lifestreet": cfg, "pulsepoint": cfg},
-			bidderInfos:   map[string]adapters.BidderInfo{"ix": infoActive, "lifestreet": infoActive, "pulsepoint": infoActive},
-			expected:      map[openrtb_ext.BidderName]adaptedBidder{"ix": expectedIx, "lifestreet": expectedLifestreet, "pulsepoint": expectedPulsepoint},
+			adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg},
+			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoActive, "pulsepoint": infoActive},
+			expected:      map[openrtb_ext.BidderName]adaptedBidder{"lifestreet": expectedLifestreet, "pulsepoint": expectedPulsepoint},
 		},
 		{
 			description:   "All Disabled",
-			adapterConfig: map[string]config.Adapter{"ix": cfg, "lifestreet": cfg, "pulsepoint": cfg},
-			bidderInfos:   map[string]adapters.BidderInfo{"ix": infoDisabled, "lifestreet": infoDisabled, "pulsepoint": infoDisabled},
+			adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg},
+			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoDisabled, "pulsepoint": infoDisabled},
 			expected:      map[openrtb_ext.BidderName]adaptedBidder{},
 		},
 		{
 			description:   "All Unknown",
-			adapterConfig: map[string]config.Adapter{"ix": cfg, "lifestreet": cfg, "pulsepoint": cfg},
-			bidderInfos:   map[string]adapters.BidderInfo{"ix": infoUnknown, "lifestreet": infoUnknown, "pulsepoint": infoUnknown},
+			adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg},
+			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoUnknown, "pulsepoint": infoUnknown},
 			expected:      map[openrtb_ext.BidderName]adaptedBidder{},
 		},
 		{
 			description:   "Mixed",
-			adapterConfig: map[string]config.Adapter{"ix": cfg, "lifestreet": cfg, "pulsepoint": cfg},
-			bidderInfos:   map[string]adapters.BidderInfo{"ix": infoActive, "lifestreet": infoDisabled, "pulsepoint": infoUnknown},
-			expected:      map[openrtb_ext.BidderName]adaptedBidder{"ix": expectedIx},
+			adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg},
+			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoActive, "pulsepoint": infoUnknown},
+			expected:      map[openrtb_ext.BidderName]adaptedBidder{"lifestreet": expectedLifestreet},
 		},
 	}
 
diff --git a/exchange/gdpr_test.go b/exchange/gdpr_test.go
index 2d8fff86..f7906104 100644
--- a/exchange/gdpr_test.go
+++ b/exchange/gdpr_test.go
@@ -5,40 +5,97 @@ import (
 	"testing"
 
 	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/gdpr"
 	"github.com/stretchr/testify/assert"
 )
 
-func TestExtractGDPRFound(t *testing.T) {
-	gdprTest := openrtb.BidRequest{
-		User: &openrtb.User{
-			Ext: json.RawMessage(`{"consent": "BOS2bx5OS2bx5ABABBAAABoAAAAAFA"}`),
+func TestExtractGDPR(t *testing.T) {
+	tests := []struct {
+		description string
+		giveRegs    *openrtb.Regs
+		wantGDPR    gdpr.Signal
+	}{
+		{
+			description: "Regs Ext GDPR = 0",
+			giveRegs:    &openrtb.Regs{Ext: json.RawMessage(`{"gdpr": 0}`)},
+			wantGDPR:    gdpr.SignalNo,
 		},
-		Regs: &openrtb.Regs{
-			Ext: json.RawMessage(`{"gdpr": 1}`),
+		{
+			description: "Regs Ext GDPR = 1",
+			giveRegs:    &openrtb.Regs{Ext: json.RawMessage(`{"gdpr": 1}`)},
+			wantGDPR:    gdpr.SignalYes,
+		},
+		{
+			description: "Regs Ext GDPR = null",
+			giveRegs:    &openrtb.Regs{Ext: json.RawMessage(`{"gdpr": null}`)},
+			wantGDPR:    gdpr.SignalAmbiguous,
+		},
+		{
+			description: "Regs is nil",
+			giveRegs:    nil,
+			wantGDPR:    gdpr.SignalAmbiguous,
+		},
+		{
+			description: "Regs Ext is nil",
+			giveRegs:    &openrtb.Regs{Ext: nil},
+			wantGDPR:    gdpr.SignalAmbiguous,
+		},
+		{
+			description: "JSON unmarshal error",
+			giveRegs:    &openrtb.Regs{Ext: json.RawMessage(`{"`)},
+			wantGDPR:    gdpr.SignalAmbiguous,
 		},
 	}
-	gdpr := extractGDPR(&gdprTest, false)
-	consent := extractConsent(&gdprTest)
-	assert.Equal(t, 1, gdpr)
-	assert.Equal(t, "BOS2bx5OS2bx5ABABBAAABoAAAAAFA", consent)
-
-	gdprTest.Regs.Ext = json.RawMessage(`{"gdpr": 0}`)
-	gdpr = extractGDPR(&gdprTest, true)
-	consent = extractConsent(&gdprTest)
-	assert.Equal(t, 0, gdpr)
-	assert.Equal(t, "BOS2bx5OS2bx5ABABBAAABoAAAAAFA", consent)
-}
 
-func TestGDPRUnknown(t *testing.T) {
-	gdprTest := openrtb.BidRequest{}
+	for _, tt := range tests {
+		bidReq := openrtb.BidRequest{
+			Regs: tt.giveRegs,
+		}
 
-	gdpr := extractGDPR(&gdprTest, false)
-	consent := extractConsent(&gdprTest)
-	assert.Equal(t, 1, gdpr)
-	assert.Equal(t, "", consent)
+		result := extractGDPR(&bidReq)
+		assert.Equal(t, tt.wantGDPR, result, tt.description)
+	}
+}
 
-	gdpr = extractGDPR(&gdprTest, true)
-	consent = extractConsent(&gdprTest)
-	assert.Equal(t, 0, gdpr)
+func TestExtractConsent(t *testing.T) {
+	tests := []struct {
+		description string
+		giveUser    *openrtb.User
+		wantConsent string
+	}{
+		{
+			description: "User Ext Consent is not empty",
+			giveUser:    &openrtb.User{Ext: json.RawMessage(`{"consent": "BOS2bx5OS2bx5ABABBAAABoAAAAAFA"}`)},
+			wantConsent: "BOS2bx5OS2bx5ABABBAAABoAAAAAFA",
+		},
+		{
+			description: "User Ext Consent is empty",
+			giveUser:    &openrtb.User{Ext: json.RawMessage(`{"consent": ""}`)},
+			wantConsent: "",
+		},
+		{
+			description: "User Ext is nil",
+			giveUser:    &openrtb.User{Ext: nil},
+			wantConsent: "",
+		},
+		{
+			description: "User is nil",
+			giveUser:    nil,
+			wantConsent: "",
+		},
+		{
+			description: "JSON unmarshal error",
+			giveUser:    &openrtb.User{Ext: json.RawMessage(`{`)},
+			wantConsent: "",
+		},
+	}
+
+	for _, tt := range tests {
+		bidReq := openrtb.BidRequest{
+			User: tt.giveUser,
+		}
 
+		result := extractConsent(&bidReq)
+		assert.Equal(t, tt.wantConsent, result, tt.description)
+	}
 }
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 757eb658..f9a04f25 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/mxmCherry/openrtb"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/gdpr"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/stretchr/testify/assert"
@@ -30,7 +31,7 @@ func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	return true, nil
 }
 
-func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) {
+func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdpr gdpr.Signal, consent string) (bool, bool, bool, error) {
 	return p.personalInfoAllowed, p.personalInfoAllowed, p.personalInfoAllowed, nil
 }
 
@@ -1053,6 +1054,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 		gdpr                string
 		gdprConsent         string
 		gdprScrub           bool
+		userSyncIfAmbiguous bool
 		expectPrivacyLabels metrics.PrivacyLabels
 	}{
 		{
@@ -1151,6 +1153,32 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 				GDPRTCFVersion: "",
 			},
 		},
+		{
+			description:         "Enforce - Ambiguous signal, don't sync user if ambiguous",
+			gdprAccountEnabled:  nil,
+			gdprHostEnabled:     true,
+			gdpr:                "null",
+			gdprConsent:         "BONV8oqONXwgmADACHENAO7pqzAAppY",
+			gdprScrub:           true,
+			userSyncIfAmbiguous: false,
+			expectPrivacyLabels: metrics.PrivacyLabels{
+				GDPREnforced:   true,
+				GDPRTCFVersion: metrics.TCFVersionV1,
+			},
+		},
+		{
+			description:         "Not Enforce - Ambiguous signal, sync user if ambiguous",
+			gdprAccountEnabled:  nil,
+			gdprHostEnabled:     true,
+			gdpr:                "null",
+			gdprConsent:         "BONV8oqONXwgmADACHENAO7pqzAAppY",
+			gdprScrub:           false,
+			userSyncIfAmbiguous: true,
+			expectPrivacyLabels: metrics.PrivacyLabels{
+				GDPREnforced:   false,
+				GDPRTCFVersion: "",
+			},
+		},
 	}
 
 	for _, test := range testCases {
@@ -1162,7 +1190,8 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 
 		privacyConfig := config.Privacy{
 			GDPR: config.GDPR{
-				Enabled: test.gdprHostEnabled,
+				Enabled:             test.gdprHostEnabled,
+				UsersyncIfAmbiguous: test.userSyncIfAmbiguous,
 				TCF2: config.TCF2{
 					Enabled: true,
 				},
@@ -1186,7 +1215,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 			auctionReq,
 			nil,
 			&permissionsMock{personalInfoAllowed: !test.gdprScrub},
-			true,
+			test.userSyncIfAmbiguous,
 			privacyConfig)
 		result := results[0]
 
diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go
index 793eb967..a7d4b26a 100644
--- a/gdpr/impl_test.go
+++ b/gdpr/impl_test.go
@@ -177,20 +177,107 @@ func TestMalformedConsent(t *testing.T) {
 }
 
 func TestAllowPersonalInfo(t *testing.T) {
+	bidderAllowedByConsent := openrtb_ext.BidderAppnexus
+	bidderBlockedByConsent := openrtb_ext.BidderRubicon
+	consent := "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA"
+
+	tests := []struct {
+		description         string
+		bidderName          openrtb_ext.BidderName
+		publisherID         string
+		userSyncIfAmbiguous bool
+		gdpr                Signal
+		consent             string
+		allowPI             bool
+	}{
+		{
+			description:         "Allow PI - Non standard publisher",
+			bidderName:          bidderBlockedByConsent,
+			publisherID:         "appNexusAppID",
+			userSyncIfAmbiguous: false,
+			gdpr:                SignalYes,
+			consent:             consent,
+			allowPI:             true,
+		},
+		{
+			description:         "Allow PI - known vendor with No GDPR",
+			bidderName:          bidderBlockedByConsent,
+			userSyncIfAmbiguous: false,
+			gdpr:                SignalNo,
+			consent:             consent,
+			allowPI:             true,
+		},
+		{
+			description:         "Allow PI - known vendor with Yes GDPR",
+			bidderName:          bidderAllowedByConsent,
+			userSyncIfAmbiguous: false,
+			gdpr:                SignalYes,
+			consent:             consent,
+			allowPI:             true,
+		},
+		{
+			description:         "PI allowed according to host setting UserSyncIfAmbiguous true - known vendor with ambiguous GDPR and empty consent",
+			bidderName:          bidderAllowedByConsent,
+			userSyncIfAmbiguous: true,
+			gdpr:                SignalAmbiguous,
+			consent:             "",
+			allowPI:             true,
+		},
+		{
+			description:         "PI allowed according to host setting UserSyncIfAmbiguous true - known vendor with ambiguous GDPR and non-empty consent",
+			bidderName:          bidderAllowedByConsent,
+			userSyncIfAmbiguous: true,
+			gdpr:                SignalAmbiguous,
+			consent:             consent,
+			allowPI:             true,
+		},
+		{
+			description:         "PI allowed according to host setting UserSyncIfAmbiguous false - known vendor with ambiguous GDPR and empty consent",
+			bidderName:          bidderAllowedByConsent,
+			userSyncIfAmbiguous: false,
+			gdpr:                SignalAmbiguous,
+			consent:             "",
+			allowPI:             false,
+		},
+		{
+			description:         "PI allowed according to host setting UserSyncIfAmbiguous false - known vendor with ambiguous GDPR and non-empty consent",
+			bidderName:          bidderAllowedByConsent,
+			userSyncIfAmbiguous: false,
+			gdpr:                SignalAmbiguous,
+			consent:             consent,
+			allowPI:             true,
+		},
+		{
+			description:         "Don't allow PI - known vendor with Yes GDPR and empty consent",
+			bidderName:          bidderAllowedByConsent,
+			userSyncIfAmbiguous: false,
+			gdpr:                SignalYes,
+			consent:             "",
+			allowPI:             false,
+		},
+		{
+			description:         "Don't allow PI - default vendor with Yes GDPR and non-empty consent",
+			bidderName:          bidderBlockedByConsent,
+			userSyncIfAmbiguous: false,
+			gdpr:                SignalYes,
+			consent:             consent,
+			allowPI:             false,
+		},
+	}
+
 	vendorListData := tcf1MarshalVendorList(tcf1VendorList{
 		VendorListVersion: 1,
 		Vendors: []tcf1Vendor{
-			{ID: 2, Purposes: []int{1}},    // cookie reads/writes
-			{ID: 3, Purposes: []int{1, 3}}, // ad personalization
+			{ID: 2, Purposes: []int{1, 3}},
 		},
 	})
 	perms := permissionsImpl{
 		cfg: config.GDPR{
-			HostVendorID: 2,
+			HostVendorID:            2,
+			NonStandardPublisherMap: map[string]struct{}{"appNexusAppID": {}},
 		},
 		vendorIDs: map[openrtb_ext.BidderName]uint16{
 			openrtb_ext.BidderAppnexus: 2,
-			openrtb_ext.BidderPubmatic: 3,
 		},
 		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
 			tcf1SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{
@@ -202,20 +289,14 @@ func TestAllowPersonalInfo(t *testing.T) {
 		},
 	}
 
-	// PI needs both purposes to succeed
-	allowPI, _, _, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA")
-	assertNilErr(t, err)
-	assertBoolsEqual(t, false, allowPI)
+	for _, tt := range tests {
+		perms.cfg.UsersyncIfAmbiguous = tt.userSyncIfAmbiguous
 
-	allowPI, _, _, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderPubmatic, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA")
-	assertNilErr(t, err)
-	assertBoolsEqual(t, true, allowPI)
+		allowPI, _, _, err := perms.PersonalInfoAllowed(context.Background(), tt.bidderName, tt.publisherID, tt.gdpr, tt.consent)
 
-	// Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array
-	perms.cfg.NonStandardPublisherMap = map[string]struct{}{"appNexusAppID": {}}
-	allowPI, _, _, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA")
-	assertNilErr(t, err)
-	assertBoolsEqual(t, true, allowPI)
+		assert.Nil(t, err, tt.description)
+		assert.Equal(t, tt.allowPI, allowPI, tt.description)
+	}
 }
 
 func buildTCF2VendorList34() tcf2VendorList {
@@ -318,7 +399,7 @@ func TestAllowPersonalInfoTCF2(t *testing.T) {
 	}
 
 	for _, td := range testDefs {
-		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent)
+		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent)
 		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
 		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
 		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
@@ -344,7 +425,7 @@ func TestAllowPersonalInfoWhitelistTCF2(t *testing.T) {
 	}
 	// Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array
 	perms.cfg.NonStandardPublisherMap = map[string]struct{}{"appNexusAppID": {}}
-	allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
 	assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed")
 	assert.EqualValuesf(t, true, allowPI, "AllowPI failure")
 	assert.EqualValuesf(t, true, allowGeo, "AllowGeo failure")
@@ -398,7 +479,7 @@ func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) {
 	}
 
 	for _, td := range testDefs {
-		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent)
+		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent)
 		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
 		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
 		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
@@ -454,7 +535,7 @@ func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) {
 	}
 
 	for _, td := range testDefs {
-		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent)
+		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent)
 		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
 		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
 		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
@@ -511,7 +592,7 @@ func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) {
 	}
 
 	for _, td := range testDefs {
-		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent)
+		allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent)
 		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
 		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
 		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index dc4ca0a6..60ab2478 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -101,6 +101,7 @@ func TestNewSyncerMap(t *testing.T) {
 		openrtb_ext.BidderInMobi:       true,
 		openrtb_ext.BidderKidoz:        true,
 		openrtb_ext.BidderKubient:      true,
+		openrtb_ext.BidderMobfoxpb:     true,
 		openrtb_ext.BidderMobileFuse:   true,
 		openrtb_ext.BidderOrbidder:     true,
 		openrtb_ext.BidderPubnative:    true,