Repository: prebid/prebid-server
Pull request #1769 (2021-03-21 22:24:00)

New Adapter: Bidmachine

meeeeeepo: > Looks good. I left just a few small comments.
> 
> Did you create a docs PR? Please see https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html#user-documentation
Yeah, i just submitted pull request with our documentation
https://github.com/prebid/prebid.github.io/pull/2819

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/config/config.go b/config/config.go
index bc15a947..e22080fe 100644
--- a/config/config.go
+++ b/config/config.go
@@ -829,6 +829,7 @@ 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")
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 46c96706..6c649fde 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -29,6 +29,7 @@ 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"
@@ -137,6 +138,7 @@ 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,
diff --git a/exchange/bidder.go b/exchange/bidder.go
index d6b8028c..5b6da499 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -167,6 +167,10 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb2.B
 		if debugInfo := ctx.Value(DebugContextKey); debugInfo != nil && debugInfo.(bool) {
 			if accountDebugAllowed {
 				if bidder.config.DebugInfo.Allow {
+					// it's safe to mutate the request headers since from this point on the
+					// information is only used for debugging.
+					removeSensitiveHeaders(httpInfo.request.Headers)
+
 					seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
 				} else {
 					debugDisabledWarning := errortypes.Warning{
@@ -325,25 +329,29 @@ 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")
+
+// removeSensitiveHeaders mutates the http header object to remove sensitive information.
+func removeSensitiveHeaders(h http.Header) {
+	h.Del(authorizationHeader)
+}
+
 // 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 = 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..66dcbebc 100755
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -100,6 +100,7 @@ const (
 	BidderBeachfront       BidderName = "beachfront"
 	BidderBeintoo          BidderName = "beintoo"
 	BidderBetween          BidderName = "between"
+	BidderBidmachine       BidderName = "bidmachine"
 	BidderBrightroll       BidderName = "brightroll"
 	BidderColossus         BidderName = "colossus"
 	BidderConnectAd        BidderName = "connectad"
@@ -208,6 +209,7 @@ func CoreBidderNames() []BidderName {
 		BidderBeachfront,
 		BidderBeintoo,
 		BidderBetween,
+		BidderBidmachine,
 		BidderBrightroll,
 		BidderColossus,
 		BidderConnectAd,
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/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-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


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/exchange/bidder_test.go b/exchange/bidder_test.go
index 8cc5b5e9..7c01cd84 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,160 @@ 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 - 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 TestRemoveSensitiveHeaders(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:    map[string][]string{},
+		},
+		{
+			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 {
+		removeSensitiveHeaders(test.given)
+		assert.Equal(t, test.expected, test.given, 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..51ea19d7 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -106,6 +106,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,