Repository: prebid/prebid-server
Pull request #1785 (2021-03-31 10:35:41)

RTB House: update parameters
PR addresses stale / non up to date params requirements by RTB House adapter


Patch:
diff --git a/adapters/bidmachine/bidmachine.go b/adapters/bidmachine/bidmachine.go
new file mode 100644
index 00000000..3c9a5b67
--- /dev/null
+++ b/adapters/bidmachine/bidmachine.go
@@ -0,0 +1,219 @@
+package bidmachine
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/url"
+	"path"
+	"strconv"
+	"text/template"
+
+	"github.com/mxmCherry/openrtb/v14/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	endpoint template.Template
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json")
+	headers.Add("Accept", "application/json")
+	headers.Add("X-Openrtb-Version", "2.5")
+
+	impressions := request.Imp
+	result := make([]*adapters.RequestData, 0, len(impressions))
+	errs := make([]error, 0, len(impressions))
+
+	for _, impression := range impressions {
+		if impression.Banner != nil {
+			banner := impression.Banner
+			if banner.W == nil && banner.H == nil {
+				if banner.Format == nil {
+					errs = append(errs, &errortypes.BadInput{
+						Message: "Impression with id: " + impression.ID + " has following error: Banner width and height is not provided and banner format is missing. At least one is required",
+					})
+					continue
+				}
+				if len(banner.Format) == 0 {
+					errs = append(errs, &errortypes.BadInput{
+						Message: "Impression with id: " + impression.ID + " has following error: Banner width and height is not provided and banner format array is empty. At least one is required",
+					})
+					continue
+				}
+			}
+
+		}
+
+		var bidderExt adapters.ExtImpBidder
+		err := json.Unmarshal(impression.Ext, &bidderExt)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		var impressionExt openrtb_ext.ExtImpBidmachine
+		err = json.Unmarshal(bidderExt.Bidder, &impressionExt)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+		url, err := a.buildEndpointURL(impressionExt)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+		if bidderExt.Prebid != nil && bidderExt.Prebid.IsRewardedInventory == 1 {
+			if impression.Banner != nil && !hasRewardedBattr(impression.Banner.BAttr) {
+				bannerCopy := *impression.Banner
+				bannerCopy.BAttr = copyBAttrWithRewardedInventory(bannerCopy.BAttr)
+				impression.Banner = &bannerCopy
+			}
+			if impression.Video != nil && !hasRewardedBattr(impression.Video.BAttr) {
+				videoCopy := *impression.Video
+				videoCopy.BAttr = copyBAttrWithRewardedInventory(videoCopy.BAttr)
+				impression.Video = &videoCopy
+			}
+		}
+		request.Imp = []openrtb2.Imp{impression}
+		body, err := json.Marshal(request)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+		result = append(result, &adapters.RequestData{
+			Method:  "POST",
+			Uri:     url,
+			Body:    body,
+			Headers: headers,
+		})
+	}
+
+	request.Imp = impressions
+
+	return result, errs
+}
+
+func hasRewardedBattr(attr []openrtb2.CreativeAttribute) bool {
+	for i := 0; i < len(attr); i++ {
+		if attr[i] == openrtb2.CreativeAttribute(16) {
+			return true
+		}
+	}
+	return false
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	var errs []error
+
+	switch responseData.StatusCode {
+	case http.StatusNoContent:
+		return nil, nil
+	case http.StatusServiceUnavailable:
+		fallthrough
+	case http.StatusBadRequest:
+		fallthrough
+	case http.StatusUnauthorized:
+		fallthrough
+	case http.StatusForbidden:
+		return nil, []error{&errortypes.BadInput{
+			Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body),
+		}}
+	case http.StatusOK:
+		break
+	default:
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body),
+		}}
+	}
+
+	var bidResponse openrtb2.BidResponse
+	err := json.Unmarshal(responseData.Body, &bidResponse)
+	if err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: err.Error(),
+		}}
+	}
+
+	response := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+
+	for _, seatBid := range bidResponse.SeatBid {
+		for _, bid := range seatBid.Bid {
+			thisBid := bid
+			bidType := GetMediaTypeForImp(bid.ImpID, request.Imp)
+			if bidType == UndefinedMediaType {
+				errs = append(errs, &errortypes.BadServerResponse{
+					Message: "ignoring bid id=" + bid.ID + ", request doesn't contain any valid impression with id=" + bid.ImpID,
+				})
+				continue
+			}
+			response.Bids = append(response.Bids, &adapters.TypedBid{
+				Bid:     &thisBid,
+				BidType: bidType,
+			})
+		}
+	}
+
+	return response, errs
+}
+
+// Builder builds a new instance of the Bidmachine adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
+	}
+
+	bidder := &adapter{
+		endpoint: *template,
+	}
+
+	return bidder, nil
+}
+
+const UndefinedMediaType = openrtb_ext.BidType("")
+
+func (a *adapter) buildEndpointURL(params openrtb_ext.ExtImpBidmachine) (string, error) {
+	endpointParams := macros.EndpointTemplateParams{Host: params.Host}
+	uriString, errMacros := macros.ResolveMacros(a.endpoint, endpointParams)
+	if errMacros != nil {
+		return "", &errortypes.BadInput{
+			Message: "Failed to resolve host macros",
+		}
+	}
+	uri, errUrl := url.Parse(uriString)
+	if errUrl != nil || uri.Scheme == "" || uri.Host == "" {
+		return "", &errortypes.BadInput{
+			Message: "Failed to create final URL with provided host",
+		}
+	}
+	uri.Path = path.Join(uri.Path, params.Path)
+	uri.Path = path.Join(uri.Path, params.SellerID)
+	return uri.String(), nil
+}
+
+func copyBAttrWithRewardedInventory(src []openrtb2.CreativeAttribute) []openrtb2.CreativeAttribute {
+	dst := make([]openrtb2.CreativeAttribute, len(src))
+	copy(dst, src)
+	dst = append(dst, openrtb2.CreativeAttribute(16))
+	return dst
+}
+
+func GetMediaTypeForImp(impID string, imps []openrtb2.Imp) openrtb_ext.BidType {
+	mediaType := openrtb_ext.BidTypeBanner
+	for _, imp := range imps {
+		if imp.ID == impID {
+			if imp.Banner == nil && imp.Video != nil {
+				mediaType = openrtb_ext.BidTypeVideo
+			}
+			return mediaType
+		}
+	}
+	return UndefinedMediaType
+}
diff --git a/adapters/criteo/criteo.go b/adapters/criteo/criteo.go
new file mode 100644
index 00000000..f045c9f0
--- /dev/null
+++ b/adapters/criteo/criteo.go
@@ -0,0 +1,114 @@
+package criteo
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/mxmCherry/openrtb/v14/openrtb2"
+	"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
+	slotIDGenerator slotIDGenerator
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, extraRequestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	criteoRequest, errs := newCriteoRequest(a.slotIDGenerator, request)
+	if len(errs) > 0 {
+		return nil, errs
+	}
+
+	jsonRequest, err := json.Marshal(criteoRequest)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	rqData := adapters.RequestData{
+		Method:  "POST",
+		Uri:     a.uri,
+		Body:    jsonRequest,
+		Headers: getCriteoRequestHeaders(&criteoRequest),
+	}
+
+	return []*adapters.RequestData{&rqData}, nil
+}
+
+func getCriteoRequestHeaders(criteoRequest *criteoRequest) http.Header {
+	headers := http.Header{}
+
+	// criteoRequest is known not to be nil
+	// If there was an error generating it from newCriteoRequest, the errors will be returned immediately
+	// and this method won't be called
+
+	if criteoRequest.User.CookieID != "" {
+		headers.Add("Cookie", "uid="+criteoRequest.User.CookieID)
+	}
+
+	if criteoRequest.User.IP != "" {
+		headers.Add("X-Forwarded-For", criteoRequest.User.IP)
+	}
+
+	if criteoRequest.User.IPv6 != "" {
+		headers.Add("X-Forwarded-For", criteoRequest.User.IPv6)
+	}
+
+	if criteoRequest.User.UA != "" {
+		headers.Add("User-Agent", criteoRequest.User.UA)
+	}
+
+	return headers
+}
+
+func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if response.StatusCode == http.StatusNoContent {
+		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),
+		}}
+	}
+
+	bidResponse, err := newCriteoResponseFromBytes(response.Body)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	bidderResponse := adapters.NewBidderResponse()
+	bidderResponse.Bids = make([]*adapters.TypedBid, len(bidResponse.Slots))
+
+	for i := 0; i < len(bidResponse.Slots); i++ {
+		bidderResponse.Bids[i] = &adapters.TypedBid{
+			Bid: &openrtb2.Bid{
+				ID:    bidResponse.Slots[i].ID,
+				ImpID: bidResponse.Slots[i].ImpID,
+				Price: bidResponse.Slots[i].CPM,
+				AdM:   bidResponse.Slots[i].Creative,
+				W:     bidResponse.Slots[i].Width,
+				H:     bidResponse.Slots[i].Height,
+				CrID:  bidResponse.Slots[i].CreativeID,
+			},
+			BidType: openrtb_ext.BidTypeBanner,
+		}
+	}
+
+	return bidderResponse, nil
+}
+
+// Builder builds a new instance of the Criteo adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	return builderWithGuidGenerator(bidderName, config, newRandomSlotIDGenerator())
+}
+
+func builderWithGuidGenerator(bidderName openrtb_ext.BidderName, config config.Adapter, slotIDGenerator slotIDGenerator) (adapters.Bidder, error) {
+	return &adapter{
+		uri:             config.Endpoint,
+		slotIDGenerator: slotIDGenerator,
+	}, nil
+}
diff --git a/adapters/criteo/generators.go b/adapters/criteo/generators.go
new file mode 100644
index 00000000..0ae0ceca
--- /dev/null
+++ b/adapters/criteo/generators.go
@@ -0,0 +1,36 @@
+package criteo
+
+import "github.com/gofrs/uuid"
+
+type slotIDGenerator interface {
+	NewSlotID() (string, error)
+}
+
+type randomSlotIDGenerator struct{}
+
+func newRandomSlotIDGenerator() randomSlotIDGenerator {
+	return randomSlotIDGenerator{}
+}
+
+func (g randomSlotIDGenerator) NewSlotID() (string, error) {
+	guid, err := uuid.NewV4()
+	if err != nil {
+		return "", err
+	}
+
+	return guid.String(), nil
+}
+
+type fakeSlotIDGenerator struct {
+	fakeSlotID string
+}
+
+func newFakeGuidGenerator(fakeSlotID string) fakeSlotIDGenerator {
+	return fakeSlotIDGenerator{
+		fakeSlotID,
+	}
+}
+
+func (f fakeSlotIDGenerator) NewSlotID() (string, error) {
+	return f.fakeSlotID, nil
+}
diff --git a/adapters/criteo/models.go b/adapters/criteo/models.go
new file mode 100644
index 00000000..64f6081d
--- /dev/null
+++ b/adapters/criteo/models.go
@@ -0,0 +1,299 @@
+package criteo
+
+import (
+	"encoding/json"
+	"fmt"
+	"strings"
+
+	"github.com/mxmCherry/openrtb/v14/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type criteoRequest struct {
+	ID          string                   `json:"id,omitempty"`
+	Publisher   criteoPublisher          `json:"publisher,omitempty"`
+	User        criteoUser               `json:"user,omitempty"`
+	GdprConsent criteoGdprConsent        `json:"gdprconsent,omitempty"`
+	Slots       []criteoRequestSlot      `json:"slots,omitempty"`
+	Eids        []openrtb_ext.ExtUserEid `json:"eids,omitempty"`
+}
+
+func newCriteoRequest(slotIDGenerator slotIDGenerator, request *openrtb2.BidRequest) (criteoRequest, []error) {
+	var errs []error
+
+	// request cannot be nil by design
+
+	criteoRequest := criteoRequest{}
+
+	criteoRequest.ID = request.ID
+
+	// Extracting request slots
+	if len(request.Imp) > 0 {
+		criteoSlots, slotsErr := newCriteoRequestSlots(slotIDGenerator, request.Imp)
+		if len(slotsErr) > 0 {
+			return criteoRequest, slotsErr
+		}
+		criteoRequest.Slots = criteoSlots
+	}
+
+	var networkId *int64
+	for _, criteoSlot := range criteoRequest.Slots {
+		if networkId == nil && criteoSlot.NetworkID != nil && *criteoSlot.NetworkID > 0 {
+			networkId = criteoSlot.NetworkID
+		} else if networkId != nil && criteoSlot.NetworkID != nil && *criteoSlot.NetworkID != *networkId {
+			return criteoRequest, []error{&errortypes.BadInput{
+				Message: "Bid request has slots coming with several network IDs which is not allowed",
+			}}
+		}
+	}
+
+	criteoRequest.Publisher = newCriteoPublisher(networkId, request.App, request.Site)
+
+	var regsExt *openrtb_ext.ExtRegs
+	if request.Regs != nil && request.Regs.Ext != nil {
+		if err := json.Unmarshal(request.Regs.Ext, &regsExt); err != nil {
+			errs = append(errs, err)
+		}
+	}
+
+	criteoRequest.User = newCriteoUser(request.User, request.Device, regsExt)
+
+	if gdprConsent, err := newCriteoGdprConsent(request.User, regsExt); err != nil {
+		errs = append(errs, err)
+	} else {
+		criteoRequest.GdprConsent = gdprConsent
+	}
+
+	if request.User != nil && request.User.Ext != nil {
+		var extUser openrtb_ext.ExtUser
+		if err := json.Unmarshal(request.User.Ext, &extUser); err != nil {
+			errs = append(errs, err)
+		} else {
+			criteoRequest.Eids = extUser.Eids
+		}
+	}
+
+	return criteoRequest, errs
+}
+
+type criteoPublisher struct {
+	SiteID    string `json:"siteid,omitempty"`
+	BundleID  string `json:"bundleid,omitempty"`
+	URL       string `json:"url,omitempty"`
+	NetworkID *int64 `json:"networkid,omitempty"`
+}
+
+func newCriteoPublisher(networkId *int64, app *openrtb2.App, site *openrtb2.Site) criteoPublisher {
+	// Both app and site cannot be nil at the same time by design in PBS
+
+	criteoPublisher := criteoPublisher{}
+
+	if networkId != nil && *networkId > 0 {
+		criteoPublisher.NetworkID = networkId
+	}
+
+	if app != nil {
+		criteoPublisher.BundleID = app.Bundle
+	}
+
+	if site != nil {
+		criteoPublisher.SiteID = site.ID
+		criteoPublisher.URL = site.Page
+	}
+
+	return criteoPublisher
+}
+
+type criteoUser struct {
+	DeviceID     string `json:"deviceid,omitempty"`
+	DeviceOS     string `json:"deviceos,omitempty"`
+	DeviceIDType string `json:"deviceidtype,omitempty"`
+	CookieID     string `json:"cookieuid,omitempty"`
+	UID          string `json:"uid,omitempty"`
+	IP           string `json:"ip,omitempty"`
+	IPv6         string `json:"ipv6,omitempty"`
+	UA           string `json:"ua,omitempty"`
+	UspIab       string `json:"uspIab,omitempty"`
+}
+
+func newCriteoUser(user *openrtb2.User, device *openrtb2.Device, regsExt *openrtb_ext.ExtRegs) criteoUser {
+	criteoUser := criteoUser{}
+
+	if user == nil && device == nil {
+		return criteoUser
+	}
+
+	if user != nil {
+		criteoUser.CookieID = user.BuyerUID
+	}
+
+	if device != nil {
+		deviceType := getDeviceType(device.OS)
+		criteoUser.DeviceIDType = deviceType
+
+		criteoUser.DeviceOS = device.OS
+		criteoUser.DeviceID = device.IFA
+		criteoUser.IP = device.IP
+		criteoUser.IPv6 = device.IPv6
+		criteoUser.UA = device.UA
+	}
+
+	if regsExt != nil {
+		criteoUser.UspIab = regsExt.USPrivacy // CCPA
+	}
+
+	return criteoUser
+}
+
+type criteoGdprConsent struct {
+	GdprApplies *bool  `json:"gdprapplies,omitempty"`
+	ConsentData string `json:"consentdata,omitempty"`
+}
+
+func newCriteoGdprConsent(user *openrtb2.User, regsExt *openrtb_ext.ExtRegs) (criteoGdprConsent, error) {
+	consent := criteoGdprConsent{}
+
+	if user == nil && regsExt == nil {
+		return consent, nil
+	}
+
+	if user != nil && user.Ext != nil {
+		var userExt *openrtb_ext.ExtUser
+		if err := json.Unmarshal(user.Ext, &userExt); err != nil {
+			return consent, err
+		}
+		consent.ConsentData = userExt.Consent
+	}
+
+	if regsExt != nil {
+		if regsExt.GDPR != nil {
+			gdprApplies := bool((*regsExt.GDPR & 1) == 1)
+			consent.GdprApplies = &gdprApplies
+		}
+	}
+
+	return consent, nil
+}
+
+type criteoRequestSlot struct {
+	SlotID    string              `json:"slotid,omitempty"`
+	ImpID     string              `json:"impid,omitempty"`
+	ZoneID    *int64              `json:"zoneid,omitempty"`
+	NetworkID *int64              `json:"networkid,omitempty"`
+	Sizes     []criteoRequestSize `json:"sizes,omitempty"`
+}
+
+func newCriteoRequestSlots(slotIDGenerator slotIDGenerator, impressions []openrtb2.Imp) ([]criteoRequestSlot, []error) {
+	var errs []error
+
+	// `impressions` known not to be nil or empty by design, PBS checks it upstream.
+
+	// Criteo slot should comes with any of (both are ok as well):
+	//   - `zoneid`
+	//   - `networkid`, `slotid`, `sizes`
+	//
+	// if not, criteo will reject the slot.
+
+	var criteoSlots = make([]criteoRequestSlot, len(impressions))
+
+	for i := 0; i < len(impressions); i++ {
+		criteoSlots[i] = criteoRequestSlot{}
+
+		criteoSlots[i].ImpID = impressions[i].ID
+
+		// Generating a random slot ID
+		generatedSlotID, err := slotIDGenerator.NewSlotID()
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+		criteoSlots[i].SlotID = generatedSlotID
+
+		if impressions[i].Banner != nil {
+			if impressions[i].Banner.Format != nil {
+				criteoSlots[i].Sizes = make([]criteoRequestSize, len(impressions[i].Banner.Format))
+				for idx, format := range impressions[i].Banner.Format {
+					criteoSlots[i].Sizes[idx] = newCriteoRequestSize(format.W, format.H)
+				}
+			} else if impressions[i].Banner.W != nil && *impressions[i].Banner.W > 0 && impressions[i].Banner.H != nil && *impressions[i].Banner.H > 0 {
+				criteoSlots[i].Sizes = make([]criteoRequestSize, 1)
+				criteoSlots[i].Sizes[0] = newCriteoRequestSize(*impressions[i].Banner.W, *impressions[i].Banner.H)
+			}
+		}
+
+		var bidderExt adapters.ExtImpBidder
+		if err := json.Unmarshal(impressions[i].Ext, &bidderExt); err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		if bidderExt.Bidder != nil {
+			var criteoExt openrtb_ext.ExtImpCriteo
+			if err := json.Unmarshal(bidderExt.Bidder, &criteoExt); err != nil {
+				errs = append(errs, err)
+				continue
+			}
+			if criteoExt.ZoneID > 0 {
+				criteoSlots[i].ZoneID = &criteoExt.ZoneID
+			}
+			if criteoExt.NetworkID > 0 {
+				criteoSlots[i].NetworkID = &criteoExt.NetworkID
+			}
+		}
+	}
+
+	return criteoSlots, errs
+}
+
+type criteoRequestSize = string
+
+func newCriteoRequestSize(width int64, height int64) criteoRequestSize {
+	return fmt.Sprintf("%dx%d", width, height)
+}
+
+var deviceType = map[string]string{
+	"ios":     "idfa",
+	"android": "gaid",
+	"unknown": "unknown",
+}
+
+func getDeviceType(os string) string {
+	if os != "" {
+		if dtype, ok := deviceType[strings.ToLower(os)]; ok {
+			return dtype
+		}
+	}
+
+	return deviceType["unknown"]
+}
+
+type criteoResponse struct {
+	ID    string               `json:"id,omitempty"`
+	Slots []criteoResponseSlot `json:"slots,omitempty"`
+}
+
+func newCriteoResponseFromBytes(bytes []byte) (criteoResponse, error) {
+	var err error
+	var bidResponse criteoResponse
+
+	if err = json.Unmarshal(bytes, &bidResponse); err != nil {
+		return bidResponse, err
+	}
+
+	return bidResponse, nil
+}
+
+type criteoResponseSlot struct {
+	ID         string  `json:"id,omitempty"`
+	ImpID      string  `json:"impid,omitempty"`
+	ZoneID     int64   `json:"zoneid,omitempty"`
+	NetworkID  int64   `json:"networkid,omitempty"`
+	CPM        float64 `json:"cpm,omitempty"`
+	Currency   string  `json:"currency,omitempty"`
+	Width      int64   `json:"width,omitempty"`
+	Height     int64   `json:"height,omitempty"`
+	Creative   string  `json:"creative,omitempty"`
+	CreativeID string  `json:"creativeid,omitempty"`
+}
diff --git a/adapters/criteo/usersync.go b/adapters/criteo/usersync.go
new file mode 100644
index 00000000..3deba085
--- /dev/null
+++ b/adapters/criteo/usersync.go
@@ -0,0 +1,14 @@
+package criteo
+
+import (
+	"text/template"
+
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/usersync"
+)
+
+// NewCriteoSyncer user syncer for Criteo
+// Criteo doesn't need user synchronization yet.
+func NewCriteoSyncer(temp *template.Template) usersync.Usersyncer {
+	return adapters.NewSyncer("criteo", temp, adapters.SyncTypeRedirect)
+}
diff --git a/adapters/dmx/dmx.go b/adapters/dmx/dmx.go
index 79e1d751..280c7187 100644
--- a/adapters/dmx/dmx.go
+++ b/adapters/dmx/dmx.go
@@ -48,6 +48,8 @@ type dmxParams struct {
 	Bidfloor    float64 `json:"bidfloor,omitempty"`
 }
 
+var protocols = []openrtb2.Protocol{2, 3, 5, 6, 7, 8}
+
 func UserSellerOrPubId(str1, str2 string) string {
 	if str1 != "" {
 		return str1
@@ -100,6 +102,12 @@ func (adapter *DmxAdapter) MakeRequests(request *openrtb2.BidRequest, req *adapt
 		if dmxReq.App.ID != "" {
 			anyHasId = true
 		}
+		if anyHasId == false {
+			if idfa, valid := getIdfa(request); valid {
+				dmxReq.App.ID = idfa
+				anyHasId = true
+			}
+		}
 	} else {
 		dmxReq.App = nil
 	}
@@ -147,13 +155,7 @@ func (adapter *DmxAdapter) MakeRequests(request *openrtb2.BidRequest, req *adapt
 		}
 	}
 
-	if anyHasId == false {
-		return nil, []error{errors.New("This request contained no identifier")}
-	}
-
 	for _, inst := range dmxReq.Imp {
-		var banner *openrtb2.Banner
-		var video *openrtb2.Video
 		var ins openrtb2.Imp
 		var params dmxExt
 		const intVal int8 = 1
@@ -164,9 +166,9 @@ func (adapter *DmxAdapter) MakeRequests(request *openrtb2.BidRequest, req *adapt
 		if isDmxParams(params.Bidder) {
 			if inst.Banner != nil {
 				if len(inst.Banner.Format) != 0 {
-					banner = inst.Banner
+					bannerCopy := *inst.Banner
 					if params.Bidder.PublisherId != "" || params.Bidder.MemberId != "" {
-						imps = fetchParams(params, inst, ins, imps, banner, nil, intVal)
+						imps = fetchParams(params, inst, ins, imps, &bannerCopy, nil, intVal)
 					} else {
 						return nil, []error{errors.New("Missing Params for auction to be send")}
 					}
@@ -174,9 +176,9 @@ func (adapter *DmxAdapter) MakeRequests(request *openrtb2.BidRequest, req *adapt
 			}
 
 			if inst.Video != nil {
-				video = inst.Video
+				videoCopy := *inst.Video
 				if params.Bidder.PublisherId != "" || params.Bidder.MemberId != "" {
-					imps = fetchParams(params, inst, ins, imps, nil, video, intVal)
+					imps = fetchParams(params, inst, ins, imps, nil, &videoCopy, intVal)
 				} else {
 					return nil, []error{errors.New("Missing Params for auction to be send")}
 				}
@@ -187,6 +189,10 @@ func (adapter *DmxAdapter) MakeRequests(request *openrtb2.BidRequest, req *adapt
 
 	dmxReq.Imp = imps
 
+	if anyHasId == false {
+		return nil, []error{errors.New("This request contained no identifier")}
+	}
+
 	oJson, err := json.Marshal(dmxReq)
 
 	if err != nil {
@@ -271,10 +277,15 @@ func fetchParams(params dmxExt, inst openrtb2.Imp, ins openrtb2.Imp, imps []open
 		tempimp.Secure = &intVal
 	}
 	if banner != nil {
+		if banner.H == nil || banner.W == nil {
+			banner.H = &banner.Format[0].H
+			banner.W = &banner.Format[0].W
+		}
 		tempimp.Banner = banner
 	}
 
 	if video != nil {
+		video.Protocols = checkProtocols(video)
 		tempimp.Video = video
 	}
 
@@ -327,3 +338,22 @@ func isDmxParams(t interface{}) bool {
 		return false
 	}
 }
+
+func getIdfa(request *openrtb2.BidRequest) (string, bool) {
+	if request.Device == nil {
+		return "", false
+	}
+
+	device := request.Device
+
+	if device.IFA != "" {
+		return device.IFA, true
+	}
+	return "", false
+}
+func checkProtocols(imp *openrtb2.Video) []openrtb2.Protocol {
+	if len(imp.Protocols) > 0 {
+		return imp.Protocols
+	}
+	return protocols
+}
diff --git a/config/config.go b/config/config.go
index bc15a947..00aceba9 100644
--- a/config/config.go
+++ b/config/config.go
@@ -829,12 +829,14 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.beachfront.extra_info", "{\"video_endpoint\":\"https://reachms.bfmio.com/bid.json?exchange_id\"}")
 	v.SetDefault("adapters.beintoo.endpoint", "https://ib.beintoo.com/um")
 	v.SetDefault("adapters.between.endpoint", "http://{{.Host}}.betweendigital.com/openrtb_bid?sspId={{.PublisherID}}")
+	v.SetDefault("adapters.bidmachine.endpoint", "https://{{.Host}}.bidmachine.io")
 	v.SetDefault("adapters.brightroll.endpoint", "http://east-bid.ybp.yahoo.com/bid/appnexuspbs")
 	v.SetDefault("adapters.colossus.endpoint", "http://colossusssp.com/?c=o&m=rtb")
 	v.SetDefault("adapters.connectad.endpoint", "http://bidder.connectad.io/API?src=pbs")
 	v.SetDefault("adapters.consumable.endpoint", "https://e.serverbid.com/api/v2")
 	v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/cvx/server/hb/ortb/25")
 	v.SetDefault("adapters.cpmstar.endpoint", "https://server.cpmstar.com/openrtbbidrq.aspx")
+	v.SetDefault("adapters.criteo.endpoint", "https://bidder.criteo.com/cdb?profileId=230")
 	v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}")
 	v.SetDefault("adapters.decenterads.endpoint", "http://supply.decenterads.com/?c=o&m=rtb")
 	v.SetDefault("adapters.deepintent.endpoint", "https://prebid.deepintent.com/prebid")
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 46c96706..0ea28f8a 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -29,12 +29,14 @@ import (
 	"github.com/prebid/prebid-server/adapters/beachfront"
 	"github.com/prebid/prebid-server/adapters/beintoo"
 	"github.com/prebid/prebid-server/adapters/between"
+	"github.com/prebid/prebid-server/adapters/bidmachine"
 	"github.com/prebid/prebid-server/adapters/brightroll"
 	"github.com/prebid/prebid-server/adapters/colossus"
 	"github.com/prebid/prebid-server/adapters/connectad"
 	"github.com/prebid/prebid-server/adapters/consumable"
 	"github.com/prebid/prebid-server/adapters/conversant"
 	"github.com/prebid/prebid-server/adapters/cpmstar"
+	"github.com/prebid/prebid-server/adapters/criteo"
 	"github.com/prebid/prebid-server/adapters/datablocks"
 	"github.com/prebid/prebid-server/adapters/decenterads"
 	"github.com/prebid/prebid-server/adapters/deepintent"
@@ -137,12 +139,14 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderBeachfront:       beachfront.Builder,
 		openrtb_ext.BidderBeintoo:          beintoo.Builder,
 		openrtb_ext.BidderBetween:          between.Builder,
+		openrtb_ext.BidderBidmachine:       bidmachine.Builder,
 		openrtb_ext.BidderBrightroll:       brightroll.Builder,
 		openrtb_ext.BidderColossus:         colossus.Builder,
 		openrtb_ext.BidderConnectAd:        connectad.Builder,
 		openrtb_ext.BidderConsumable:       consumable.Builder,
 		openrtb_ext.BidderConversant:       conversant.Builder,
 		openrtb_ext.BidderCpmstar:          cpmstar.Builder,
+		openrtb_ext.BidderCriteo:           criteo.Builder,
 		openrtb_ext.BidderDatablocks:       datablocks.Builder,
 		openrtb_ext.BidderDecenterAds:      decenterads.Builder,
 		openrtb_ext.BidderDeepintent:       deepintent.Builder,
diff --git a/exchange/bidder.go b/exchange/bidder.go
index d6b8028c..9707ddcb 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -325,25 +325,30 @@ func getAssetByID(id int64, assets []nativeRequests.Asset) (nativeRequests.Asset
 	return nativeRequests.Asset{}, fmt.Errorf("Unable to find asset with ID:%d in the request", id)
 }
 
+var authorizationHeader = http.CanonicalHeaderKey("authorization")
+
+func filterHeader(h http.Header) http.Header {
+	clone := h.Clone()
+	clone.Del(authorizationHeader)
+	return clone
+}
+
 // makeExt transforms information about the HTTP call into the contract class for the PBS response.
 func makeExt(httpInfo *httpCallInfo) *openrtb_ext.ExtHttpCall {
-	if httpInfo.err == nil {
-		return &openrtb_ext.ExtHttpCall{
-			Uri:            httpInfo.request.Uri,
-			RequestBody:    string(httpInfo.request.Body),
-			ResponseBody:   string(httpInfo.response.Body),
-			Status:         httpInfo.response.StatusCode,
-			RequestHeaders: httpInfo.request.Headers,
-		}
-	} else if httpInfo.request == nil {
-		return &openrtb_ext.ExtHttpCall{}
-	} else {
-		return &openrtb_ext.ExtHttpCall{
-			Uri:            httpInfo.request.Uri,
-			RequestBody:    string(httpInfo.request.Body),
-			RequestHeaders: httpInfo.request.Headers,
+	ext := &openrtb_ext.ExtHttpCall{}
+
+	if httpInfo != nil && httpInfo.request != nil {
+		ext.Uri = httpInfo.request.Uri
+		ext.RequestBody = string(httpInfo.request.Body)
+		ext.RequestHeaders = filterHeader(httpInfo.request.Headers)
+
+		if httpInfo.err == nil && httpInfo.response != nil {
+			ext.ResponseBody = string(httpInfo.response.Body)
+			ext.Status = httpInfo.response.StatusCode
 		}
 	}
+
+	return ext
 }
 
 // doRequest makes a request, handles the response, and returns the data needed by the
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index e03c234b..4d95babc 100755
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -100,12 +100,14 @@ const (
 	BidderBeachfront       BidderName = "beachfront"
 	BidderBeintoo          BidderName = "beintoo"
 	BidderBetween          BidderName = "between"
+	BidderBidmachine       BidderName = "bidmachine"
 	BidderBrightroll       BidderName = "brightroll"
 	BidderColossus         BidderName = "colossus"
 	BidderConnectAd        BidderName = "connectad"
 	BidderConsumable       BidderName = "consumable"
 	BidderConversant       BidderName = "conversant"
 	BidderCpmstar          BidderName = "cpmstar"
+	BidderCriteo           BidderName = "criteo"
 	BidderDatablocks       BidderName = "datablocks"
 	BidderDmx              BidderName = "dmx"
 	BidderDecenterAds      BidderName = "decenterads"
@@ -208,12 +210,14 @@ func CoreBidderNames() []BidderName {
 		BidderBeachfront,
 		BidderBeintoo,
 		BidderBetween,
+		BidderBidmachine,
 		BidderBrightroll,
 		BidderColossus,
 		BidderConnectAd,
 		BidderConsumable,
 		BidderConversant,
 		BidderCpmstar,
+		BidderCriteo,
 		BidderDatablocks,
 		BidderDecenterAds,
 		BidderDeepintent,
diff --git a/openrtb_ext/imp_bidmachine.go b/openrtb_ext/imp_bidmachine.go
new file mode 100644
index 00000000..20491d25
--- /dev/null
+++ b/openrtb_ext/imp_bidmachine.go
@@ -0,0 +1,7 @@
+package openrtb_ext
+
+type ExtImpBidmachine struct {
+	Host     string `json:"host"`
+	Path     string `json:"path"`
+	SellerID string `json:"seller_id"`
+}
diff --git a/openrtb_ext/imp_criteo.go b/openrtb_ext/imp_criteo.go
new file mode 100644
index 00000000..e200aace
--- /dev/null
+++ b/openrtb_ext/imp_criteo.go
@@ -0,0 +1,7 @@
+package openrtb_ext
+
+// ExtImpCriteo defines the contract for bidrequest.imp[i].ext.criteo
+type ExtImpCriteo struct {
+	ZoneID    int64 `json:"zoneId"`
+	NetworkID int64 `json:"networkId"`
+}
diff --git a/static/bidder-info/bidmachine.yaml b/static/bidder-info/bidmachine.yaml
new file mode 100644
index 00000000..6868125b
--- /dev/null
+++ b/static/bidder-info/bidmachine.yaml
@@ -0,0 +1,8 @@
+maintainer:
+  email: hi@bidmachine.io
+gvlVendorID: 736
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
\ No newline at end of file
diff --git a/static/bidder-info/criteo.yaml b/static/bidder-info/criteo.yaml
new file mode 100644
index 00000000..b27e1fae
--- /dev/null
+++ b/static/bidder-info/criteo.yaml
@@ -0,0 +1,10 @@
+maintainer:
+  email: "pi-direct@criteo.com"
+gvlVendorID: 91
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+  site:
+    mediaTypes:
+      - banner
\ No newline at end of file
diff --git a/static/bidder-params/bidmachine.json b/static/bidder-params/bidmachine.json
new file mode 100644
index 00000000..f3971c00
--- /dev/null
+++ b/static/bidder-params/bidmachine.json
@@ -0,0 +1,31 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "Bidmachine Adapter Params",
+    "description": "A schema which validates params accepted by the Kidoz adapter",
+    "type": "object",
+    "properties": {
+        "host": {
+            "$ref": "#/definitions/non-empty-string",
+            "description": "Host"
+        },
+        "path": {
+            "$ref": "#/definitions/non-empty-string",
+            "description": "URL path"
+        },
+        "seller_id": {
+            "$ref": "#/definitions/non-empty-string",
+            "description": "Seller Identifier"
+        }
+    },
+    "definitions": {
+        "non-empty-string": {
+            "type": "string",
+            "minLength": 1
+        }
+    },
+    "required": [
+        "host",
+        "path",
+        "seller_id"
+    ]
+}
\ No newline at end of file
diff --git a/static/bidder-params/criteo.json b/static/bidder-params/criteo.json
new file mode 100644
index 00000000..9d348a7e
--- /dev/null
+++ b/static/bidder-params/criteo.json
@@ -0,0 +1,30 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "Criteo adapter params",
+    "description": "The schema to validate Criteo specific params accepted by Criteo adapter",
+    "type": "object",
+    "properties": {
+        "zoneid": {
+            "type": "number",
+            "description": "Impression's zone ID.",
+            "minimum": 0
+        },
+        "networkid": {
+            "type": "number",
+            "description": "Impression's network ID.",
+            "minimum": 0
+        }
+    },
+    "anyOf": [
+        {
+            "required": [
+                "zoneid"
+            ]
+        },
+        {
+            "required": [
+                "networkid"
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/static/bidder-params/rtbhouse.json b/static/bidder-params/rtbhouse.json
index e8f6bd03..00732bed 100644
--- a/static/bidder-params/rtbhouse.json
+++ b/static/bidder-params/rtbhouse.json
@@ -1,8 +1,13 @@
 {
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "title": "RTB House Adapter Params",
-  "description": "A schema which validates params accepted by the RTB House adapter",
-  "type": "object",
-  "properties": {},
-  "required": []
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "RTB House Adapter Params",
+    "description": "A schema which validates params accepted by the RTB House adapter",
+    "type": "object",
+    "properties": {
+        "publisherId": {
+            "type": "string",
+            "description": "The publisher’s ID provided by RTB House"
+        }
+    },
+    "required": ["publisherId"]
 }
diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go
index 7467eb26..f01e4e4b 100644
--- a/usersync/usersyncers/syncer.go
+++ b/usersync/usersyncers/syncer.go
@@ -32,6 +32,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/consumable"
 	"github.com/prebid/prebid-server/adapters/conversant"
 	"github.com/prebid/prebid-server/adapters/cpmstar"
+	"github.com/prebid/prebid-server/adapters/criteo"
 	"github.com/prebid/prebid-server/adapters/datablocks"
 	"github.com/prebid/prebid-server/adapters/deepintent"
 	"github.com/prebid/prebid-server/adapters/dmx"
@@ -122,6 +123,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderConnectAd, connectad.NewConnectAdSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderConsumable, consumable.NewConsumableSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderConversant, conversant.NewConversantSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderCriteo, criteo.NewCriteoSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderCpmstar, cpmstar.NewCpmstarSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderDatablocks, datablocks.NewDatablocksSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderDeepintent, deepintent.NewDeepintentSyncer)


Test patch:
diff --git a/adapters/bidmachine/bidmachine_test.go b/adapters/bidmachine/bidmachine_test.go
new file mode 100644
index 00000000..9dc82150
--- /dev/null
+++ b/adapters/bidmachine/bidmachine_test.go
@@ -0,0 +1,28 @@
+package bidmachine
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderBidmachine, config.Adapter{
+		Endpoint: "https://{{.Host}}.bidmachine.io"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "bidmachinetest", bidder)
+}
+
+func TestEndpointTemplateMalformed(t *testing.T) {
+	_, buildErr := Builder(openrtb_ext.BidderBidmachine, config.Adapter{
+		Endpoint: "{{Malformed}}"})
+
+	assert.Error(t, buildErr)
+}
diff --git a/adapters/bidmachine/bidmachinetest/exemplary/rewarded_interstitial_no_battr.json b/adapters/bidmachine/bidmachinetest/exemplary/rewarded_interstitial_no_battr.json
new file mode 100644
index 00000000..96bf06be
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/exemplary/rewarded_interstitial_no_battr.json
@@ -0,0 +1,128 @@
+{
+    "mockBidRequest":
+    {
+        "id": "test-request-id",
+        "imp": [
+        {
+            "id": "test-imp-id",
+            "instl": 1,
+            "banner":
+            {
+                "battr": [1],
+                "mimes": [
+                    "image/jpeg",
+                    "image/jpg",
+                    "image/gif",
+                    "image/png"
+                ],
+                "w": 320,
+                "h": 480
+            },
+            "ext":
+            {
+                "prebid":
+                {
+                    "is_rewarded_inventory": 1
+                },
+                "bidder":
+                {
+                    "host": "api-us",
+                    "path": "auction/rtb/v2",
+                    "seller_id": "1"
+                }
+            }
+        }]
+    },
+    "httpCalls": [
+    {
+        "expectedRequest":
+        {
+            "uri": "https://api-us.bidmachine.io/auction/rtb/v2/1",
+            "headers":
+            {
+                "Content-Type": [
+                    "application/json"
+                ],
+                "Accept": [
+                    "application/json"
+                ],
+                "X-Openrtb-Version": [
+                    "2.5"
+                ]
+            },
+            "body":
+            {
+                "id": "test-request-id",
+                "imp": [
+                {
+                    "id": "test-imp-id",
+                    "instl": 1,
+                    "banner":
+                    {
+                        "battr": [1, 16],
+                        "mimes": [
+                            "image/jpeg",
+                            "image/jpg",
+                            "image/gif",
+                            "image/png"
+                        ],
+                        "w": 320,
+                        "h": 480
+                    },
+                    "ext":
+                    {
+                        "prebid":
+                        {
+                            "is_rewarded_inventory": 1
+                        },
+                        "bidder":
+                        {
+                            "host": "api-us",
+                            "path": "auction/rtb/v2",
+                            "seller_id": "1"
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse":
+        {
+            "status": 200,
+            "body":
+            {
+                "id": "test-request-id",
+                "seatbid": [
+                {
+                    "bid": [
+                    {
+                        "id": "test-bid-id",
+                        "impid": "test-imp-id",
+                        "price": 100,
+                        "adm": "test-adm",
+                        "cid": "test-cid",
+                        "crid": "test-crid"
+                    }],
+                    "seat": "bidmachine"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [
+    {
+        "currency": "USD",
+        "bids": [
+        {
+            "bid":
+            {
+                "id": "test-bid-id",
+                "impid": "test-imp-id",
+                "price": 100,
+                "adm": "test-adm",
+                "cid": "test-cid",
+                "crid": "test-crid"
+            },
+            "type": "banner"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/exemplary/rewarded_interstitial_w_battr.json b/adapters/bidmachine/bidmachinetest/exemplary/rewarded_interstitial_w_battr.json
new file mode 100644
index 00000000..08b187e5
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/exemplary/rewarded_interstitial_w_battr.json
@@ -0,0 +1,128 @@
+{
+    "mockBidRequest":
+    {
+        "id": "test-request-id",
+        "imp": [
+        {
+            "id": "test-imp-id",
+            "instl": 1,
+            "banner":
+            {
+                "battr": [1, 16],
+                "mimes": [
+                    "image/jpeg",
+                    "image/jpg",
+                    "image/gif",
+                    "image/png"
+                ],
+                "w": 320,
+                "h": 480
+            },
+            "ext":
+            {
+                "prebid":
+                {
+                    "is_rewarded_inventory": 1
+                },
+                "bidder":
+                {
+                    "host": "api-us",
+                    "path": "auction/rtb/v2",
+                    "seller_id": "1"
+                }
+            }
+        }]
+    },
+    "httpCalls": [
+    {
+        "expectedRequest":
+        {
+            "uri": "https://api-us.bidmachine.io/auction/rtb/v2/1",
+            "headers":
+            {
+                "Content-Type": [
+                    "application/json"
+                ],
+                "Accept": [
+                    "application/json"
+                ],
+                "X-Openrtb-Version": [
+                    "2.5"
+                ]
+            },
+            "body":
+            {
+                "id": "test-request-id",
+                "imp": [
+                {
+                    "id": "test-imp-id",
+                    "instl": 1,
+                    "banner":
+                    {
+                        "battr": [1, 16],
+                        "mimes": [
+                            "image/jpeg",
+                            "image/jpg",
+                            "image/gif",
+                            "image/png"
+                        ],
+                        "w": 320,
+                        "h": 480
+                    },
+                    "ext":
+                    {
+                        "prebid":
+                        {
+                            "is_rewarded_inventory": 1
+                        },
+                        "bidder":
+                        {
+                            "host": "api-us",
+                            "path": "auction/rtb/v2",
+                            "seller_id": "1"
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse":
+        {
+            "status": 200,
+            "body":
+            {
+                "id": "test-request-id",
+                "seatbid": [
+                {
+                    "bid": [
+                    {
+                        "id": "test-bid-id",
+                        "impid": "test-imp-id",
+                        "price": 100,
+                        "adm": "test-adm",
+                        "cid": "test-cid",
+                        "crid": "test-crid"
+                    }],
+                    "seat": "bidmachine"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [
+    {
+        "currency": "USD",
+        "bids": [
+        {
+            "bid":
+            {
+                "id": "test-bid-id",
+                "impid": "test-imp-id",
+                "price": 100,
+                "adm": "test-adm",
+                "cid": "test-cid",
+                "crid": "test-crid"
+            },
+            "type": "banner"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/exemplary/rewarded_video_no_battr.json b/adapters/bidmachine/bidmachinetest/exemplary/rewarded_video_no_battr.json
new file mode 100644
index 00000000..72bf59d7
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/exemplary/rewarded_video_no_battr.json
@@ -0,0 +1,132 @@
+{
+    "mockBidRequest":
+    {
+        "id": "test-request-id",
+        "imp": [
+        {
+            "id": "test-imp-id",
+            "video":
+            {
+                "battr": [1],
+                "mimes": [
+                    "video/mp4",
+                    "video/3gpp"
+                ],
+                "protocols": [
+                    1,
+                    2,
+                    3
+                ],
+                "w": 320,
+                "h": 480
+            },
+            "ext":
+            {
+                "prebid":
+                {
+                    "is_rewarded_inventory": 1
+                },
+                "bidder":
+                {
+                    "host": "api-us",
+                    "path": "auction/rtb/v2",
+                    "seller_id": "1"
+                }
+            }
+        }]
+    },
+    "httpCalls": [
+    {
+        "expectedRequest":
+        {
+            "uri": "https://api-us.bidmachine.io/auction/rtb/v2/1",
+            "headers":
+            {
+                "Content-Type": [
+                    "application/json"
+                ],
+                "Accept": [
+                    "application/json"
+                ],
+                "X-Openrtb-Version": [
+                    "2.5"
+                ]
+            },
+            "body":
+            {
+                "id": "test-request-id",
+                "imp": [
+                {
+                    "id": "test-imp-id",
+                    "video":
+                    {
+                        "battr": [1, 16],
+                        "mimes": [
+                            "video/mp4",
+                            "video/3gpp"
+                        ],
+                        "protocols": [
+                            1,
+                            2,
+                            3
+                        ],
+                        "w": 320,
+                        "h": 480
+                    },
+                    "ext":
+                    {
+                        "prebid":
+                        {
+                            "is_rewarded_inventory": 1
+                        },
+                        "bidder":
+                        {
+                            "host": "api-us",
+                            "path": "auction/rtb/v2",
+                            "seller_id": "1"
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse":
+        {
+            "status": 200,
+            "body":
+            {
+                "id": "test-request-id",
+                "seatbid": [
+                {
+                    "bid": [
+                    {
+                        "id": "test-bid-id",
+                        "impid": "test-imp-id",
+                        "price": 100,
+                        "adm": "test-adm",
+                        "cid": "test-cid",
+                        "crid": "test-crid"
+                    }],
+                    "seat": "bidmachine"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [
+    {
+        "currency": "USD",
+        "bids": [
+        {
+            "bid":
+            {
+                "id": "test-bid-id",
+                "impid": "test-imp-id",
+                "price": 100,
+                "adm": "test-adm",
+                "cid": "test-cid",
+                "crid": "test-crid"
+            },
+            "type": "video"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/exemplary/rewarded_video_w_battr.json b/adapters/bidmachine/bidmachinetest/exemplary/rewarded_video_w_battr.json
new file mode 100644
index 00000000..0d681e16
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/exemplary/rewarded_video_w_battr.json
@@ -0,0 +1,132 @@
+{
+    "mockBidRequest":
+    {
+        "id": "test-request-id",
+        "imp": [
+        {
+            "id": "test-imp-id",
+            "video":
+            {
+                "battr": [1, 16],
+                "mimes": [
+                    "video/mp4",
+                    "video/3gpp"
+                ],
+                "protocols": [
+                    1,
+                    2,
+                    3
+                ],
+                "w": 320,
+                "h": 480
+            },
+            "ext":
+            {
+                "prebid":
+                {
+                    "is_rewarded_inventory": 1
+                },
+                "bidder":
+                {
+                    "host": "api-us",
+                    "path": "auction/rtb/v2",
+                    "seller_id": "1"
+                }
+            }
+        }]
+    },
+    "httpCalls": [
+    {
+        "expectedRequest":
+        {
+            "uri": "https://api-us.bidmachine.io/auction/rtb/v2/1",
+            "headers":
+            {
+                "Content-Type": [
+                    "application/json"
+                ],
+                "Accept": [
+                    "application/json"
+                ],
+                "X-Openrtb-Version": [
+                    "2.5"
+                ]
+            },
+            "body":
+            {
+                "id": "test-request-id",
+                "imp": [
+                {
+                    "id": "test-imp-id",
+                    "video":
+                    {
+                        "battr": [1, 16],
+                        "mimes": [
+                            "video/mp4",
+                            "video/3gpp"
+                        ],
+                        "protocols": [
+                            1,
+                            2,
+                            3
+                        ],
+                        "w": 320,
+                        "h": 480
+                    },
+                    "ext":
+                    {
+                        "prebid":
+                        {
+                            "is_rewarded_inventory": 1
+                        },
+                        "bidder":
+                        {
+                            "host": "api-us",
+                            "path": "auction/rtb/v2",
+                            "seller_id": "1"
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse":
+        {
+            "status": 200,
+            "body":
+            {
+                "id": "test-request-id",
+                "seatbid": [
+                {
+                    "bid": [
+                    {
+                        "id": "test-bid-id",
+                        "impid": "test-imp-id",
+                        "price": 100,
+                        "adm": "test-adm",
+                        "cid": "test-cid",
+                        "crid": "test-crid"
+                    }],
+                    "seat": "bidmachine"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [
+    {
+        "currency": "USD",
+        "bids": [
+        {
+            "bid":
+            {
+                "id": "test-bid-id",
+                "impid": "test-imp-id",
+                "price": 100,
+                "adm": "test-adm",
+                "cid": "test-cid",
+                "crid": "test-crid"
+            },
+            "type": "video"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/exemplary/simple_banner.json b/adapters/bidmachine/bidmachinetest/exemplary/simple_banner.json
new file mode 100644
index 00000000..bde25edc
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/exemplary/simple_banner.json
@@ -0,0 +1,118 @@
+{
+    "mockBidRequest":
+    {
+        "id": "test-request-id",
+        "imp": [
+        {
+            "id": "test-imp-id",
+            "banner":
+            {
+                "battr": [1],
+                "mimes": [
+                    "image/jpeg",
+                    "image/jpg",
+                    "image/gif",
+                    "image/png"
+                ],
+                "w": 320,
+                "h": 50
+            },
+            "ext":
+            {
+                "bidder":
+                {
+                    "host": "api-us",
+                    "path": "auction/rtb/v2",
+                    "seller_id": "1"
+                }
+            }
+        }]
+    },
+    "httpCalls": [
+    {
+        "expectedRequest":
+        {
+            "uri": "https://api-us.bidmachine.io/auction/rtb/v2/1",
+            "headers":
+            {
+                "Content-Type": [
+                    "application/json"
+                ],
+                "Accept": [
+                    "application/json"
+                ],
+                "X-Openrtb-Version": [
+                    "2.5"
+                ]
+            },
+            "body":
+            {
+                "id": "test-request-id",
+                "imp": [
+                {
+                    "id": "test-imp-id",
+                    "banner":
+                    {
+                        "battr": [1],
+                        "mimes": [
+                            "image/jpeg",
+                            "image/jpg",
+                            "image/gif",
+                            "image/png"
+                        ],
+                        "w": 320,
+                        "h": 50
+                    },
+                    "ext":
+                    {
+                        "bidder":
+                        {
+                            "host": "api-us",
+                            "path": "auction/rtb/v2",
+                            "seller_id": "1"
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse":
+        {
+            "status": 200,
+            "body":
+            {
+                "id": "test-request-id",
+                "seatbid": [
+                {
+                    "bid": [
+                    {
+                        "id": "test-bid-id",
+                        "impid": "test-imp-id",
+                        "price": 100,
+                        "adm": "test-adm",
+                        "cid": "test-cid",
+                        "crid": "test-crid"
+                    }],
+                    "seat": "bidmachine"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [
+    {
+        "currency": "USD",
+        "bids": [
+        {
+            "bid":
+            {
+                "id": "test-bid-id",
+                "impid": "test-imp-id",
+                "price": 100,
+                "adm": "test-adm",
+                "cid": "test-cid",
+                "crid": "test-crid"
+            },
+            "type": "banner"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/exemplary/simple_interstitial.json b/adapters/bidmachine/bidmachinetest/exemplary/simple_interstitial.json
new file mode 100644
index 00000000..b49c85e5
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/exemplary/simple_interstitial.json
@@ -0,0 +1,120 @@
+{
+    "mockBidRequest":
+    {
+        "id": "test-request-id",
+        "imp": [
+        {
+            "id": "test-imp-id",
+            "instl": 1,
+            "banner":
+            {
+                "battr": [1],
+                "mimes": [
+                    "image/jpeg",
+                    "image/jpg",
+                    "image/gif",
+                    "image/png"
+                ],
+                "w": 320,
+                "h": 480
+            },
+            "ext":
+            {
+                "bidder":
+                {
+                    "host": "api-us",
+                    "path": "auction/rtb/v2",
+                    "seller_id": "1"
+                }
+            }
+        }]
+    },
+    "httpCalls": [
+    {
+        "expectedRequest":
+        {
+            "uri": "https://api-us.bidmachine.io/auction/rtb/v2/1",
+            "headers":
+            {
+                "Content-Type": [
+                    "application/json"
+                ],
+                "Accept": [
+                    "application/json"
+                ],
+                "X-Openrtb-Version": [
+                    "2.5"
+                ]
+            },
+            "body":
+            {
+                "id": "test-request-id",
+                "imp": [
+                {
+                    "id": "test-imp-id",
+                    "instl": 1,
+                    "banner":
+                    {
+                        "battr": [1],
+                        "mimes": [
+                            "image/jpeg",
+                            "image/jpg",
+                            "image/gif",
+                            "image/png"
+                        ],
+                        "w": 320,
+                        "h": 480
+                    },
+                    "ext":
+                    {
+                        "bidder":
+                        {
+                            "host": "api-us",
+                            "path": "auction/rtb/v2",
+                            "seller_id": "1"
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse":
+        {
+            "status": 200,
+            "body":
+            {
+                "id": "test-request-id",
+                "seatbid": [
+                {
+                    "bid": [
+                    {
+                        "id": "test-bid-id",
+                        "impid": "test-imp-id",
+                        "price": 100,
+                        "adm": "test-adm",
+                        "cid": "test-cid",
+                        "crid": "test-crid"
+                    }],
+                    "seat": "bidmachine"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [
+    {
+        "currency": "USD",
+        "bids": [
+        {
+            "bid":
+            {
+                "id": "test-bid-id",
+                "impid": "test-imp-id",
+                "price": 100,
+                "adm": "test-adm",
+                "cid": "test-cid",
+                "crid": "test-crid"
+            },
+            "type": "banner"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/exemplary/simple_video.json b/adapters/bidmachine/bidmachinetest/exemplary/simple_video.json
new file mode 100644
index 00000000..e6e1d2dc
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/exemplary/simple_video.json
@@ -0,0 +1,124 @@
+{
+    "mockBidRequest":
+    {
+        "id": "test-request-id",
+        "imp": [
+        {
+            "id": "test-imp-id",
+            "video":
+            {
+                "battr": [1],
+                "mimes": [
+                    "video/mp4",
+                    "video/3gpp"
+                ],
+                "protocols": [
+                    1,
+                    2,
+                    3
+                ],
+                "w": 320,
+                "h": 480
+            },
+            "ext":
+            {
+                "bidder":
+                {
+                    "host": "api-us",
+                    "path": "auction/rtb/v2",
+                    "seller_id": "1"
+                }
+            }
+        }]
+    },
+    "httpCalls": [
+    {
+        "expectedRequest":
+        {
+            "uri": "https://api-us.bidmachine.io/auction/rtb/v2/1",
+            "headers":
+            {
+                "Content-Type": [
+                    "application/json"
+                ],
+                "Accept": [
+                    "application/json"
+                ],
+                "X-Openrtb-Version": [
+                    "2.5"
+                ]
+            },
+            "body":
+            {
+                "id": "test-request-id",
+                "imp": [
+                {
+                    "id": "test-imp-id",
+                    "video":
+                    {
+                        "battr": [1],
+                        "mimes": [
+                            "video/mp4",
+                            "video/3gpp"
+                        ],
+                        "protocols": [
+                            1,
+                            2,
+                            3
+                        ],
+                        "w": 320,
+                        "h": 480
+                    },
+                    "ext":
+                    {
+                        "bidder":
+                        {
+                            "host": "api-us",
+                            "path": "auction/rtb/v2",
+                            "seller_id": "1"
+                        }
+                    }
+                }]
+            }
+        },
+        "mockResponse":
+        {
+            "status": 200,
+            "body":
+            {
+                "id": "test-request-id",
+                "seatbid": [
+                {
+                    "bid": [
+                    {
+                        "id": "test-bid-id",
+                        "impid": "test-imp-id",
+                        "price": 100,
+                        "adm": "test-adm",
+                        "cid": "test-cid",
+                        "crid": "test-crid"
+                    }],
+                    "seat": "bidmachine"
+                }],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [
+    {
+        "currency": "USD",
+        "bids": [
+        {
+            "bid":
+            {
+                "id": "test-bid-id",
+                "impid": "test-imp-id",
+                "price": 100,
+                "adm": "test-adm",
+                "cid": "test-cid",
+                "crid": "test-crid"
+            },
+            "type": "video"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/params/race/banner.json b/adapters/bidmachine/bidmachinetest/params/race/banner.json
new file mode 100644
index 00000000..0402e4a1
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/params/race/banner.json
@@ -0,0 +1,5 @@
+{
+  "seller_id": "1",
+  "path": "auction/rtb/v2",
+  "host": "api-eu"
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/params/race/video.json b/adapters/bidmachine/bidmachinetest/params/race/video.json
new file mode 100644
index 00000000..0402e4a1
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/params/race/video.json
@@ -0,0 +1,5 @@
+{
+  "seller_id": "1",
+  "path": "auction/rtb/v2",
+  "host": "api-eu"
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/supplemental/empty-banner-format .json b/adapters/bidmachine/bidmachinetest/supplemental/empty-banner-format .json
new file mode 100644
index 00000000..b454ce3d
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/supplemental/empty-banner-format .json
@@ -0,0 +1,19 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-impression-id-1",
+        "banner": {
+          "format": []
+        }
+      }
+    ]
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Impression with id: test-impression-id-1 has following error: Banner width and height is not provided and banner format array is empty. At least one is required",
+      "comparison": "literal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/supplemental/missing-banner-format.json b/adapters/bidmachine/bidmachinetest/supplemental/missing-banner-format.json
new file mode 100644
index 00000000..563737e1
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/supplemental/missing-banner-format.json
@@ -0,0 +1,26 @@
+{
+  "mockBidRequest":
+  {
+    "id": "test-request-id",
+    "imp": [
+    {
+      "id": "test-impression-id-1",
+      "banner":
+      {},
+      "ext":
+      {
+        "bidder":
+        {
+          "seller_id": "1",
+          "host": "api-eu",
+          "path": "auction/rtb/v2"
+        }
+      }
+    }]
+  },
+  "expectedMakeRequestsErrors": [
+  {
+    "value": "Impression with id: test-impression-id-1 has following error: Banner width and height is not provided and banner format is missing. At least one is required",
+    "comparison": "literal"
+  }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/supplemental/status-204.json b/adapters/bidmachine/bidmachinetest/supplemental/status-204.json
new file mode 100644
index 00000000..daa7f00a
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/supplemental/status-204.json
@@ -0,0 +1,66 @@
+{
+  "mockBidRequest":
+  {
+    "id": "test-request-id",
+    "imp": [
+    {
+      "id": "test-impression-id-1",
+      "banner":
+      {
+        "format": [
+        {
+          "h": 250,
+          "w": 300
+        }]
+      },
+      "ext":
+      {
+        "bidder":
+        {
+          "seller_id": "1",
+          "host": "api-eu",
+          "path": "auction/rtb/v2"
+        }
+      }
+    }]
+  },
+  "httpCalls": [
+  {
+    "expectedRequest":
+    {
+      "uri": "https://api-eu.bidmachine.io/auction/rtb/v2/1",
+      "body":
+      {
+        "id": "test-request-id",
+        "imp": [
+        {
+          "id": "test-impression-id-1",
+          "banner":
+          {
+            "format": [
+            {
+              "h": 250,
+              "w": 300
+            }]
+          },
+          "ext":
+          {
+            "bidder":
+            {
+              "seller_id": "1",
+              "host": "api-eu",
+              "path": "auction/rtb/v2"
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse":
+    {
+      "status": 204,
+      "body":
+      {}
+    }
+  }],
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/supplemental/status-400.json b/adapters/bidmachine/bidmachinetest/supplemental/status-400.json
new file mode 100644
index 00000000..5eb4ccfc
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/supplemental/status-400.json
@@ -0,0 +1,69 @@
+{
+  "mockBidRequest":
+  {
+    "id": "test-request-id",
+    "imp": [
+    {
+      "id": "test-impression-id-1",
+      "banner":
+      {
+        "format": [
+        {
+          "h": 250,
+          "w": 300
+        }]
+      },
+      "ext":
+      {
+        "bidder":
+        {
+          "seller_id": "1",
+          "host": "api-eu",
+          "path": "auction/rtb/v2"
+        }
+      }
+    }]
+  },
+  "httpCalls": [
+  {
+    "expectedRequest":
+    {
+      "uri": "https://api-eu.bidmachine.io/auction/rtb/v2/1",
+      "body":
+      {
+        "id": "test-request-id",
+        "imp": [
+        {
+          "id": "test-impression-id-1",
+          "banner":
+          {
+            "format": [
+            {
+              "h": 250,
+              "w": 300
+            }]
+          },
+          "ext":
+          {
+            "bidder":
+            {
+              "seller_id": "1",
+              "host": "api-eu",
+              "path": "auction/rtb/v2"
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse":
+    {
+      "status": 400,
+      "body": "server text here"
+    }
+  }],
+  "expectedMakeBidsErrors": [
+  {
+    "value": "unexpected status code: 400 \"server text here\"",
+    "comparison": "literal"
+  }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/supplemental/status-403.json b/adapters/bidmachine/bidmachinetest/supplemental/status-403.json
new file mode 100644
index 00000000..3434275b
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/supplemental/status-403.json
@@ -0,0 +1,69 @@
+{
+  "mockBidRequest":
+  {
+    "id": "test-request-id",
+    "imp": [
+    {
+      "id": "test-impression-id-1",
+      "banner":
+      {
+        "format": [
+        {
+          "h": 250,
+          "w": 300
+        }]
+      },
+      "ext":
+      {
+        "bidder":
+        {
+          "seller_id": "1",
+          "host": "api-eu",
+          "path": "auction/rtb/v2"
+        }
+      }
+    }]
+  },
+  "httpCalls": [
+  {
+    "expectedRequest":
+    {
+      "uri": "https://api-eu.bidmachine.io/auction/rtb/v2/1",
+      "body":
+      {
+        "id": "test-request-id",
+        "imp": [
+        {
+          "id": "test-impression-id-1",
+          "banner":
+          {
+            "format": [
+            {
+              "h": 250,
+              "w": 300
+            }]
+          },
+          "ext":
+          {
+            "bidder":
+            {
+              "seller_id": "1",
+              "host": "api-eu",
+              "path": "auction/rtb/v2"
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse":
+    {
+      "status": 403,
+      "body": "server text here"
+    }
+  }],
+  "expectedMakeBidsErrors": [
+  {
+    "value": "unexpected status code: 403 \"server text here\"",
+    "comparison": "literal"
+  }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/supplemental/status-408.json b/adapters/bidmachine/bidmachinetest/supplemental/status-408.json
new file mode 100644
index 00000000..51965461
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/supplemental/status-408.json
@@ -0,0 +1,69 @@
+{
+  "mockBidRequest":
+  {
+    "id": "test-request-id",
+    "imp": [
+    {
+      "id": "test-impression-id-1",
+      "banner":
+      {
+        "format": [
+        {
+          "h": 250,
+          "w": 300
+        }]
+      },
+      "ext":
+      {
+        "bidder":
+        {
+          "seller_id": "1",
+          "host": "api-eu",
+          "path": "auction/rtb/v2"
+        }
+      }
+    }]
+  },
+  "httpCalls": [
+  {
+    "expectedRequest":
+    {
+      "uri": "https://api-eu.bidmachine.io/auction/rtb/v2/1",
+      "body":
+      {
+        "id": "test-request-id",
+        "imp": [
+        {
+          "id": "test-impression-id-1",
+          "banner":
+          {
+            "format": [
+            {
+              "h": 250,
+              "w": 300
+            }]
+          },
+          "ext":
+          {
+            "bidder":
+            {
+              "seller_id": "1",
+              "host": "api-eu",
+              "path": "auction/rtb/v2"
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse":
+    {
+      "status": 408,
+      "body": "server text here"
+    }
+  }],
+  "expectedMakeBidsErrors": [
+  {
+    "value": "unexpected status code: 408 \"server text here\"",
+    "comparison": "literal"
+  }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/supplemental/status-500.json b/adapters/bidmachine/bidmachinetest/supplemental/status-500.json
new file mode 100644
index 00000000..f7abfb0f
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/supplemental/status-500.json
@@ -0,0 +1,69 @@
+{
+  "mockBidRequest":
+  {
+    "id": "test-request-id",
+    "imp": [
+    {
+      "id": "test-impression-id-1",
+      "banner":
+      {
+        "format": [
+        {
+          "h": 250,
+          "w": 300
+        }]
+      },
+      "ext":
+      {
+        "bidder":
+        {
+          "seller_id": "1",
+          "host": "api-eu",
+          "path": "auction/rtb/v2"
+        }
+      }
+    }]
+  },
+  "httpCalls": [
+  {
+    "expectedRequest":
+    {
+      "uri": "https://api-eu.bidmachine.io/auction/rtb/v2/1",
+      "body":
+      {
+        "id": "test-request-id",
+        "imp": [
+        {
+          "id": "test-impression-id-1",
+          "banner":
+          {
+            "format": [
+            {
+              "h": 250,
+              "w": 300
+            }]
+          },
+          "ext":
+          {
+            "bidder":
+            {
+              "seller_id": "1",
+              "host": "api-eu",
+              "path": "auction/rtb/v2"
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse":
+    {
+      "status": 500,
+      "body": "server text here"
+    }
+  }],
+  "expectedMakeBidsErrors": [
+  {
+    "value": "unexpected status code: 500 \"server text here\"",
+    "comparison": "literal"
+  }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/supplemental/status-502.json b/adapters/bidmachine/bidmachinetest/supplemental/status-502.json
new file mode 100644
index 00000000..44dabb05
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/supplemental/status-502.json
@@ -0,0 +1,69 @@
+{
+  "mockBidRequest":
+  {
+    "id": "test-request-id",
+    "imp": [
+    {
+      "id": "test-impression-id-1",
+      "banner":
+      {
+        "format": [
+        {
+          "h": 250,
+          "w": 300
+        }]
+      },
+      "ext":
+      {
+        "bidder":
+        {
+          "seller_id": "1",
+          "host": "api-eu",
+          "path": "auction/rtb/v2"
+        }
+      }
+    }]
+  },
+  "httpCalls": [
+  {
+    "expectedRequest":
+    {
+      "uri": "https://api-eu.bidmachine.io/auction/rtb/v2/1",
+      "body":
+      {
+        "id": "test-request-id",
+        "imp": [
+        {
+          "id": "test-impression-id-1",
+          "banner":
+          {
+            "format": [
+            {
+              "h": 250,
+              "w": 300
+            }]
+          },
+          "ext":
+          {
+            "bidder":
+            {
+              "seller_id": "1",
+              "host": "api-eu",
+              "path": "auction/rtb/v2"
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse":
+    {
+      "status": 502,
+      "body": "server text here"
+    }
+  }],
+  "expectedMakeBidsErrors": [
+  {
+    "value": "unexpected status code: 502 \"server text here\"",
+    "comparison": "literal"
+  }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/supplemental/status-503.json b/adapters/bidmachine/bidmachinetest/supplemental/status-503.json
new file mode 100644
index 00000000..979ac4f6
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/supplemental/status-503.json
@@ -0,0 +1,69 @@
+{
+  "mockBidRequest":
+  {
+    "id": "test-request-id",
+    "imp": [
+    {
+      "id": "test-impression-id-1",
+      "banner":
+      {
+        "format": [
+        {
+          "h": 250,
+          "w": 300
+        }]
+      },
+      "ext":
+      {
+        "bidder":
+        {
+          "seller_id": "1",
+          "host": "api-eu",
+          "path": "auction/rtb/v2"
+        }
+      }
+    }]
+  },
+  "httpCalls": [
+  {
+    "expectedRequest":
+    {
+      "uri": "https://api-eu.bidmachine.io/auction/rtb/v2/1",
+      "body":
+      {
+        "id": "test-request-id",
+        "imp": [
+        {
+          "id": "test-impression-id-1",
+          "banner":
+          {
+            "format": [
+            {
+              "h": 250,
+              "w": 300
+            }]
+          },
+          "ext":
+          {
+            "bidder":
+            {
+              "seller_id": "1",
+              "host": "api-eu",
+              "path": "auction/rtb/v2"
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse":
+    {
+      "status": 503,
+      "body": "server text here"
+    }
+  }],
+  "expectedMakeBidsErrors": [
+  {
+    "value": "unexpected status code: 503 \"server text here\"",
+    "comparison": "literal"
+  }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/supplemental/status-504.json b/adapters/bidmachine/bidmachinetest/supplemental/status-504.json
new file mode 100644
index 00000000..918ada2c
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/supplemental/status-504.json
@@ -0,0 +1,69 @@
+{
+  "mockBidRequest":
+  {
+    "id": "test-request-id",
+    "imp": [
+    {
+      "id": "test-impression-id-1",
+      "banner":
+      {
+        "format": [
+        {
+          "h": 250,
+          "w": 300
+        }]
+      },
+      "ext":
+      {
+        "bidder":
+        {
+          "seller_id": "1",
+          "host": "api-eu",
+          "path": "auction/rtb/v2"
+        }
+      }
+    }]
+  },
+  "httpCalls": [
+  {
+    "expectedRequest":
+    {
+      "uri": "https://api-eu.bidmachine.io/auction/rtb/v2/1",
+      "body":
+      {
+        "id": "test-request-id",
+        "imp": [
+        {
+          "id": "test-impression-id-1",
+          "banner":
+          {
+            "format": [
+            {
+              "h": 250,
+              "w": 300
+            }]
+          },
+          "ext":
+          {
+            "bidder":
+            {
+              "seller_id": "1",
+              "host": "api-eu",
+              "path": "auction/rtb/v2"
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse":
+    {
+      "status": 504,
+      "body": "server text here"
+    }
+  }],
+  "expectedMakeBidsErrors": [
+  {
+    "value": "unexpected status code: 504 \"server text here\"",
+    "comparison": "literal"
+  }]
+}
\ No newline at end of file
diff --git a/adapters/bidmachine/bidmachinetest/supplemental/wrong-host.json b/adapters/bidmachine/bidmachinetest/supplemental/wrong-host.json
new file mode 100644
index 00000000..82ee3269
--- /dev/null
+++ b/adapters/bidmachine/bidmachinetest/supplemental/wrong-host.json
@@ -0,0 +1,35 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-impression-id-1",
+        "banner": {
+          "format": [
+            {
+              "w": 320,
+              "h": 50
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "host": "`",
+            "path": "auction/rtb/v2",
+            "seller_id": "1"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [],
+  "expectedBidResponses": [],
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Failed to create final URL with provided host",
+      "comparison": "literal"
+    }
+  ],
+  "expectedMakeBidsErrors": []
+}
+
diff --git a/adapters/bidmachine/params_test.go b/adapters/bidmachine/params_test.go
new file mode 100644
index 00000000..15ebbcbf
--- /dev/null
+++ b/adapters/bidmachine/params_test.go
@@ -0,0 +1,78 @@
+package bidmachine
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderBidmachine, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected Bidmachine params: %s \n Error: %s", validParam, err)
+		}
+	}
+}
+
+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.BidderBidmachine, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"seller_id":"1", "host":"api-eu", "path":"auction/rtb/v2"}`,
+	`{"seller_id":"1", "host":"api-us", "path":"auction/rtb/v2"}`,
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{"some_random_field":""}`,
+	`{"seller_id":""}`,
+	`{"seller_id": 1}`,
+	`{"seller_id": 1.2}`,
+	`{"seller_id": null}`,
+	`{"seller_id": true}`,
+	`{"seller_id": []}`,
+	`{"seller_id": {}}`,
+	`{"host":""}`,
+	`{"host": 1}`,
+	`{"host": 1.2}`,
+	`{"host": null}`,
+	`{"host": true}`,
+	`{"host": []}`,
+	`{"host": {}}`,
+	`{"path":""}`,
+	`{"path": 1}`,
+	`{"path": 1.2}`,
+	`{"path": null}`,
+	`{"path": true}`,
+	`{"path": []}`,
+	`{"path": {}}`,
+	`{"seller_id":"", "path": "", host: ""}`,
+	`{"seller_id": 1, "path": 2, host: 3}`,
+	`{"seller_id": 1.2}, "path": 5.5, host: 3.3`,
+	`{"seller_id": null, "path": null, host: null}`,
+	`{"seller_id": true, "path": false, host: true}`,
+	`{"seller_id": [], "path": [], host: []}`,
+	`{"seller_id": {}, "path": {}, host: {}}`,
+}
diff --git a/adapters/criteo/criteo_test.go b/adapters/criteo/criteo_test.go
new file mode 100644
index 00000000..0a700734
--- /dev/null
+++ b/adapters/criteo/criteo_test.go
@@ -0,0 +1,28 @@
+package criteo
+
+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) {
+
+	// Setup:
+	bidder, buildErr := builderWithGuidGenerator(
+		openrtb_ext.BidderCriteo,
+		config.Adapter{
+			Endpoint: "https://bidder.criteo.com/cdb?profileId=230",
+		},
+		newFakeGuidGenerator("00000000-0000-0000-00000000"),
+	)
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	// Execute & Verify:
+	adapterstest.RunJSONBidderTest(t, "criteotest", bidder)
+}
diff --git a/adapters/criteo/criteotest/exemplary/simple-banner-cookie-uid.json b/adapters/criteo/criteotest/exemplary/simple-banner-cookie-uid.json
new file mode 100755
index 00000000..cb152f47
--- /dev/null
+++ b/adapters/criteo/criteotest/exemplary/simple-banner-cookie-uid.json
@@ -0,0 +1,115 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "id": "site-id",
+      "page": "criteo.com"
+    },
+    "device": {
+      "os": "android",
+      "ip": "91.199.242.236",
+      "ua": "random user agent"
+    },
+    "user": {
+      "buyeruid": "criteo-user-id"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "zoneid": 123456,
+            "networkid": 78910
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bidder.criteo.com/cdb?profileId=230",
+        "headers": {
+          "X-Forwarded-For": ["91.199.242.236"],
+          "User-Agent": ["random user agent"],
+          "Cookie": ["uid=criteo-user-id"]
+        },
+        "body": {
+          "id": "test-request-id",
+          "publisher": {
+            "siteid": "site-id",
+            "url": "criteo.com",
+            "networkid": 78910
+          },
+          "user": {
+            "cookieuid": "criteo-user-id",
+            "ip": "91.199.242.236",
+            "ua": "random user agent",
+            "deviceos": "android",
+            "deviceidtype": "gaid"
+          },
+          "gdprconsent": {},
+          "slots": [
+            {
+              "slotid": "00000000-0000-0000-00000000",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "sizes": [
+                "300x250"
+              ]
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "slots": [
+            {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "cpm": 0.1,
+              "currency": "USD",
+              "width": 300,
+              "height": 250,
+              "creativeid": "creative-123",
+              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-slot-id",
+            "impid": "test-imp-id",
+            "price": 0.1,
+            "crid": "creative-123",
+            "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/adapters/criteo/criteotest/exemplary/simple-banner-inapp.json b/adapters/criteo/criteotest/exemplary/simple-banner-inapp.json
new file mode 100755
index 00000000..0ec1b7f8
--- /dev/null
+++ b/adapters/criteo/criteotest/exemplary/simple-banner-inapp.json
@@ -0,0 +1,110 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "app": {
+        "bundle": "test.app.bundle"
+      },
+      "device": {
+        "ifa": "test-ifa-123456",
+        "os": "android",
+        "ip": "91.199.242.236",
+        "ua": "random user agent"
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": ["91.199.242.236"],
+            "User-Agent": ["random user agent"]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "bundleid": "test.app.bundle",
+              "networkid": 78910
+            },
+            "user": {
+              "deviceid": "test-ifa-123456",
+              "deviceos": "android",
+              "deviceidtype": "gaid",
+              "ip": "91.199.242.236",
+              "ua": "random user agent"
+            },
+            "gdprconsent": {},
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "id": "test-request-id",
+            "slots": [
+              {
+                "id": "test-slot-id",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "cpm": 0.1,
+                "currency": "USD",
+                "width": 300,
+                "height": 250,
+                "creativeid": "creative-123",
+                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              }
+            ]
+          }
+        }
+      }
+    ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-slot-id",
+            "impid": "test-imp-id",
+            "price": 0.1,
+            "crid": "creative-123",
+            "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
+  
diff --git a/adapters/criteo/criteotest/exemplary/simple-banner-uid.json b/adapters/criteo/criteotest/exemplary/simple-banner-uid.json
new file mode 100755
index 00000000..2499790b
--- /dev/null
+++ b/adapters/criteo/criteotest/exemplary/simple-banner-uid.json
@@ -0,0 +1,132 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "id": "site-id",
+      "page": "criteo.com"
+    },
+    "device": {
+      "os": "android",
+      "ip": "91.199.242.236",
+      "ua": "random user agent"
+    },
+    "user": {
+      "ip": "91.199.242.236",
+      "ua": "random user agent",
+      "ext": {
+        "eids": [{
+          "source": "criteo.com",
+          "uids": [{
+            "id": "criteo-eid"
+          }]
+        }]
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "zoneid": 123456,
+            "networkid": 78910
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bidder.criteo.com/cdb?profileId=230",
+        "headers": {
+          "X-Forwarded-For": ["91.199.242.236"],
+          "User-Agent": ["random user agent"]
+        },
+        "body": {
+          "id": "test-request-id",
+          "publisher": {
+            "siteid": "site-id",
+            "url": "criteo.com",
+            "networkid": 78910
+          },
+          "user": {
+            "ip": "91.199.242.236",
+            "ua": "random user agent",
+            "deviceos": "android",
+            "deviceidtype": "gaid"
+          },
+          "gdprconsent": {},
+          "slots": [
+            {
+              "slotid": "00000000-0000-0000-00000000",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "sizes": [
+                "300x250"
+              ]
+            }
+          ],
+          "eids": [
+            {
+              "source": "criteo.com",
+              "uids": [
+                {
+                  "id": "criteo-eid"
+                }
+              ]
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "slots": [
+            {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "cpm": 0.1,
+              "currency": "USD",
+              "width": 300,
+              "height": 250,
+              "creativeid": "creative-123",
+              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-slot-id",
+            "impid": "test-imp-id",
+            "price": 0.1,
+            "crid": "creative-123",
+            "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/adapters/criteo/criteotest/supplemental/204-response-from-target.json b/adapters/criteo/criteotest/supplemental/204-response-from-target.json
new file mode 100755
index 00000000..9ac14ff6
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/204-response-from-target.json
@@ -0,0 +1,81 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "app": {
+        "bundle": "test.app.bundle"
+      },
+      "device": {
+        "ifa": "test-ifa-123456",
+        "ip": "91.199.242.236",
+        "ua": "random user agent",
+        "os": "android"
+      },
+      "regs": {
+        "ext": {
+          "us_privacy": "1YYY"
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": ["91.199.242.236"],
+            "User-Agent": ["random user agent"]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "bundleid": "test.app.bundle",
+              "networkid": 78910
+            },
+            "user": {
+              "deviceid": "test-ifa-123456",
+              "deviceos": "android",
+              "deviceidtype": "gaid",
+              "uspIab": "1YYY",
+              "ip": "91.199.242.236",
+              "ua": "random user agent"
+            },
+            "gdprconsent": {},
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 204
+        }
+      }
+    ],
+    "expectedMakeBidsErrors": []
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/400-response-from-target.json b/adapters/criteo/criteotest/supplemental/400-response-from-target.json
new file mode 100755
index 00000000..ce3a4b62
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/400-response-from-target.json
@@ -0,0 +1,86 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "app": {
+        "bundle": "test.app.bundle"
+      },
+      "device": {
+        "ifa": "test-ifa-123456",
+        "ip": "91.199.242.236",
+        "ua": "random user agent",
+        "os": "android"
+      },
+      "regs": {
+        "ext": {
+          "us_privacy": "1YYY"
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": ["91.199.242.236"],
+            "User-Agent": ["random user agent"]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "bundleid": "test.app.bundle",
+              "networkid": 78910
+            },
+            "user": {
+              "deviceid": "test-ifa-123456",
+              "deviceos": "android",
+              "deviceidtype": "gaid",
+              "uspIab": "1YYY",
+              "ip": "91.199.242.236",
+              "ua": "random user agent"
+            },
+            "gdprconsent": {},
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 400
+        }
+      }
+    ],
+    "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/criteo/criteotest/supplemental/500-response-from-target.json b/adapters/criteo/criteotest/supplemental/500-response-from-target.json
new file mode 100755
index 00000000..db6c16a3
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/500-response-from-target.json
@@ -0,0 +1,86 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "app": {
+        "bundle": "test.app.bundle"
+      },
+      "device": {
+        "ifa": "test-ifa-123456",
+        "ip": "91.199.242.236",
+        "ua": "random user agent",
+        "os": "android"
+      },
+      "regs": {
+        "ext": {
+          "us_privacy": "1YYY"
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": ["91.199.242.236"],
+            "User-Agent": ["random user agent"]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "bundleid": "test.app.bundle",
+              "networkid": 78910
+            },
+            "user": {
+              "deviceid": "test-ifa-123456",
+              "deviceos": "android",
+              "deviceidtype": "gaid",
+              "uspIab": "1YYY",
+              "ip": "91.199.242.236",
+              "ua": "random user agent"
+            },
+            "gdprconsent": {},
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 500
+        }
+      }
+    ],
+    "expectedMakeBidsErrors": [
+      {
+        "value": "Unexpected status code: 500. Run with request.debug = 1 for more info",
+        "comparison": "literal"
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-inapp.json b/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-inapp.json
new file mode 100755
index 00000000..b047fa18
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-inapp.json
@@ -0,0 +1,115 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "app": {
+        "bundle": "test.app.bundle"
+      },
+      "device": {
+        "ifa": "test-ifa-123456",
+        "ip": "91.199.242.236",
+        "ua": "random user agent",
+        "os": "android"
+      },
+      "regs": {
+        "ext": {
+          "us_privacy": "1YYY"
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": ["91.199.242.236"],
+            "User-Agent": ["random user agent"]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "bundleid": "test.app.bundle",
+              "networkid": 78910
+            },
+            "user": {
+              "deviceid": "test-ifa-123456",
+              "deviceos": "android",
+              "deviceidtype": "gaid",
+              "uspIab": "1YYY",
+              "ip": "91.199.242.236",
+              "ua": "random user agent"
+            },
+            "gdprconsent": {},
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "slots": [
+              {
+                "id": "test-slot-id",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "cpm": 0.1,
+                "currency": "USD",
+                "width": 300,
+                "height": 250,
+                "creativeid": "creative-123",
+                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              }
+            ]
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "price": 0.1,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 250
+            },
+            "type": "banner"
+          }
+        ]
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-uid.json b/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-uid.json
new file mode 100755
index 00000000..f839624a
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/ccpa-with-consent-simple-banner-uid.json
@@ -0,0 +1,139 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "site": {
+        "id": "site-id",
+        "page": "criteo.com"
+      },
+      "device": {
+        "os": "android",
+        "ip": "91.199.242.236",
+        "ua": "random user agent"
+      },
+      "user": {
+        "ext": {
+          "eids": [{
+            "source": "criteo.com",
+            "uids": [{
+              "id": "criteo-eid"
+            }]
+          }]
+        }
+      },
+      "regs": {
+        "ext": {
+          "us_privacy": "1YYY"
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": [
+              "91.199.242.236"
+            ],
+            "User-Agent": [
+              "random user agent"
+            ]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "siteid": "site-id",
+              "url": "criteo.com",
+              "networkid": 78910
+            },
+            "user": {
+              "ip": "91.199.242.236",
+              "ua": "random user agent",
+              "deviceos": "android",
+              "deviceidtype": "gaid",
+              "uspIab": "1YYY"
+            },
+            "gdprconsent": {},
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ],
+            "eids": [
+              {
+                "source": "criteo.com",
+                "uids": [
+                  {
+                    "id": "criteo-eid"
+                  }
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "slots": [
+              {
+                "id": "test-slot-id",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "cpm": 0.1,
+                "currency": "USD",
+                "width": 300,
+                "height": 250,
+                "creativeid": "creative-123",
+                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              }
+            ]
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "price": 0.1,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 250
+            },
+            "type": "banner"
+          }
+        ]
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-inapp.json b/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-inapp.json
new file mode 100755
index 00000000..77a37361
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-inapp.json
@@ -0,0 +1,85 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "app": {
+        "bundle": "test.app.bundle"
+      },
+      "device": {
+        "ifa": "test-ifa-123456",
+        "ip": "91.199.242.236",
+        "ua": "random user agent",
+        "os": "android"
+      },
+      "regs": {
+        "ext": {
+          "us_privacy": "1YNN"
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": [
+              "91.199.242.236"
+            ],
+            "User-Agent": [
+              "random user agent"
+            ]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "bundleid": "test.app.bundle",
+              "networkid": 78910
+            },
+            "user": {
+              "deviceid": "test-ifa-123456",
+              "deviceos": "android",
+              "deviceidtype": "gaid",
+              "uspIab": "1YNN",
+              "ip": "91.199.242.236",
+              "ua": "random user agent"
+            },
+            "gdprconsent": {},
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 204
+        }
+      }
+    ],
+    "expectedBidResponses": []
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-uid.json b/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-uid.json
new file mode 100755
index 00000000..31c14fc4
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/ccpa-without-consent-simple-banner-uid.json
@@ -0,0 +1,105 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "site": {
+        "id": "site-id",
+        "page": "criteo.com"
+      },
+      "device": {
+        "os": "android",
+        "ip": "91.199.242.236",
+        "ua": "random user agent"
+      },
+      "user": {
+        "ext": {
+          "eids": [{
+            "source": "criteo.com",
+            "uids": [{
+              "id": "criteo-eid"
+            }]
+          }]
+        }
+      },
+      "regs": {
+        "ext": {
+          "us_privacy": "1YNN"
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": [
+              "91.199.242.236"
+            ],
+            "User-Agent": [
+              "random user agent"
+            ]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "siteid": "site-id",
+              "url": "criteo.com",
+              "networkid": 78910
+            },
+            "user": {
+              "ip": "91.199.242.236",
+              "ua": "random user agent",
+              "deviceos": "android",
+              "deviceidtype": "gaid",
+              "uspIab": "1YNN"
+            },
+            "gdprconsent": {},
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ],
+            "eids": [
+              {
+                "source": "criteo.com",
+                "uids": [
+                  {
+                    "id": "criteo-eid"
+                  }
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 204
+        }
+      }
+    ],
+    "expectedBidResponses": []
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-inapp.json b/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-inapp.json
new file mode 100755
index 00000000..416053db
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-inapp.json
@@ -0,0 +1,126 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "app": {
+        "bundle": "test.app.bundle"
+      },
+      "device": {
+        "ifa": "test-ifa-123456",
+        "os": "android",
+        "ip": "91.199.242.236",
+        "ua": "random user agent"
+      },
+      "user": {
+        "ext": {
+          "consent": "iabconsentstringwithconsent"
+        }
+      },
+      "regs": {
+        "ext": {
+          "gdpr": 1
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": [
+              "91.199.242.236"
+            ],
+            "User-Agent": [
+              "random user agent"
+            ]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "bundleid": "test.app.bundle",
+              "networkid": 78910
+            },
+            "user": {
+              "deviceid": "test-ifa-123456",
+              "deviceos": "android",
+              "deviceidtype": "gaid",
+              "ip": "91.199.242.236",
+              "ua": "random user agent"
+            },
+            "gdprconsent": {
+              "consentdata": "iabconsentstringwithconsent",
+              "gdprapplies": true
+            },
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "slots": [
+              {
+                "id": "test-slot-id",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "cpm": 0.1,
+                "currency": "USD",
+                "width": 300,
+                "height": 250,
+                "creativeid": "creative-123",
+                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              }
+            ]
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "price": 0.1,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 250
+            },
+            "type": "banner"
+          }
+        ]
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-uid.json b/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-uid.json
new file mode 100755
index 00000000..e8374616
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/gdpr-with-consent-simple-banner-uid.json
@@ -0,0 +1,142 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "site": {
+        "id": "site-id",
+        "page": "criteo.com"
+      },
+      "device": {
+        "os": "android",
+        "ip": "91.199.242.236",
+        "ua": "random user agent"
+      },
+      "user": {
+        "ext": {
+          "consent": "iabconsentstringwithconsent",
+          "eids": [{
+            "source": "criteo.com",
+            "uids": [{
+              "id": "criteo-eid"
+            }]
+          }]
+        }
+      },
+      "regs": {
+        "ext": {
+          "gdpr": 1
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": [
+              "91.199.242.236"
+            ],
+            "User-Agent": [
+              "random user agent"
+            ]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "siteid": "site-id",
+              "url": "criteo.com",
+              "networkid": 78910
+            },
+            "user": {
+              "ip": "91.199.242.236",
+              "ua": "random user agent",
+              "deviceos": "android",
+              "deviceidtype": "gaid"
+            },
+            "gdprconsent": {
+              "consentdata": "iabconsentstringwithconsent",
+              "gdprapplies": true
+            },
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ],
+            "eids": [
+              {
+                "source": "criteo.com",
+                "uids": [
+                  {
+                    "id": "criteo-eid"
+                  }
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "slots": [
+              {
+                "id": "test-slot-id",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "cpm": 0.1,
+                "currency": "USD",
+                "width": 300,
+                "height": 250,
+                "creativeid": "creative-123",
+                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              }
+            ]
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "price": 0.1,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 250
+            },
+            "type": "banner"
+          }
+        ]
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-inapp.json b/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-inapp.json
new file mode 100755
index 00000000..7385b937
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-inapp.json
@@ -0,0 +1,92 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "app": {
+        "bundle": "test.app.bundle"
+      },
+      "device": {
+        "ifa": "test-ifa-123456",
+        "os": "android",
+        "ip": "91.199.242.236",
+        "ua": "random user agent"
+      },
+      "user": {
+        "ext": {
+          "consent": "iabconsentstringwithnoconsent"
+        }
+      },
+      "regs": {
+        "ext": {
+          "gdpr": 1
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": [
+              "91.199.242.236"
+            ],
+            "User-Agent": [
+              "random user agent"
+            ]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "bundleid": "test.app.bundle",
+              "networkid": 78910
+            },
+            "user": {
+              "deviceid": "test-ifa-123456",
+              "deviceos": "android",
+              "deviceidtype": "gaid",
+              "ip": "91.199.242.236",
+              "ua": "random user agent"
+            },
+            "gdprconsent": {
+              "consentdata": "iabconsentstringwithnoconsent",
+              "gdprapplies": true
+            },
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 204
+        }
+      }
+    ],
+    "expectedBidResponses": []
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-uid.json b/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-uid.json
new file mode 100755
index 00000000..dbc5fb32
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/gdpr-without-consent-simple-banner-uid.json
@@ -0,0 +1,108 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "site": {
+        "id": "site-id",
+        "page": "criteo.com"
+      },
+      "device": {
+        "os": "android",
+        "ip": "91.199.242.236",
+        "ua": "random user agent"
+      },
+      "user": {
+        "ext": {
+          "consent": "iabconsentstringwithoutconsent",
+          "eids": [{
+            "source": "criteo.com",
+            "uids": [{
+              "id": "criteo-eid"
+            }]
+          }]
+        }
+      },
+      "regs": {
+        "ext": {
+          "gdpr": 1
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": [
+              "91.199.242.236"
+            ],
+            "User-Agent": [
+              "random user agent"
+            ]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "siteid": "site-id",
+              "url": "criteo.com",
+              "networkid": 78910
+            },
+            "user": {
+              "ip": "91.199.242.236",
+              "ua": "random user agent",
+              "deviceos": "android",
+              "deviceidtype": "gaid"
+            },
+            "gdprconsent": {
+              "consentdata": "iabconsentstringwithoutconsent",
+              "gdprapplies": true
+            },
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ],
+            "eids": [
+              {
+                "source": "criteo.com",
+                "uids": [
+                  {
+                    "id": "criteo-eid"
+                  }
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 204
+        }
+      }
+    ],
+    "expectedBidResponses": []
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/multislots-simple-banner-inapp.json b/adapters/criteo/criteotest/supplemental/multislots-simple-banner-inapp.json
new file mode 100755
index 00000000..75f4118e
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/multislots-simple-banner-inapp.json
@@ -0,0 +1,213 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "app": {
+        "bundle": "test.app.bundle"
+      },
+      "device": {
+        "ip": "91.199.242.236",
+        "ua": "random user agent",
+        "ifa": "test-ifa-123456",
+        "os": "android"
+      },
+      "imp": [
+        {
+          "id": "test-imp-id-1",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        },
+        {
+          "id": "test-imp-id-2",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 7891011,
+              "networkid": 78910
+            }
+          }
+        },
+        {
+          "id": "test-imp-id-3",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 121314,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": [
+              "91.199.242.236"
+            ],
+            "User-Agent": [
+              "random user agent"
+            ]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "bundleid": "test.app.bundle",
+              "networkid": 78910
+            },
+            "user": {
+              "deviceid": "test-ifa-123456",
+              "deviceos": "android",
+              "deviceidtype": "gaid",
+              "ip": "91.199.242.236",
+              "ua": "random user agent"
+            },
+            "gdprconsent": {},
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id-1",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              },
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id-2",
+                "zoneid": 7891011,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              },
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id-3",
+                "zoneid": 121314,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "slots": [
+              {
+                "id": "test-slot-id-1",
+                "impid": "test-imp-id-1",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "cpm": 0.1,
+                "currency": "USD",
+                "width": 300,
+                "height": 250,
+                "creativeid": "creative-123",
+                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              },
+              {
+                "id": "test-slot-id-2",
+                "impid": "test-imp-id-2",
+                "zoneid": 7891011,
+                "networkid": 78910,
+                "cpm": 0.2,
+                "currency": "USD",
+                "width": 320,
+                "height": 50,
+                "creativeid": "creative-123",
+                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              },
+              {
+                "id": "test-slot-id-3",
+                "impid": "test-imp-id-3",
+                "zoneid": 121314,
+                "networkid": 78910,
+                "cpm": 0.3,
+                "currency": "USD",
+                "width": 300,
+                "height": 600,
+                "creativeid": "creative-123",
+                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              }
+            ]
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "test-slot-id-1",
+              "impid": "test-imp-id-1",
+              "price": 0.1,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 250
+            },
+            "type": "banner"
+          },
+          {
+            "bid": {
+              "id": "test-slot-id-2",
+              "impid": "test-imp-id-2",
+              "price": 0.2,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 320,
+              "h": 50
+            },
+            "type": "banner"
+          },
+          {
+            "bid": {
+              "id": "test-slot-id-3",
+              "impid": "test-imp-id-3",
+              "price": 0.3,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 600
+            },
+            "type": "banner"
+          }
+        ]
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid-different-network-ids.json b/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid-different-network-ids.json
new file mode 100755
index 00000000..dcb51825
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid-different-network-ids.json
@@ -0,0 +1,84 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "site": {
+        "id": "site-id",
+        "page": "criteo.com"
+      },
+      "device": {
+        "os": "android",
+        "ip": "91.199.242.236",
+        "ua": "random user agent"
+      },
+      "user": {
+        "ext": {
+          "eids": [{
+            "source": "criteo.com",
+            "uids": [{
+              "id": "criteo-eid"
+            }]
+          }]
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id-1",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        },
+        {
+          "id": "test-imp-id-2",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 7891011,
+              "networkid": 123456
+            }
+          }
+        },
+        {
+          "id": "test-imp-id-3",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 121314,
+              "networkid": 467890
+            }
+          }
+        }
+      ]
+    },
+      "expectedMakeRequestsErrors": [
+        {
+          "value": "Bid request has slots coming with several network IDs which is not allowed",
+          "comparison": "literal"
+        }
+      ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid.json b/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid.json
new file mode 100755
index 00000000..e5de75e0
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/multislots-simple-banner-uid.json
@@ -0,0 +1,233 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "site": {
+        "id": "site-id",
+        "page": "criteo.com"
+      },
+      "device": {
+        "os": "android",
+        "ip": "91.199.242.236",
+        "ua": "random user agent"
+      },
+      "user": {
+        "ext": {
+          "eids": [{
+            "source": "criteo.com",
+            "uids": [{
+              "id": "criteo-eid"
+            }]
+          }]
+        }
+      },
+      "imp": [
+        {
+          "id": "test-imp-id-1",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 123456,
+              "networkid": 78910
+            }
+          }
+        },
+        {
+          "id": "test-imp-id-2",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 7891011,
+              "networkid": 78910
+            }
+          }
+        },
+        {
+          "id": "test-imp-id-3",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneid": 121314,
+              "networkid": 78910
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://bidder.criteo.com/cdb?profileId=230",
+          "headers": {
+            "X-Forwarded-For": [
+              "91.199.242.236"
+            ],
+            "User-Agent": [
+              "random user agent"
+            ]
+          },
+          "body": {
+            "id": "test-request-id",
+            "publisher": {
+              "siteid": "site-id",
+              "url": "criteo.com",
+              "networkid": 78910
+            },
+            "user": {
+              "ip": "91.199.242.236",
+              "ua": "random user agent",
+              "deviceos": "android",
+              "deviceidtype": "gaid"
+            },
+            "gdprconsent": {},
+            "slots": [
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id-1",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              },
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id-2",
+                "zoneid": 7891011,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              },
+              {
+                "slotid": "00000000-0000-0000-00000000",
+                "impid": "test-imp-id-3",
+                "zoneid": 121314,
+                "networkid": 78910,
+                "sizes": [
+                  "300x250"
+                ]
+              }
+            ],
+            "eids": [
+              {
+                "source": "criteo.com",
+                "uids": [
+                  {
+                    "id": "criteo-eid"
+                  }
+                ]
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "slots": [
+              {
+                "id": "test-slot-id-1",
+                "impid": "test-imp-id-1",
+                "zoneid": 123456,
+                "networkid": 78910,
+                "cpm": 0.1,
+                "currency": "USD",
+                "width": 300,
+                "height": 250,
+                "creativeid": "creative-123",
+                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              },
+              {
+                "id": "test-slot-id-2",
+                "impid": "test-imp-id-2",
+                "zoneid": 7891011,
+                "networkid": 78910,
+                "cpm": 0.2,
+                "currency": "USD",
+                "width": 320,
+                "height": 50,
+                "creativeid": "creative-123",
+                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              },
+              {
+                "id": "test-slot-id-3",
+                "impid": "test-imp-id-3",
+                "zoneid": 121314,
+                "networkid": 78910,
+                "cpm": 0.3,
+                "currency": "USD",
+                "width": 300,
+                "height": 600,
+                "creativeid": "creative-123",
+                "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+              }
+            ]
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "test-slot-id-1",
+              "impid": "test-imp-id-1",
+              "price": 0.1,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 250
+            },
+            "type": "banner"
+          },
+          {
+            "bid": {
+              "id": "test-slot-id-2",
+              "impid": "test-imp-id-2",
+              "price": 0.2,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 320,
+              "h": 50
+            },
+            "type": "banner"
+          },
+          {
+            "bid": {
+              "id": "test-slot-id-3",
+              "impid": "test-imp-id-3",
+              "price": 0.3,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 600
+            },
+            "type": "banner"
+          }
+        ]
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats-not-included.json b/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats-not-included.json
new file mode 100755
index 00000000..a0cc53d0
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats-not-included.json
@@ -0,0 +1,132 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "id": "site-id",
+      "page": "criteo.com"
+    },
+    "device": {
+      "os": "android",
+      "ip": "91.199.242.236",
+      "ua": "random user agent"
+    },
+    "user": {
+      "ext": {
+        "eids": [{
+          "source": "criteo.com",
+          "uids": [{
+            "id": "criteo-eid"
+          }]
+        }]
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250,
+          "format": [
+            {
+              "w": 250,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "zoneid": 123456,
+            "networkid": 78910
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bidder.criteo.com/cdb?profileId=230",
+        "headers": {
+          "X-Forwarded-For": ["91.199.242.236"],
+          "User-Agent": ["random user agent"]
+        },
+        "body": {
+          "id": "test-request-id",
+          "publisher": {
+            "siteid": "site-id",
+            "url": "criteo.com",
+            "networkid": 78910
+          },
+          "user": {
+            "ip": "91.199.242.236",
+            "ua": "random user agent",
+            "deviceos": "android",
+            "deviceidtype": "gaid"
+          },
+          "gdprconsent": {},
+          "slots": [
+            {
+              "slotid": "00000000-0000-0000-00000000",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "sizes": [
+                "250x250"
+              ]
+            }
+          ],
+          "eids": [
+            {
+              "source": "criteo.com",
+              "uids": [
+                {
+                  "id": "criteo-eid"
+                }
+              ]
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "slots": [
+            {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "cpm": 0.1,
+              "currency": "USD",
+              "width": 300,
+              "height": 250,
+              "creativeid": "creative-123",
+              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+            }
+          ]
+        }
+      }
+    }
+  ],
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "price": 0.1,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 250
+            },
+            "type": "banner"
+          }
+        ]
+      }
+  ]
+}
+
diff --git a/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats.json b/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats.json
new file mode 100755
index 00000000..016c16a8
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size-and-formats.json
@@ -0,0 +1,132 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "id": "site-id",
+      "page": "criteo.com"
+    },
+    "device": {
+      "os": "android",
+      "ip": "91.199.242.236",
+      "ua": "random user agent"
+    },
+    "user": {
+      "ext": {
+        "eids": [{
+          "source": "criteo.com",
+          "uids": [{
+            "id": "criteo-eid"
+          }]
+        }]
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250,
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "zoneid": 123456,
+            "networkid": 78910
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bidder.criteo.com/cdb?profileId=230",
+        "headers": {
+          "X-Forwarded-For": ["91.199.242.236"],
+          "User-Agent": ["random user agent"]
+        },
+        "body": {
+          "id": "test-request-id",
+          "publisher": {
+            "siteid": "site-id",
+            "url": "criteo.com",
+            "networkid": 78910
+          },
+          "user": {
+            "ip": "91.199.242.236",
+            "ua": "random user agent",
+            "deviceos": "android",
+            "deviceidtype": "gaid"
+          },
+          "gdprconsent": {},
+          "slots": [
+            {
+              "slotid": "00000000-0000-0000-00000000",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "sizes": [
+                "300x250"
+              ]
+            }
+          ],
+          "eids": [
+            {
+              "source": "criteo.com",
+              "uids": [
+                {
+                  "id": "criteo-eid"
+                }
+              ]
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "slots": [
+            {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "cpm": 0.1,
+              "currency": "USD",
+              "width": 300,
+              "height": 250,
+              "creativeid": "creative-123",
+              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+            }
+          ]
+        }
+      }
+    }
+  ],
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "price": 0.1,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 250
+            },
+            "type": "banner"
+          }
+        ]
+      }
+  ]
+}
+
diff --git a/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size.json b/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size.json
new file mode 100755
index 00000000..68e45da9
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/simple-banner-with-direct-size.json
@@ -0,0 +1,126 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "id": "site-id",
+      "page": "criteo.com"
+    },
+    "device": {
+      "os": "android",
+      "ip": "91.199.242.236",
+      "ua": "random user agent"
+    },
+    "user": {
+      "ext": {
+        "eids": [{
+          "source": "criteo.com",
+          "uids": [{
+            "id": "criteo-eid"
+          }]
+        }]
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "zoneid": 123456,
+            "networkid": 78910
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bidder.criteo.com/cdb?profileId=230",
+        "headers": {
+          "X-Forwarded-For": ["91.199.242.236"],
+          "User-Agent": ["random user agent"]
+        },
+        "body": {
+          "id": "test-request-id",
+          "publisher": {
+            "siteid": "site-id",
+            "url": "criteo.com",
+            "networkid": 78910
+          },
+          "user": {
+            "ip": "91.199.242.236",
+            "ua": "random user agent",
+            "deviceos": "android",
+            "deviceidtype": "gaid"
+          },
+          "gdprconsent": {},
+          "slots": [
+            {
+              "slotid": "00000000-0000-0000-00000000",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "sizes": [
+                "300x250"
+              ]
+            }
+          ],
+          "eids": [
+            {
+              "source": "criteo.com",
+              "uids": [
+                {
+                  "id": "criteo-eid"
+                }
+              ]
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "slots": [
+            {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "cpm": 0.1,
+              "currency": "USD",
+              "width": 300,
+              "height": 250,
+              "creativeid": "creative-123",
+              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+            }
+          ]
+        }
+      }
+    }
+  ],
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "price": 0.1,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 250
+            },
+            "type": "banner"
+          }
+        ]
+      }
+  ]
+}
+
diff --git a/adapters/criteo/criteotest/supplemental/simple-banner-with-ipv6.json b/adapters/criteo/criteotest/supplemental/simple-banner-with-ipv6.json
new file mode 100755
index 00000000..e248db9b
--- /dev/null
+++ b/adapters/criteo/criteotest/supplemental/simple-banner-with-ipv6.json
@@ -0,0 +1,126 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "id": "site-id",
+      "page": "criteo.com"
+    },
+    "device": {
+      "os": "android",
+      "ipv6": "fd36:ce97:0fa1:dec0:0000:0000:0000:0000",
+      "ua": "random user agent"
+    },
+    "user": {
+      "ext": {
+        "eids": [{
+          "source": "criteo.com",
+          "uids": [{
+            "id": "criteo-eid"
+          }]
+        }]
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "zoneid": 123456,
+            "networkid": 78910
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bidder.criteo.com/cdb?profileId=230",
+        "headers": {
+          "X-Forwarded-For": ["fd36:ce97:0fa1:dec0:0000:0000:0000:0000"],
+          "User-Agent": ["random user agent"]
+        },
+        "body": {
+          "id": "test-request-id",
+          "publisher": {
+            "siteid": "site-id",
+            "url": "criteo.com",
+            "networkid": 78910
+          },
+          "user": {
+            "ipv6": "fd36:ce97:0fa1:dec0:0000:0000:0000:0000",
+            "ua": "random user agent",
+            "deviceos": "android",
+            "deviceidtype": "gaid"
+          },
+          "gdprconsent": {},
+          "slots": [
+            {
+              "slotid": "00000000-0000-0000-00000000",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "sizes": [
+                "300x250"
+              ]
+            }
+          ],
+          "eids": [
+            {
+              "source": "criteo.com",
+              "uids": [
+                {
+                  "id": "criteo-eid"
+                }
+              ]
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "slots": [
+            {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "zoneid": 123456,
+              "networkid": 78910,
+              "cpm": 0.1,
+              "currency": "USD",
+              "width": 300,
+              "height": 250,
+              "creativeid": "creative-123",
+              "creative": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>"
+            }
+          ]
+        }
+      }
+    }
+  ],
+    "expectedBidResponses": [
+      {
+        "currency": "USD",
+        "bids": [
+          {
+            "bid": {
+              "id": "test-slot-id",
+              "impid": "test-imp-id",
+              "price": 0.1,
+              "crid": "creative-123",
+              "adm": "<iframe id='789abc' name='789abc' src='http://creative-url.criteo.com'></iframe>",
+              "w": 300,
+              "h": 250
+            },
+            "type": "banner"
+          }
+        ]
+      }
+  ]
+}
+
diff --git a/adapters/criteo/models_test.go b/adapters/criteo/models_test.go
new file mode 100644
index 00000000..9da7bda2
--- /dev/null
+++ b/adapters/criteo/models_test.go
@@ -0,0 +1,452 @@
+package criteo
+
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+
+	"github.com/mxmCherry/openrtb/v14/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestGetDeviceType(t *testing.T) {
+
+	// Setup:
+	deviceTypeCases := []struct {
+		deviceType string
+		expected   string
+	}{
+		{"ios", "idfa"},
+		{"Ios", "idfa"},
+		{"IOS", "idfa"},
+		{"android", "gaid"},
+		{"unknown", "unknown"},
+		{"", "unknown"},
+		{"qwerty", "unknown"},
+		{"qWerty", "unknown"},
+		{"abc", "unknown"},
+	}
+
+	for _, uc := range deviceTypeCases {
+		// Execute:
+		result := getDeviceType(uc.deviceType)
+
+		// Verify:
+		if uc.expected != result {
+			t.Errorf("Bad getDeviceType for '%s'. Expected: %s, got %s", uc.deviceType, uc.expected, result)
+		}
+	}
+}
+
+func TestNewCriteoRequest(t *testing.T) {
+	// Setup:
+	var (
+		dummyRequestID         = "random request ID"
+		dummyPublisherBundleID = "bundleid"
+		dummyPublisherURL      = "test.com"
+		dummyPublisherSiteID   = "siteid"
+		dummyUserDeviceID      = "random-device-id"
+		dummyUserDeviceOS      = "android"
+		dummyUserDeviceIDType  = "gaid"
+		dummyUserCookieID      = "random-cookie-id"
+		dummyUserIP            = "1.1.1.1"
+		dummyUserUA            = "random UA"
+		dummyGdprApplies       = true
+		dummyGdprAppliesUint   = int8(1)
+		dummyGdprConsentData   = "randomconsentdata"
+		dummySlotID            = "11111111-1111-1111-11111111"
+		dummySlotImpID         = "fake-imp-id-1"
+		dummySlotZoneID        = int64(1)
+	)
+
+	fakeSlotIDGenerator := newFakeGuidGenerator(dummySlotID)
+
+	// The request doesn't make any sense but aims to fill every single criteo request fields
+	expectedCriteoRequest := criteoRequest{
+		ID: dummyRequestID,
+		Publisher: criteoPublisher{
+			SiteID:   dummyPublisherSiteID,
+			BundleID: dummyPublisherBundleID,
+			URL:      dummyPublisherURL,
+		},
+		User: criteoUser{
+			DeviceID:     dummyUserDeviceID,
+			DeviceOS:     dummyUserDeviceOS,
+			DeviceIDType: dummyUserDeviceIDType,
+			CookieID:     dummyUserCookieID,
+			IP:           dummyUserIP,
+			UA:           dummyUserUA,
+		},
+		GdprConsent: criteoGdprConsent{
+			GdprApplies: &dummyGdprApplies,
+			ConsentData: dummyGdprConsentData,
+		},
+		Slots: []criteoRequestSlot{
+			{
+				SlotID: dummySlotID,
+				ImpID:  dummySlotImpID,
+				ZoneID: &dummySlotZoneID,
+			},
+		},
+	}
+
+	userExtJSON, _ := json.Marshal(&openrtb_ext.ExtUser{
+		Consent: dummyGdprConsentData,
+	})
+	regsExtJSON, _ := json.Marshal(&openrtb_ext.ExtRegs{
+		GDPR: &dummyGdprAppliesUint,
+	})
+	bidderExtJSON, _ := json.Marshal(&openrtb_ext.ExtImpCriteo{
+		ZoneID: dummySlotZoneID,
+	})
+	impExtJSON, _ := json.Marshal(&adapters.ExtImpBidder{
+		Bidder: bidderExtJSON,
+	})
+	incomingRequest := &openrtb2.BidRequest{
+		ID: dummyRequestID,
+		App: &openrtb2.App{
+			Bundle: dummyPublisherBundleID,
+		},
+		Site: &openrtb2.Site{
+			ID:   dummyPublisherSiteID,
+			Page: dummyPublisherURL,
+		},
+		User: &openrtb2.User{
+			BuyerUID: dummyUserCookieID,
+			Ext:      userExtJSON,
+		},
+		Regs: &openrtb2.Regs{
+			Ext: regsExtJSON,
+		},
+		Device: &openrtb2.Device{
+			IFA: dummyUserDeviceID,
+			OS:  dummyUserDeviceOS,
+			IP:  dummyUserIP,
+			UA:  dummyUserUA,
+		},
+		Imp: []openrtb2.Imp{
+			{
+				ID:  dummySlotImpID,
+				Ext: impExtJSON,
+			},
+		},
+	}
+
+	// Execute:
+	result, err := newCriteoRequest(fakeSlotIDGenerator, incomingRequest)
+
+	// Verify:
+	if err != nil {
+		t.Errorf("newCriteoRequest has errors: %s", err)
+	}
+
+	if expectedCriteoRequest.ID != result.ID ||
+		!reflect.DeepEqual(expectedCriteoRequest, result) ||
+		!reflect.DeepEqual(expectedCriteoRequest.Publisher, result.Publisher) ||
+		!reflect.DeepEqual(expectedCriteoRequest.User, result.User) ||
+		!reflect.DeepEqual(expectedCriteoRequest.GdprConsent, result.GdprConsent) ||
+		len(expectedCriteoRequest.Slots) != len(result.Slots) ||
+		!reflect.DeepEqual(expectedCriteoRequest.Slots[0], result.Slots[0]) {
+		actualResultJSON, _ := json.Marshal(result)
+		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
+		t.Errorf("newCriteoRequest was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
+	}
+}
+
+func TestGetGdprConsent(t *testing.T) {
+	// Setup:
+	var (
+		dummyGdprApplies     = true
+		dummyGdprConsentData = "randomconsentdata"
+		dummyGdprAppliesUint = int8(1)
+	)
+
+	expectedCriteoRequest := criteoRequest{
+		GdprConsent: criteoGdprConsent{
+			GdprApplies: &dummyGdprApplies,
+			ConsentData: dummyGdprConsentData,
+		},
+	}
+
+	userExtJSON, _ := json.Marshal(&openrtb_ext.ExtUser{
+		Consent: dummyGdprConsentData,
+	})
+	regsExtJSON, _ := json.Marshal(&openrtb_ext.ExtRegs{
+		GDPR: &dummyGdprAppliesUint,
+	})
+	incomingRequest := &openrtb2.BidRequest{
+		User: &openrtb2.User{
+			Ext: userExtJSON,
+		},
+		Regs: &openrtb2.Regs{
+			Ext: regsExtJSON,
+		},
+	}
+
+	var regsExt *openrtb_ext.ExtRegs
+	if incomingRequest.Regs != nil {
+		json.Unmarshal(incomingRequest.Regs.Ext, &regsExt)
+	}
+
+	// Execute:
+	gdprConsent, _ := newCriteoGdprConsent(incomingRequest.User, regsExt)
+	result := criteoRequest{
+		GdprConsent: gdprConsent,
+	}
+
+	// Verify:
+	if !reflect.DeepEqual(expectedCriteoRequest, result) {
+		actualResultJSON, _ := json.Marshal(result)
+		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
+		t.Errorf("getGdprConsent was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
+	}
+}
+
+func TestGetUser(t *testing.T) {
+	// Setup:
+	var (
+		dummyUserDeviceID     = "random-device-id"
+		dummyUserDeviceOS     = "android"
+		dummyUserDeviceIDType = "gaid"
+		dummyUserCookieID     = "random-cookie-id"
+		dummyUserIP           = "1.1.1.1"
+		dummyUserUA           = "random UA"
+		dummyCcpaString       = "1YYY"
+	)
+	expectedCriteoRequest := &criteoRequest{
+		User: criteoUser{
+			DeviceID:     dummyUserDeviceID,
+			DeviceOS:     dummyUserDeviceOS,
+			DeviceIDType: dummyUserDeviceIDType,
+			CookieID:     dummyUserCookieID,
+			IP:           dummyUserIP,
+			UA:           dummyUserUA,
+			UspIab:       dummyCcpaString,
+		},
+	}
+
+	regsExt := &openrtb_ext.ExtRegs{
+		USPrivacy: dummyCcpaString,
+	}
+	regsExtData, err := json.Marshal(regsExt)
+	if err != nil {
+		t.Errorf("cannot marshal regsExt data")
+	}
+
+	incomingRequest := &openrtb2.BidRequest{
+		User: &openrtb2.User{
+			BuyerUID: dummyUserCookieID,
+		},
+		Device: &openrtb2.Device{
+			IFA: dummyUserDeviceID,
+			OS:  dummyUserDeviceOS,
+			IP:  dummyUserIP,
+			UA:  dummyUserUA,
+		},
+		Regs: &openrtb2.Regs{
+			Ext: regsExtData,
+		},
+	}
+
+	// Execute:
+	result := &criteoRequest{
+		User: newCriteoUser(incomingRequest.User, incomingRequest.Device, regsExt),
+	}
+
+	// Verify:
+	if !reflect.DeepEqual(expectedCriteoRequest.User, result.User) {
+		actualResultJSON, _ := json.Marshal(result)
+		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
+		t.Errorf("getUser was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
+	}
+}
+
+func TestGetUser_NilUserAndDevice(t *testing.T) {
+	// Setup:
+	var dummyCcpaString = "1YYY"
+
+	expectedCriteoRequest := &criteoRequest{}
+
+	regsExt := &openrtb_ext.ExtRegs{
+		USPrivacy: dummyCcpaString,
+	}
+	regsExtData, err := json.Marshal(regsExt)
+	if err != nil {
+		t.Errorf("cannot marshal regsExt data")
+	}
+
+	incomingRequest := &openrtb2.BidRequest{
+		Regs: &openrtb2.Regs{
+			Ext: regsExtData,
+		},
+	}
+
+	// Execute:
+	result := &criteoRequest{
+		User: newCriteoUser(incomingRequest.User, incomingRequest.Device, regsExt),
+	}
+
+	// Verify:
+	if !reflect.DeepEqual(expectedCriteoRequest.User, result.User) {
+		actualResultJSON, _ := json.Marshal(result)
+		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
+		t.Errorf("getUser was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
+	}
+}
+
+func TestPublisher(t *testing.T) {
+	// Setup:
+	var (
+		dummyPublisherSiteID         = "siteid"
+		dummyPublisherBundleID       = "bundleid"
+		dummyPublisherURL            = "test.com"
+		dummyNetworkID         int64 = 1234567
+	)
+	expectedCriteoRequest := &criteoRequest{
+		Publisher: criteoPublisher{
+			SiteID:    dummyPublisherSiteID,
+			BundleID:  dummyPublisherBundleID,
+			URL:       dummyPublisherURL,
+			NetworkID: &dummyNetworkID,
+		},
+	}
+
+	incomingRequest := &openrtb2.BidRequest{
+		App: &openrtb2.App{
+			Bundle: dummyPublisherBundleID,
+		},
+		Site: &openrtb2.Site{
+			ID:   dummyPublisherSiteID,
+			Page: dummyPublisherURL,
+		},
+	}
+
+	// Execute:
+	result := &criteoRequest{
+		Publisher: newCriteoPublisher(&dummyNetworkID, incomingRequest.App, incomingRequest.Site),
+	}
+
+	// Verify:
+	if !reflect.DeepEqual(expectedCriteoRequest.Publisher, result.Publisher) {
+		actualResultJSON, _ := json.Marshal(result)
+		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
+		t.Errorf("getPublisher was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
+	}
+}
+
+func TestGetRequestSlots(t *testing.T) {
+	// Setup:
+	var (
+		dummySlotImpID  = "fake-imp-id-1"
+		dummySlotZoneID = int64(1)
+		dummySlotID     = "22222222-2222-2222-22222222"
+	)
+
+	fakeSlotIDGenerator := newFakeGuidGenerator(dummySlotID)
+
+	expectedCriteoRequest := &criteoRequest{
+		Slots: []criteoRequestSlot{
+			{
+				SlotID: dummySlotID,
+				ImpID:  dummySlotImpID,
+				ZoneID: &dummySlotZoneID,
+			},
+		},
+	}
+
+	bidderExtJSON, _ := json.Marshal(&openrtb_ext.ExtImpCriteo{
+		ZoneID: dummySlotZoneID,
+	})
+	impExtJSON, _ := json.Marshal(&adapters.ExtImpBidder{
+		Bidder: bidderExtJSON,
+	})
+	incomingRequest := &openrtb2.BidRequest{
+		Imp: []openrtb2.Imp{
+			{
+				ID:  dummySlotImpID,
+				Ext: impExtJSON,
+			},
+		},
+	}
+
+	// Execute:
+	slots, err := newCriteoRequestSlots(fakeSlotIDGenerator, incomingRequest.Imp)
+	result := &criteoRequest{
+		Slots: slots,
+	}
+
+	// Verify:
+	if err != nil {
+		t.Errorf("newCriteoRequestSlots has errors: %s", err)
+	}
+
+	if len(expectedCriteoRequest.Slots) != len(result.Slots) ||
+		!reflect.DeepEqual(expectedCriteoRequest.Slots[0], result.Slots[0]) {
+		actualResultJSON, _ := json.Marshal(result)
+		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
+		t.Errorf("newCriteoRequest was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
+	}
+}
+
+func TestGetRequestMultipleSlots(t *testing.T) {
+	// Setup:
+	dummySlots := []struct {
+		ID     string
+		ZoneID int64
+	}{
+		{"fake-imp-id-1", 1},
+		{"fake-imp-id-2", 2},
+		{"fake-imp-id-3", 3},
+		{"fake-imp-id-4", 4},
+		{"fake-imp-id-5", 5},
+	}
+
+	incomingRequest := &openrtb2.BidRequest{
+		Imp: make([]openrtb2.Imp, len(dummySlots)),
+	}
+	slots := make([]criteoRequestSlot, len(dummySlots))
+
+	for i := range dummySlots {
+		// Build expected slots
+		slots[i] = criteoRequestSlot{
+			ImpID:  dummySlots[i].ID,
+			ZoneID: &dummySlots[i].ZoneID,
+		}
+
+		// Build incoming request imps
+		bidderExtJSON, _ := json.Marshal(&openrtb_ext.ExtImpCriteo{
+			ZoneID: dummySlots[i].ZoneID,
+		})
+		impExtJSON, _ := json.Marshal(&adapters.ExtImpBidder{
+			Bidder: bidderExtJSON,
+		})
+		incomingRequest.Imp[i] = openrtb2.Imp{
+			ID:  dummySlots[i].ID,
+			Ext: impExtJSON,
+		}
+	}
+
+	expectedCriteoRequestSlots, err := newCriteoRequestSlots(newFakeGuidGenerator(""), incomingRequest.Imp)
+	expectedCriteoRequest := &criteoRequest{
+		Slots: expectedCriteoRequestSlots,
+	}
+
+	// Execute:
+	slotsResult, err := newCriteoRequestSlots(newFakeGuidGenerator(""), incomingRequest.Imp)
+	result := &criteoRequest{
+		Slots: slotsResult,
+	}
+
+	// Verify:
+	if err != nil {
+		t.Errorf("newCriteoRequestSlots has errors: %s", err)
+	}
+
+	if len(expectedCriteoRequest.Slots) != len(result.Slots) ||
+		!reflect.DeepEqual(expectedCriteoRequest.Slots, result.Slots) {
+		actualResultJSON, _ := json.Marshal(result)
+		expectedResultJSON, _ := json.Marshal(expectedCriteoRequest)
+		t.Errorf("newCriteoRequest was incorrect, got '%s', want '%s'.", actualResultJSON, expectedResultJSON)
+	}
+}
diff --git a/adapters/criteo/params_test.go b/adapters/criteo/params_test.go
new file mode 100644
index 00000000..73ace617
--- /dev/null
+++ b/adapters/criteo/params_test.go
@@ -0,0 +1,63 @@
+package criteo
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/criteo.json
+//
+// These also validate the format of the external API: request.imp[i].ext.criteo
+
+// TestValidParams makes sure that the criteo 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.BidderCriteo, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected criteo params: %s", validParam)
+		}
+	}
+}
+
+// TestInvalidParams makes sure that the criteo 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.BidderCriteo, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"zoneid": 123456}`,
+	`{"networkid": 78910}`,
+	`{"zoneid": 123456, "networkid": 78910}`,
+	`{"zoneid": 0, "networkid": 0}`,
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{"zoneid": -123}`,
+	`{"networkid": -321}`,
+	`{"zoneid": -123, "networkid": -321}`,
+	`{"zoneid": -1}`,
+	`{"networkid": -1}`,
+	`{"zoneid": -1, "networkid": -1}`,
+}
diff --git a/adapters/criteo/usersync_test.go b/adapters/criteo/usersync_test.go
new file mode 100644
index 00000000..9010d8bb
--- /dev/null
+++ b/adapters/criteo/usersync_test.go
@@ -0,0 +1,26 @@
+package criteo
+
+import (
+	"testing"
+	"text/template"
+
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/privacy"
+	"github.com/prebid/prebid-server/privacy/gdpr"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestCriteoSyncer(t *testing.T) {
+	url := "//prebidserver/getuid?https%3A%2F%2Fprebidserver%2Fpbs%2Fv1%2Fsetuid%3Fbidder%3Dcriteo%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID"
+	temp := template.Must(template.New("sync-template").Parse(url))
+	syncer := NewCriteoSyncer(temp)
+	info, err := syncer.GetUsersyncInfo(privacy.Policies{
+		GDPR: gdpr.Policy{
+			Signal: "1",
+		},
+	})
+	assert.NoError(t, err)
+	assert.EqualValues(t, adapters.SyncTypeRedirect, info.Type)
+	assert.Equal(t, false, info.SupportCORS)
+	assert.Equal(t, "//prebidserver/getuid?https%3A%2F%2Fprebidserver%2Fpbs%2Fv1%2Fsetuid%3Fbidder%3Dcriteo%26gdpr%3D1%26gdpr_consent%3D%26uid%3D%24UID", info.URL)
+}
diff --git a/adapters/dmx/dmxtest/exemplary/idfa-to-app-id.json b/adapters/dmx/dmxtest/exemplary/idfa-to-app-id.json
new file mode 100644
index 00000000..2fcf9179
--- /dev/null
+++ b/adapters/dmx/dmxtest/exemplary/idfa-to-app-id.json
@@ -0,0 +1,148 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app":{
+      "bundle":"302324249",
+      "name":"Skout - iOS Match Buy",
+      "publisher":{
+        "id":"10400"
+      },
+      "storeurl":"https://itunes.apple.com/app/id302324249"
+    },
+    "device": {
+      "ifa": "ed6207cefff74c14878963566683c070"
+    },
+    "imp": [
+      {
+
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250,
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "dmxid": "123454",
+            "publisher_id": "10400"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "",
+        "body": {
+          "id": "test-request-id",
+          "app":{
+            "bundle":"302324249",
+            "id":"ed6207cefff74c14878963566683c070",
+            "name":"Skout - iOS Match Buy",
+            "publisher":{
+              "id":"10400",
+              "ext": {
+                "dmx": {
+                  "id": "10400"
+                }
+              }
+            },
+            "storeurl":"https://itunes.apple.com/app/id302324249"
+          },
+          "device": {
+            "ifa": "ed6207cefff74c14878963566683c070"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "tagid": "123454",
+              "secure": 1,
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ],
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "publisher_id": "10400",
+                  "dmxid": "123454"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "958",
+              "bid": [{
+                "id": "7706636740145184841",
+                "impid": "test-imp-id",
+                "price": 1.75,
+                "adid": "29681110",
+                "adm": "<div>banner-ads</div>",
+                "adomain": ["dmx.districtm.io"],
+                "iurl": "https://dmx.districtm.io/b/v2",
+                "cid": "958",
+                "crid": "29681110",
+                "h": 250,
+                "w": 300
+              }]
+            }
+          ],
+          "bidid": "5778926625248726496",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7706636740145184841",
+            "impid": "test-imp-id",
+            "price": 1.75,
+            "adm": "<div>banner-ads</div>",
+            "adid": "29681110",
+            "adomain": ["dmx.districtm.io"],
+            "iurl": "https://dmx.districtm.io/b/v2",
+            "cid": "958",
+            "crid": "29681110",
+            "w": 300,
+            "h": 250
+
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/dmx/dmxtest/exemplary/missing-width-height.json b/adapters/dmx/dmxtest/exemplary/missing-width-height.json
new file mode 100644
index 00000000..b7eeec68
--- /dev/null
+++ b/adapters/dmx/dmxtest/exemplary/missing-width-height.json
@@ -0,0 +1,143 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app":{
+      "bundle":"302324249",
+      "id":"ed6207cefff74c14878963566683c070",
+      "name":"Skout - iOS Match Buy",
+      "publisher":{
+        "id":"10400"
+      },
+      "storeurl":"https://itunes.apple.com/app/id302324249"
+    },
+    "imp": [
+      {
+        "bidfloor": 0.35,
+        "id": "test-imp-id",
+        "banner": {
+
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "dmxid": "123454",
+            "publisher_id": "10400"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "",
+        "body": {
+          "id": "test-request-id",
+          "app":{
+            "bundle":"302324249",
+            "id":"ed6207cefff74c14878963566683c070",
+            "name":"Skout - iOS Match Buy",
+            "publisher":{
+              "id":"10400",
+              "ext": {
+                "dmx": {
+                  "id": "10400"
+                }
+              }
+            },
+            "storeurl":"https://itunes.apple.com/app/id302324249"
+          },
+          "imp": [
+            {
+              "bidfloor": 0.35,
+              "id": "test-imp-id",
+              "tagid": "123454",
+              "secure": 1,
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ],
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "publisher_id": "10400",
+                  "dmxid": "123454"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "958",
+              "bid": [{
+                "id": "7706636740145184841",
+                "impid": "test-imp-id",
+                "price": 1.75,
+                "adid": "29681110",
+                "adm": "<div>banner-ads</div>",
+                "adomain": ["dmx.districtm.io"],
+                "iurl": "https://dmx.districtm.io/b/v2",
+                "cid": "958",
+                "crid": "29681110",
+                "h": 250,
+                "w": 300
+              }]
+            }
+          ],
+          "bidid": "5778926625248726496",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7706636740145184841",
+            "impid": "test-imp-id",
+            "price": 1.75,
+            "adm": "<div>banner-ads</div>",
+            "adid": "29681110",
+            "adomain": ["dmx.districtm.io"],
+            "iurl": "https://dmx.districtm.io/b/v2",
+            "cid": "958",
+            "crid": "29681110",
+            "w": 300,
+            "h": 250
+
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index 8cc5b5e9..41ca420a 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -71,7 +71,6 @@ func TestSingleBidder(t *testing.T) {
 	ctx = context.WithValue(ctx, DebugContextKey, true)
 
 	for _, test := range testCases {
-
 		mockBidderResponse := &adapters.BidderResponse{
 			Bids: []*adapters.TypedBid{
 				{
@@ -142,6 +141,48 @@ func TestSingleBidder(t *testing.T) {
 	}
 }
 
+func TestRequestBidRemovesSensitiveHeaders(t *testing.T) {
+	server := httptest.NewServer(mockHandler(200, "getBody", "responseJson"))
+	defer server.Close()
+
+	requestHeaders := http.Header{}
+	requestHeaders.Add("Content-Type", "application/json")
+	requestHeaders.Add("Authorization", "anySecret")
+
+	bidderImpl := &goodSingleBidder{
+		httpRequest: &adapters.RequestData{
+			Method:  "POST",
+			Uri:     server.URL,
+			Body:    []byte("requestJson"),
+			Headers: requestHeaders,
+		},
+		bidResponse: &adapters.BidderResponse{
+			Bids: []*adapters.TypedBid{},
+		},
+	}
+
+	debugInfo := &config.DebugInfo{Allow: true}
+	ctx := context.Background()
+	ctx = context.WithValue(ctx, DebugContextKey, true)
+
+	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, debugInfo)
+	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
+	seatBid, errs := bidder.requestBid(ctx, &openrtb2.BidRequest{}, "test", 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
+
+	expectedHttpCalls := []*openrtb_ext.ExtHttpCall{
+		{
+			Uri:            server.URL,
+			RequestBody:    "requestJson",
+			RequestHeaders: map[string][]string{"Content-Type": {"application/json"}},
+			ResponseBody:   "responseJson",
+			Status:         200,
+		},
+	}
+
+	assert.Empty(t, errs)
+	assert.ElementsMatch(t, seatBid.httpCalls, expectedHttpCalls)
+}
+
 // TestMultiBidder makes sure all the requests get sent, and the responses processed.
 // Because this is done in parallel, it should be run under the race detector.
 func TestMultiBidder(t *testing.T) {
@@ -891,86 +932,204 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) {
 	}
 }
 
-// TestBadResponseLogging makes sure that openrtb_ext works properly on malformed HTTP requests.
-func TestBadRequestLogging(t *testing.T) {
-	info := &httpCallInfo{
-		err: errors.New("Bad request"),
-	}
-	ext := makeExt(info)
-	if ext.Uri != "" {
-		t.Errorf("The URI should be empty. Got %s", ext.Uri)
-	}
-	if ext.RequestBody != "" {
-		t.Errorf("The request body should be empty. Got %s", ext.RequestBody)
-	}
-	if ext.ResponseBody != "" {
-		t.Errorf("The response body should be empty. Got %s", ext.ResponseBody)
-	}
-	if ext.Status != 0 {
-		t.Errorf("The Status code should be 0. Got %d", ext.Status)
-	}
-	if len(ext.RequestHeaders) > 0 {
-		t.Errorf("The request headers should be empty. Got %s", ext.RequestHeaders)
-	}
-}
-
-// TestBadResponseLogging makes sure that openrtb_ext works properly if we don't get a sensible HTTP response.
-func TestBadResponseLogging(t *testing.T) {
-	info := &httpCallInfo{
-		request: &adapters.RequestData{
-			Uri:  "test.com",
-			Body: []byte("request body"),
-			Headers: http.Header{
-				"header-1": []string{"value-1"},
+func TestMakeExt(t *testing.T) {
+	testCases := []struct {
+		description string
+		given       *httpCallInfo
+		expected    *openrtb_ext.ExtHttpCall
+	}{
+		{
+			description: "Nil",
+			given:       nil,
+			expected:    &openrtb_ext.ExtHttpCall{},
+		},
+		{
+			description: "Empty",
+			given: &httpCallInfo{
+				err:      nil,
+				response: nil,
+				request:  nil,
 			},
+			expected: &openrtb_ext.ExtHttpCall{},
+		},
+		{
+			description: "Request & Response - No Error",
+			given: &httpCallInfo{
+				err: nil,
+				request: &adapters.RequestData{
+					Uri:     "requestUri",
+					Body:    []byte("requestBody"),
+					Headers: makeHeader(map[string][]string{"Key1": {"value1", "value2"}}),
+				},
+				response: &adapters.ResponseData{
+					Body:       []byte("responseBody"),
+					StatusCode: 999,
+				},
+			},
+			expected: &openrtb_ext.ExtHttpCall{
+				Uri:            "requestUri",
+				RequestBody:    "requestBody",
+				RequestHeaders: map[string][]string{"Key1": {"value1", "value2"}},
+				ResponseBody:   "responseBody",
+				Status:         999,
+			},
+		},
+		{
+			description: "Request & Response - No Error with Authorization removal",
+			given: &httpCallInfo{
+				err: nil,
+				request: &adapters.RequestData{
+					Uri:     "requestUri",
+					Body:    []byte("requestBody"),
+					Headers: makeHeader(map[string][]string{"Key1": {"value1", "value2"}, "Authorization": {"secret"}}),
+				},
+				response: &adapters.ResponseData{
+					Body:       []byte("responseBody"),
+					StatusCode: 999,
+				},
+			},
+			expected: &openrtb_ext.ExtHttpCall{
+				Uri:            "requestUri",
+				RequestBody:    "requestBody",
+				RequestHeaders: map[string][]string{"Key1": {"value1", "value2"}},
+				ResponseBody:   "responseBody",
+				Status:         999,
+			},
+		},
+		{
+			description: "Request & Response - No Error with nil header",
+			given: &httpCallInfo{
+				err: nil,
+				request: &adapters.RequestData{
+					Uri:     "requestUri",
+					Body:    []byte("requestBody"),
+					Headers: nil,
+				},
+				response: &adapters.ResponseData{
+					Body:       []byte("responseBody"),
+					StatusCode: 999,
+				},
+			},
+			expected: &openrtb_ext.ExtHttpCall{
+				Uri:            "requestUri",
+				RequestBody:    "requestBody",
+				RequestHeaders: nil,
+				ResponseBody:   "responseBody",
+				Status:         999,
+			},
+		},
+		{
+			description: "Request & Response - Error",
+			given: &httpCallInfo{
+				err: errors.New("error"),
+				request: &adapters.RequestData{
+					Uri:     "requestUri",
+					Body:    []byte("requestBody"),
+					Headers: makeHeader(map[string][]string{"Key1": {"value1", "value2"}}),
+				},
+				response: &adapters.ResponseData{
+					Body:       []byte("responseBody"),
+					StatusCode: 999,
+				},
+			},
+			expected: &openrtb_ext.ExtHttpCall{
+				Uri:            "requestUri",
+				RequestBody:    "requestBody",
+				RequestHeaders: map[string][]string{"Key1": {"value1", "value2"}},
+			},
+		},
+		{
+			description: "Request Only",
+			given: &httpCallInfo{
+				err: nil,
+				request: &adapters.RequestData{
+					Uri:     "requestUri",
+					Body:    []byte("requestBody"),
+					Headers: makeHeader(map[string][]string{"Key1": {"value1", "value2"}}),
+				},
+				response: nil,
+			},
+			expected: &openrtb_ext.ExtHttpCall{
+				Uri:            "requestUri",
+				RequestBody:    "requestBody",
+				RequestHeaders: map[string][]string{"Key1": {"value1", "value2"}},
+			},
+		}, {
+			description: "Response Only",
+			given: &httpCallInfo{
+				err: nil,
+				response: &adapters.ResponseData{
+					Body:       []byte("responseBody"),
+					StatusCode: 999,
+				},
+			},
+			expected: &openrtb_ext.ExtHttpCall{},
 		},
-		err: errors.New("Bad response"),
-	}
-	ext := makeExt(info)
-	if ext.Uri != info.request.Uri {
-		t.Errorf("The URI should be test.com. Got %s", ext.Uri)
-	}
-	if ext.RequestBody != string(info.request.Body) {
-		t.Errorf("The request body should be empty. Got %s", ext.RequestBody)
-	}
-	if ext.ResponseBody != "" {
-		t.Errorf("The response body should be empty. Got %s", ext.ResponseBody)
 	}
-	if ext.Status != 0 {
-		t.Errorf("The Status code should be 0. Got %d", ext.Status)
+
+	for _, test := range testCases {
+		result := makeExt(test.given)
+		assert.Equal(t, test.expected, result, test.description)
 	}
-	assert.Equal(t, info.request.Headers, http.Header(ext.RequestHeaders), "The request headers should be \"header-1:value-1\"")
 }
 
-// TestSuccessfulResponseLogging makes sure that openrtb_ext works properly if the HTTP request is successful.
-func TestSuccessfulResponseLogging(t *testing.T) {
-	info := &httpCallInfo{
-		request: &adapters.RequestData{
-			Uri:  "test.com",
-			Body: []byte("request body"),
-			Headers: http.Header{
-				"header-1": []string{"value-1", "value-2"},
-			},
+func TestFilterHeader(t *testing.T) {
+	testCases := []struct {
+		description string
+		given       http.Header
+		expected    http.Header
+	}{
+		{
+			description: "Nil",
+			given:       nil,
+			expected:    nil,
 		},
-		response: &adapters.ResponseData{
-			StatusCode: 200,
-			Body:       []byte("response body"),
+		{
+			description: "Empty",
+			given:       http.Header{},
+			expected:    http.Header{},
+		},
+		{
+			description: "One",
+			given:       makeHeader(map[string][]string{"Key1": {"value1"}}),
+			expected:    makeHeader(map[string][]string{"Key1": {"value1"}}),
+		},
+		{
+			description: "Many",
+			given:       makeHeader(map[string][]string{"Key1": {"value1"}, "Key2": {"value2a", "value2b"}}),
+			expected:    makeHeader(map[string][]string{"Key1": {"value1"}, "Key2": {"value2a", "value2b"}}),
+		},
+		{
+			description: "Authorization Header Omitted",
+			given:       makeHeader(map[string][]string{"authorization": {"secret"}}),
+			expected:    http.Header{},
+		},
+		{
+			description: "Authorization Header Omitted - Case Insensitive",
+			given:       makeHeader(map[string][]string{"AuThOrIzAtIoN": {"secret"}}),
+			expected:    http.Header{},
+		},
+		{
+			description: "Authorization Header Omitted + Other Keys",
+			given:       makeHeader(map[string][]string{"authorization": {"secret"}, "Key1": {"value1"}}),
+			expected:    makeHeader(map[string][]string{"Key1": {"value1"}}),
 		},
 	}
-	ext := makeExt(info)
-	if ext.Uri != info.request.Uri {
-		t.Errorf("The URI should be test.com. Got %s", ext.Uri)
-	}
-	if ext.RequestBody != string(info.request.Body) {
-		t.Errorf("The request body should be \"request body\". Got %s", ext.RequestBody)
-	}
-	if ext.ResponseBody != string(info.response.Body) {
-		t.Errorf("The response body should be \"response body\". Got %s", ext.ResponseBody)
+
+	for _, test := range testCases {
+		result := filterHeader(test.given)
+		assert.Equal(t, test.expected, result, test.description)
 	}
-	if ext.Status != info.response.StatusCode {
-		t.Errorf("The Status code should be 0. Got %d", ext.Status)
+}
+
+func makeHeader(v map[string][]string) http.Header {
+	h := http.Header{}
+	for key, values := range v {
+		for _, value := range values {
+			h.Add(key, value)
+		}
 	}
-	assert.Equal(t, info.request.Headers, http.Header(ext.RequestHeaders), "The request headers should be \"%s\". Got %s", info.request.Headers, ext.RequestHeaders)
+	return h
 }
 
 func TestMobileNativeTypes(t *testing.T) {
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 5b2e8046..a389a007 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -41,6 +41,7 @@ func TestNewSyncerMap(t *testing.T) {
 			string(openrtb_ext.BidderConsumable):       syncConfig,
 			string(openrtb_ext.BidderConversant):       syncConfig,
 			string(openrtb_ext.BidderCpmstar):          syncConfig,
+			string(openrtb_ext.BidderCriteo):           syncConfig,
 			string(openrtb_ext.BidderDatablocks):       syncConfig,
 			string(openrtb_ext.BidderDmx):              syncConfig,
 			string(openrtb_ext.BidderDeepintent):       syncConfig,
@@ -106,6 +107,7 @@ func TestNewSyncerMap(t *testing.T) {
 		openrtb_ext.BidderAdot:         true,
 		openrtb_ext.BidderAdprime:      true,
 		openrtb_ext.BidderApplogy:      true,
+		openrtb_ext.BidderBidmachine:   true,
 		openrtb_ext.BidderEpom:         true,
 		openrtb_ext.BidderDecenterAds:  true,
 		openrtb_ext.BidderInMobi:       true,