Repository: prebid/prebid-server
Pull request #1512 (2020-09-28 09:31:01)

Invibes: Generic domainId parameter
More flexible way to use domainId parameter


Patch:
diff --git a/adapters/invibes/invibes.go b/adapters/invibes/invibes.go
index 1c9baff2..934cf6c1 100644
--- a/adapters/invibes/invibes.go
+++ b/adapters/invibes/invibes.go
@@ -283,7 +283,7 @@ func (a *InvibesAdapter) makeURL(request *openrtb.BidRequest, domainID int) (str
 	} else if domainID == 1001 {
 		host = "bid.videostep.com"
 	} else if domainID >= 1002 {
-		host = "bid2.videostep.com"
+		host = "bid" + strconv.Itoa(domainID-1000) + ".videostep.com"
 	}
 
 	var endpointURL *url.URL
diff --git a/adapters/krushmedia/krushmedia.go b/adapters/krushmedia/krushmedia.go
new file mode 100644
index 00000000..905c788d
--- /dev/null
+++ b/adapters/krushmedia/krushmedia.go
@@ -0,0 +1,186 @@
+package krushmedia
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strconv"
+	"text/template"
+
+	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type KrushmediaAdapter struct {
+	endpoint template.Template
+}
+
+func NewKrushmediaBidder(endpointTemplate string) *KrushmediaAdapter {
+	template, err := template.New("endpointTemplate").Parse(endpointTemplate)
+	if err != nil {
+		return nil
+	}
+	return &KrushmediaAdapter{endpoint: *template}
+}
+
+func getHeaders(request *openrtb.BidRequest) *http.Header {
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	headers.Add("X-Openrtb-Version", "2.5")
+
+	if request.Device != nil {
+		if len(request.Device.UA) > 0 {
+			headers.Add("User-Agent", request.Device.UA)
+		}
+
+		if len(request.Device.IP) > 0 {
+			headers.Add("X-Forwarded-For", request.Device.IP)
+		}
+
+		if len(request.Device.Language) > 0 {
+			headers.Add("Accept-Language", request.Device.Language)
+		}
+
+		if request.Device.DNT != nil {
+			headers.Add("Dnt", strconv.Itoa(int(*request.Device.DNT)))
+		}
+	}
+
+	return &headers
+}
+
+func (a *KrushmediaAdapter) MakeRequests(
+	openRTBRequest *openrtb.BidRequest,
+	reqInfo *adapters.ExtraRequestInfo,
+) (
+	requestsToBidder []*adapters.RequestData,
+	errs []error,
+) {
+
+	request := *openRTBRequest
+
+	var errors []error
+	var krushmediaExt *openrtb_ext.ExtKrushmedia
+	var err error
+
+	if len(request.Imp) > 0 {
+		krushmediaExt, err = a.getImpressionExt(&(request.Imp[0]))
+		if err != nil {
+			errors = append(errors, err)
+		}
+		request.Imp[0].Ext = nil
+	} else {
+		errors = append(errors, &errortypes.BadInput{
+			Message: "Missing Imp Object",
+		})
+	}
+
+	if len(errors) > 0 {
+		return nil, errors
+	}
+
+	url, err := a.buildEndpointURL(krushmediaExt)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	reqJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	return []*adapters.RequestData{{
+		Method:  http.MethodPost,
+		Body:    reqJSON,
+		Uri:     url,
+		Headers: *getHeaders(&request),
+	}}, nil
+}
+
+func (a *KrushmediaAdapter) getImpressionExt(imp *openrtb.Imp) (*openrtb_ext.ExtKrushmedia, error) {
+	var bidderExt adapters.ExtImpBidder
+	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "Bidder extension not provided or can't be unmarshalled",
+		}
+	}
+	var krushmediaExt openrtb_ext.ExtKrushmedia
+	if err := json.Unmarshal(bidderExt.Bidder, &krushmediaExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "Error while unmarshaling bidder extension",
+		}
+	}
+	return &krushmediaExt, nil
+}
+
+func (a *KrushmediaAdapter) buildEndpointURL(params *openrtb_ext.ExtKrushmedia) (string, error) {
+	endpointParams := macros.EndpointTemplateParams{AccountID: params.AccountID}
+	return macros.ResolveMacros(a.endpoint, endpointParams)
+}
+
+func (a *KrushmediaAdapter) MakeBids(
+	openRTBRequest *openrtb.BidRequest,
+	requestToBidder *adapters.RequestData,
+	bidderRawResponse *adapters.ResponseData,
+) (
+	bidderResponse *adapters.BidderResponse,
+	errs []error,
+) {
+	if bidderRawResponse.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if bidderRawResponse.StatusCode == http.StatusBadRequest {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: [ %d ]", bidderRawResponse.StatusCode),
+		}}
+	}
+
+	if bidderRawResponse.StatusCode == http.StatusServiceUnavailable {
+		return nil, nil
+	}
+
+	if bidderRawResponse.StatusCode != http.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", bidderRawResponse.StatusCode),
+		}}
+	}
+
+	responseBody := bidderRawResponse.Body
+	var bidResp openrtb.BidResponse
+	if err := json.Unmarshal(responseBody, &bidResp); err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Bad Server Response",
+		}}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid))
+	sb := bidResp.SeatBid[0]
+
+	for _, bid := range sb.Bid {
+		bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+			Bid:     &bid,
+			BidType: getMediaTypeForImp(bid.ImpID, openRTBRequest.Imp),
+		})
+	}
+	return bidResponse, nil
+}
+
+func getMediaTypeForImp(impId string, imps []openrtb.Imp) openrtb_ext.BidType {
+	mediaType := openrtb_ext.BidTypeBanner
+	for _, imp := range imps {
+		if imp.ID == impId {
+			if imp.Video != nil {
+				mediaType = openrtb_ext.BidTypeVideo
+			} else if imp.Native != nil {
+				mediaType = openrtb_ext.BidTypeNative
+			}
+			return mediaType
+		}
+	}
+	return mediaType
+}
diff --git a/adapters/krushmedia/usersync.go b/adapters/krushmedia/usersync.go
new file mode 100644
index 00000000..5dc1471f
--- /dev/null
+++ b/adapters/krushmedia/usersync.go
@@ -0,0 +1,12 @@
+package krushmedia
+
+import (
+	"text/template"
+
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/usersync"
+)
+
+func NewKrushmediaSyncer(temp *template.Template) usersync.Usersyncer {
+	return adapters.NewSyncer("krushmedia", 0, temp, adapters.SyncTypeRedirect)
+}
diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go
index 56ae7b2f..812095f9 100644
--- a/adapters/rubicon/rubicon.go
+++ b/adapters/rubicon/rubicon.go
@@ -194,12 +194,17 @@ var rubiSizeMap = map[rubiSize]int{
 	{w: 468, h: 60}:    1,
 	{w: 728, h: 90}:    2,
 	{w: 728, h: 91}:    2,
+	{w: 120, h: 90}:    5,
+	{w: 125, h: 125}:   7,
 	{w: 120, h: 600}:   8,
 	{w: 160, h: 600}:   9,
 	{w: 300, h: 600}:   10,
+	{w: 200, h: 200}:   13,
+	{w: 250, h: 250}:   14,
 	{w: 300, h: 250}:   15,
 	{w: 300, h: 251}:   15,
 	{w: 336, h: 280}:   16,
+	{w: 240, h: 400}:   17,
 	{w: 300, h: 100}:   19,
 	{w: 980, h: 120}:   31,
 	{w: 250, h: 360}:   32,
@@ -207,16 +212,23 @@ var rubiSizeMap = map[rubiSize]int{
 	{w: 980, h: 150}:   35,
 	{w: 468, h: 400}:   37,
 	{w: 930, h: 180}:   38,
+	{w: 750, h: 100}:   39,
+	{w: 750, h: 200}:   40,
+	{w: 750, h: 300}:   41,
 	{w: 320, h: 50}:    43,
 	{w: 300, h: 50}:    44,
 	{w: 300, h: 300}:   48,
+	{w: 1024, h: 768}:  53,
 	{w: 300, h: 1050}:  54,
 	{w: 970, h: 90}:    55,
 	{w: 970, h: 250}:   57,
 	{w: 1000, h: 90}:   58,
 	{w: 320, h: 80}:    59,
+	{w: 320, h: 150}:   60,
 	{w: 1000, h: 1000}: 61,
+	{w: 580, h: 500}:   64,
 	{w: 640, h: 480}:   65,
+	{w: 930, h: 600}:   66,
 	{w: 320, h: 480}:   67,
 	{w: 1800, h: 1000}: 68,
 	{w: 320, h: 320}:   72,
@@ -225,17 +237,44 @@ var rubiSizeMap = map[rubiSize]int{
 	{w: 980, h: 300}:   79,
 	{w: 980, h: 400}:   80,
 	{w: 480, h: 300}:   83,
+	{w: 300, h: 120}:   85,
+	{w: 548, h: 150}:   90,
 	{w: 970, h: 310}:   94,
+	{w: 970, h: 100}:   95,
 	{w: 970, h: 210}:   96,
 	{w: 480, h: 320}:   101,
 	{w: 768, h: 1024}:  102,
 	{w: 480, h: 280}:   103,
+	{w: 250, h: 800}:   105,
 	{w: 320, h: 240}:   108,
 	{w: 1000, h: 300}:  113,
 	{w: 320, h: 100}:   117,
 	{w: 800, h: 250}:   125,
 	{w: 200, h: 600}:   126,
+	{w: 980, h: 600}:   144,
+	{w: 980, h: 150}:   145,
+	{w: 1000, h: 250}:  152,
 	{w: 640, h: 320}:   156,
+	{w: 320, h: 250}:   159,
+	{w: 250, h: 600}:   179,
+	{w: 600, h: 300}:   195,
+	{w: 640, h: 360}:   198,
+	{w: 640, h: 200}:   199,
+	{w: 1030, h: 590}:  213,
+	{w: 980, h: 360}:   214,
+	{w: 320, h: 180}:   229,
+	{w: 2000, h: 1400}: 230,
+	{w: 580, h: 400}:   232,
+	{w: 480, h: 820}:   256,
+	{w: 400, h: 600}:   257,
+	{w: 500, h: 200}:   258,
+	{w: 998, h: 200}:   259,
+	{w: 970, h: 1000}:  264,
+	{w: 1920, h: 1080}: 265,
+	{w: 1800, h: 200}:  274,
+	{w: 320, h: 500}:   278,
+	{w: 320, h: 400}:   282,
+	{w: 640, h: 380}:   288,
 }
 
 // defines the contract for bidrequest.user.ext.eids[i].ext
diff --git a/config/config.go b/config/config.go
index dd660992..d17b957a 100755
--- a/config/config.go
+++ b/config/config.go
@@ -212,7 +212,7 @@ type GDPR struct {
 	UsersyncIfAmbiguous     bool         `mapstructure:"usersync_if_ambiguous"`
 	Timeouts                GDPRTimeouts `mapstructure:"timeouts_ms"`
 	NonStandardPublishers   []string     `mapstructure:"non_standard_publishers,flow"`
-	NonStandardPublisherMap map[string]int
+	NonStandardPublisherMap map[string]struct{}
 	TCF1                    TCF1 `mapstructure:"tcf1"`
 	TCF2                    TCF2 `mapstructure:"tcf2"`
 	AMPException            bool `mapstructure:"amp_exception"`
@@ -220,7 +220,8 @@ type GDPR struct {
 	// If the gdpr flag is unset in a request, but geo.country is set, we will assume GDPR applies if and only
 	// if the country matches one on this list. If both the GDPR flag and country are not set, we default
 	// to UsersyncIfAmbiguous
-	EEACountries []string `mapstructure:"eea_countries"`
+	EEACountries    []string `mapstructure:"eea_countries"`
+	EEACountriesMap map[string]struct{}
 }
 
 func (cfg *GDPR) validate(errs configErrors) configErrors {
@@ -608,9 +609,15 @@ func New(v *viper.Viper) (*Configuration, error) {
 
 	// To look for a request's publisher_id in the NonStandardPublishers list in
 	// O(1) time, we fill this hash table located in the NonStandardPublisherMap field of GDPR
-	c.GDPR.NonStandardPublisherMap = make(map[string]int)
+	var s struct{}
+	c.GDPR.NonStandardPublisherMap = make(map[string]struct{})
 	for i := 0; i < len(c.GDPR.NonStandardPublishers); i++ {
-		c.GDPR.NonStandardPublisherMap[c.GDPR.NonStandardPublishers[i]] = 1
+		c.GDPR.NonStandardPublisherMap[c.GDPR.NonStandardPublishers[i]] = s
+	}
+
+	c.GDPR.EEACountriesMap = make(map[string]struct{})
+	for i := 0; i < len(c.GDPR.EEACountriesMap); i++ {
+		c.GDPR.NonStandardPublisherMap[c.GDPR.EEACountries[i]] = s
 	}
 
 	// To look for a request's app_id in O(1) time, we fill this hash table located in the
@@ -711,6 +718,7 @@ func (cfg *Configuration) setDerivedDefaults() {
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderImprovedigital, "https://ad.360yield.com/server_match?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dimprovedigital%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BPUB_USER_ID%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderIx, "https://ssum.casalemedia.com/usermatchredir?s=184932&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dix%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderInvibes, "https://u2.videostep.com/User/getLid?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dinvibes%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderKrushmedia, "https://cs.krushmedia.com/4e4abdd5ecc661643458a730b1aa927d.gif?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dkrushmedia%26uid%3D%5BUID%5D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLifestreet, "https://ads.lfstmedia.com/idsync/137062?synced=1&ttl=1s&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlifestreet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLockerDome, "https://lockerdome.com/usync/prebidserver?pid="+cfg.Adapters["lockerdome"].PlatformID+"&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlockerdome%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7Buid%7D%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLogicad, "https://cr-p31.ladsp.jp/cookiesender/31?r=true&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&ru="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlogicad%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
@@ -932,6 +940,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.improvedigital.endpoint", "http://ad.360yield.com/pbs")
 	v.SetDefault("adapters.inmobi.endpoint", "https://api.w.inmobi.com/showad/openrtb/bidder/prebid")
 	v.SetDefault("adapters.ix.endpoint", "http://appnexus-us-east.lb.indexww.com/transbidder?p=184932")
+	v.SetDefault("adapters.krushmedia.endpoint", "http://ads4.krushmedia.com/?c=rtb&m=req&key={{.AccountID}}")
 	v.SetDefault("adapters.invibes.endpoint", "https://{{.Host}}/bid/ServerBidAdContent")
 	v.SetDefault("adapters.kidoz.endpoint", "http://prebid-adapter.kidoz.net/openrtb2/auction?src=prebid-server")
 	v.SetDefault("adapters.kubient.endpoint", "https://kssp.kbntx.ch/prebid")
@@ -966,7 +975,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.triplelift_native.disabled", true)
 	v.SetDefault("adapters.triplelift_native.extra_info", "{\"publisher_whitelist\":[]}")
 	v.SetDefault("adapters.triplelift.endpoint", "https://tlx.3lift.com/s2s/auction?sra=1&supplier_id=20")
-	v.SetDefault("adapters.ucfunnel.endpoint", "http://apac-hk-adx.aralego.com/prebid")
+	v.SetDefault("adapters.ucfunnel.endpoint", "http://pbs.aralego.com/prebid")
 	v.SetDefault("adapters.unruly.endpoint", "http://targeting.unrulymedia.com/openrtb/2.2")
 	v.SetDefault("adapters.valueimpression.endpoint", "https://rtb.valueimpression.com/endpoint")
 	v.SetDefault("adapters.verizonmedia.disabled", true)
diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go
index 834c44c5..d2397635 100755
--- a/exchange/adapter_map.go
+++ b/exchange/adapter_map.go
@@ -51,6 +51,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/invibes"
 	"github.com/prebid/prebid-server/adapters/ix"
 	"github.com/prebid/prebid-server/adapters/kidoz"
+	"github.com/prebid/prebid-server/adapters/krushmedia"
 	"github.com/prebid/prebid-server/adapters/kubient"
 	"github.com/prebid/prebid-server/adapters/lifestreet"
 	"github.com/prebid/prebid-server/adapters/lockerdome"
@@ -144,6 +145,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter
 		openrtb_ext.BidderInMobi:          inmobi.NewInMobiAdapter(cfg.Adapters[string(openrtb_ext.BidderInMobi)].Endpoint),
 		openrtb_ext.BidderInvibes:         invibes.NewInvibesBidder(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderInvibes))].Endpoint),
 		openrtb_ext.BidderKidoz:           kidoz.NewKidozBidder(cfg.Adapters[string(openrtb_ext.BidderKidoz)].Endpoint),
+		openrtb_ext.BidderKrushmedia:      krushmedia.NewKrushmediaBidder(cfg.Adapters[string(openrtb_ext.BidderKrushmedia)].Endpoint),
 		openrtb_ext.BidderKubient:         kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint),
 		openrtb_ext.BidderLockerDome:      lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint),
 		openrtb_ext.BidderLunaMedia:       lunamedia.NewLunaMediaBidder(cfg.Adapters[string(openrtb_ext.BidderLunaMedia)].Endpoint),
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 11ef9a98..62fb9a2c 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -59,7 +59,6 @@ type exchange struct {
 	currencyConverter   *currencies.RateConverter
 	UsersyncIfAmbiguous bool
 	privacyConfig       config.Privacy
-	eeaCountries        map[string]struct{}
 }
 
 // Container to pass out response ext data from the GetAllBids goroutines back into the main thread
@@ -80,11 +79,6 @@ type bidResponseWrapper struct {
 func NewExchange(client *http.Client, cache prebid_cache_client.Client, cfg *config.Configuration, metricsEngine pbsmetrics.MetricsEngine, infos adapters.BidderInfos, gDPR gdpr.Permissions, currencyConverter *currencies.RateConverter) Exchange {
 	e := new(exchange)
 
-	e.eeaCountries = make(map[string]struct{}, len(cfg.GDPR.EEACountries))
-	var s struct{}
-	for _, c := range cfg.GDPR.EEACountries {
-		e.eeaCountries[c] = s
-	}
 	e.adapterMap = newAdapterMap(client, cfg, infos, metricsEngine)
 	e.cache = cache
 	e.cacheTime = time.Duration(cfg.CacheURL.ExpectedTimeMillis) * time.Millisecond
@@ -141,7 +135,7 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque
 	if geo != nil {
 		// If we have a country set, and it is on the list, we assume GDPR applies if not set on the request.
 		// Otherwise we assume it does not apply as long as it appears "valid" (is 3 characters long).
-		if _, found := e.eeaCountries[strings.ToUpper(geo.Country)]; found {
+		if _, found := e.privacyConfig.GDPR.EEACountriesMap[strings.ToUpper(geo.Country)]; found {
 			usersyncIfAmbiguous = false
 		} else if len(geo.Country) == 3 {
 			// The country field is formatted properly as a three character country code
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 2bb7ffff..580ffecf 100755
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -70,6 +70,7 @@ const (
 	BidderInvibes          BidderName = "invibes"
 	BidderIx               BidderName = "ix"
 	BidderKidoz            BidderName = "kidoz"
+	BidderKrushmedia       BidderName = "krushmedia"
 	BidderKubient          BidderName = "kubient"
 	BidderLifestreet       BidderName = "lifestreet"
 	BidderLockerDome       BidderName = "lockerdome"
@@ -159,6 +160,7 @@ var BidderMap = map[string]BidderName{
 	"invibes":           BidderInvibes,
 	"ix":                BidderIx,
 	"kidoz":             BidderKidoz,
+	"krushmedia":        BidderKrushmedia,
 	"kubient":           BidderKubient,
 	"lifestreet":        BidderLifestreet,
 	"lockerdome":        BidderLockerDome,
diff --git a/openrtb_ext/imp_krushmedia.go b/openrtb_ext/imp_krushmedia.go
new file mode 100644
index 00000000..a175c227
--- /dev/null
+++ b/openrtb_ext/imp_krushmedia.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+// ExtKrushmedia defines imp[0].ext object structure
+type ExtKrushmedia struct {
+	AccountID string `json:"key"`
+}
diff --git a/static/bidder-info/krushmedia.yaml b/static/bidder-info/krushmedia.yaml
new file mode 100644
index 00000000..342e11df
--- /dev/null
+++ b/static/bidder-info/krushmedia.yaml
@@ -0,0 +1,13 @@
+maintainer:
+  email: "adapter@krushmedia.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
\ No newline at end of file
diff --git a/static/bidder-params/krushmedia.json b/static/bidder-params/krushmedia.json
new file mode 100644
index 00000000..e395da85
--- /dev/null
+++ b/static/bidder-params/krushmedia.json
@@ -0,0 +1,13 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "Krushmedia Adapter Params",
+    "description": "A schema which validates params accepted by the Krushmedia adapter",
+    "type": "object",
+    "properties": {
+      "key": {
+        "type": "string",
+        "description": "ssp key"
+      }
+    },
+    "required": ["key"]
+  }
\ No newline at end of file
diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go
index 5a7e306f..894d76e9 100755
--- a/usersync/usersyncers/syncer.go
+++ b/usersync/usersyncers/syncer.go
@@ -40,6 +40,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/improvedigital"
 	"github.com/prebid/prebid-server/adapters/invibes"
 	"github.com/prebid/prebid-server/adapters/ix"
+	"github.com/prebid/prebid-server/adapters/krushmedia"
 	"github.com/prebid/prebid-server/adapters/lifestreet"
 	"github.com/prebid/prebid-server/adapters/lockerdome"
 	"github.com/prebid/prebid-server/adapters/logicad"
@@ -120,6 +121,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderImprovedigital, improvedigital.NewImprovedigitalSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderInvibes, invibes.NewInvibesSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderIx, ix.NewIxSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderKrushmedia, krushmedia.NewKrushmediaSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderLifestreet, lifestreet.NewLifestreetSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderLockerDome, lockerdome.NewLockerDomeSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderLogicad, logicad.NewLogicadSyncer)


Test patch:
diff --git a/adapters/invibes/invibes_test.go b/adapters/invibes/invibes_test.go
index fd3ea303..740af6af 100644
--- a/adapters/invibes/invibes_test.go
+++ b/adapters/invibes/invibes_test.go
@@ -7,5 +7,5 @@ import (
 )
 
 func TestJsonSamples(t *testing.T) {
-	adapterstest.RunJSONBidderTest(t, "invibestest", NewInvibesBidder("https://adweb.videostepstage.com/bid/VideoAdContent"))
+	adapterstest.RunJSONBidderTest(t, "invibestest", NewInvibesBidder("https://{{.Host}}/bid/ServerBidAdContent"))
 }
diff --git a/adapters/invibes/invibestest/amp/amp-ad.json b/adapters/invibes/invibestest/amp/amp-ad.json
index 9dcb8531..9b781650 100644
--- a/adapters/invibes/invibestest/amp/amp-ad.json
+++ b/adapters/invibes/invibestest/amp/amp-ad.json
@@ -22,6 +22,7 @@
         "ext": {
           "bidder": {
             "placementId": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
+            "domainId": 1002,
             "debug": {
               "testAmp": "true"
             }
@@ -33,7 +34,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://adweb.videostepstage.com/bid/VideoAdContent",
+        "uri": "https://bid2.videostep.com/bid/ServerBidAdContent",
         "body": {
           "BidParamsJson": "{\"PlacementIds\":[\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\"],\"BidVersion\":\"4\",\"Properties\":{\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\":{\"Formats\":[{\"w\":300,\"h\":150}],\"ImpId\":\"test-imp-id\"}}}",
           "Bvid": "",
diff --git a/adapters/invibes/invibestest/exemplary/advanced-ad.json b/adapters/invibes/invibestest/exemplary/advanced-ad.json
index 18736dd9..b324357c 100644
--- a/adapters/invibes/invibestest/exemplary/advanced-ad.json
+++ b/adapters/invibes/invibestest/exemplary/advanced-ad.json
@@ -34,7 +34,8 @@
         },
         "ext": {
           "bidder": {
-            "placementId": "2eb6bd58-865c-47ce-af7f-a918108c3fd2"
+            "placementId": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
+            "domainId": 2
           }
         }
       }
@@ -43,7 +44,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://adweb.videostepstage.com/bid/VideoAdContent",
+        "uri": "https://adweb.invibesstage.com/bid/ServerBidAdContent",
         "body": {
           "BidParamsJson": "{\"PlacementIds\":[\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\"],\"BidVersion\":\"4\",\"Properties\":{\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\":{\"Formats\":[{\"w\":300,\"h\":150}],\"ImpId\":\"test-imp-id\"}}}",
           "Bvid": "",
diff --git a/adapters/invibes/invibestest/exemplary/basic-ad.json b/adapters/invibes/invibestest/exemplary/basic-ad.json
index 7bfd65fc..b62578f2 100644
--- a/adapters/invibes/invibestest/exemplary/basic-ad.json
+++ b/adapters/invibes/invibestest/exemplary/basic-ad.json
@@ -31,7 +31,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://adweb.videostepstage.com/bid/VideoAdContent",
+        "uri": "https://bid.videostep.com/bid/ServerBidAdContent",
         "body": {
           "BidParamsJson": "{\"PlacementIds\":[\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\"],\"BidVersion\":\"4\",\"Properties\":{\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\":{\"Formats\":[{\"w\":300,\"h\":150}],\"ImpId\":\"test-imp-id\"}}}",
           "Bvid": "",
diff --git a/adapters/invibes/invibestest/exemplary/no-ad.json b/adapters/invibes/invibestest/exemplary/no-ad.json
index 30744de4..da8d55a3 100644
--- a/adapters/invibes/invibestest/exemplary/no-ad.json
+++ b/adapters/invibes/invibestest/exemplary/no-ad.json
@@ -34,7 +34,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://adweb.videostepstage.com/bid/VideoAdContent",
+        "uri": "https://bid.videostep.com/bid/ServerBidAdContent",
         "body": {
           "BidParamsJson": "{\"PlacementIds\":[\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\"],\"BidVersion\":\"4\",\"Properties\":{\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\":{\"Formats\":[{\"w\":300,\"h\":150}],\"ImpId\":\"test-imp-id\"}}}",
           "Bvid": "",
diff --git a/adapters/invibes/invibestest/exemplary/test-ad.json b/adapters/invibes/invibestest/exemplary/test-ad.json
index e9c894b9..87c6e83d 100644
--- a/adapters/invibes/invibestest/exemplary/test-ad.json
+++ b/adapters/invibes/invibestest/exemplary/test-ad.json
@@ -21,6 +21,7 @@
         "ext": {
           "bidder": {
             "placementId": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
+            "domainId": 1,
             "debug": {
               "testBvid": "111",
               "testLog": false
@@ -33,7 +34,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://adweb.videostepstage.com/bid/VideoAdContent",
+        "uri": "https://adweb.videostepstage.com/bid/ServerBidAdContent",
         "body": {
           "BidParamsJson": "{\"PlacementIds\":[\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\"],\"BidVersion\":\"4\",\"Properties\":{\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\":{\"Formats\":[{\"w\":300,\"h\":150}],\"ImpId\":\"test-imp-id\"}}}",
           "Bvid": "111",
diff --git a/adapters/invibes/invibestest/supplemental/request-error-servererr.json b/adapters/invibes/invibestest/supplemental/request-error-servererr.json
index 0249bf83..270d87a6 100644
--- a/adapters/invibes/invibestest/supplemental/request-error-servererr.json
+++ b/adapters/invibes/invibestest/supplemental/request-error-servererr.json
@@ -21,6 +21,7 @@
         "ext": {
           "bidder": {
             "placementId": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
+            "domainId": 1001,
             "debug": {
               "testLog": true
             }
@@ -32,7 +33,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://adweb.videostepstage.com/bid/VideoAdContent",
+        "uri": "https://bid.videostep.com/bid/ServerBidAdContent",
         "body": {
           "BidParamsJson": "{\"PlacementIds\":[\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\"],\"BidVersion\":\"4\",\"Properties\":{\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\":{\"Formats\":[{\"w\":300,\"h\":150}],\"ImpId\":\"test-imp-id\"}}}",
           "Bvid": "",
diff --git a/adapters/invibes/invibestest/supplemental/request-error-statuscode.json b/adapters/invibes/invibestest/supplemental/request-error-statuscode.json
index 4c7ac03a..11a7f94f 100644
--- a/adapters/invibes/invibestest/supplemental/request-error-statuscode.json
+++ b/adapters/invibes/invibestest/supplemental/request-error-statuscode.json
@@ -32,7 +32,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://adweb.videostepstage.com/bid/VideoAdContent",
+        "uri": "https://bid.videostep.com/bid/ServerBidAdContent",
         "body": {
           "BidParamsJson": "{\"PlacementIds\":[\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\"],\"BidVersion\":\"4\",\"Properties\":{\"2eb6bd58-865c-47ce-af7f-a918108c3fd2\":{\"Formats\":[{\"w\":300,\"h\":150}],\"ImpId\":\"test-imp-id\"}}}",
           "Bvid": "",
diff --git a/adapters/krushmedia/krushmedia_test.go b/adapters/krushmedia/krushmedia_test.go
new file mode 100644
index 00000000..b8e54a97
--- /dev/null
+++ b/adapters/krushmedia/krushmedia_test.go
@@ -0,0 +1,11 @@
+package krushmedia
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+)
+
+func TestJsonSamples(t *testing.T) {
+	adapterstest.RunJSONBidderTest(t, "krushmediatest", NewKrushmediaBidder("http://example.com/?c=rtb&m=req&key={{.AccountID}}"))
+}
diff --git a/adapters/krushmedia/krushmediatest/exemplary/banner-app.json b/adapters/krushmedia/krushmediatest/exemplary/banner-app.json
new file mode 100644
index 00000000..dc6b598e
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/exemplary/banner-app.json
@@ -0,0 +1,154 @@
+{
+    "mockBidRequest": {
+      "id": "some-request-id",
+      "device": {
+        "ua": "test-user-agent",
+        "ip": "123.123.123.123",
+        "language": "en",
+        "dnt": 0
+      },
+      "tmax": 1000,
+      "user": {
+        "buyeruid": "0000-000-000-0000"
+      },
+      "app": {
+        "publisher": {
+          "id": "123456789"
+        },
+        "cat": [
+          "IAB22-1"
+        ],
+        "bundle": "com.app.awesome",
+        "name": "Awesome App",
+        "domain": "awesomeapp.com",
+        "id": "123456789"
+      },
+      "imp": [
+        {
+          "id": "some-impression-id",
+          
+          "banner": {
+            "w":320,
+            "h":50
+          },
+          "ext": {
+            "bidder": {
+              "key": "2"
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "headers": {
+            "Content-Type": [
+              "application/json;charset=utf-8"
+            ],
+            "Accept": [
+              "application/json"
+            ],
+            "X-Openrtb-Version": [
+              "2.5"
+            ],
+            "User-Agent": [
+              "test-user-agent"
+            ],
+            "X-Forwarded-For": [
+              "123.123.123.123"
+            ],
+            "Accept-Language": [
+              "en"
+            ],
+            "Dnt": [
+              "0"
+            ]
+          },
+          "uri": "http://example.com/?c=rtb&m=req&key=2",
+          "body": {
+            "id": "some-request-id",
+            "device": {
+              "ua": "test-user-agent",
+              "ip": "123.123.123.123",
+              "language": "en",
+              "dnt": 0
+            },
+            "imp": [
+              {
+                "id": "some-impression-id",
+                "banner": {
+                    "w":320,
+                    "h":50
+                }
+              }
+            ],
+            "app": {
+              "id": "123456789",
+              "name": "Awesome App",
+              "bundle": "com.app.awesome",
+              "domain": "awesomeapp.com",
+              "cat": [
+                "IAB22-1"
+              ],
+              "publisher": {
+                "id": "123456789"
+              }
+            },
+            "user": {
+              "buyeruid": "0000-000-000-0000"
+            },
+            "tmax": 1000
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "id": "awesome-resp-id",
+            "seatbid": [
+              {
+                "bid": [
+                  {
+                    "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                    "impid": "1",
+                    "price": 3.5,
+                    "adm": "asesome-markup",
+                    "adomain": [
+                      "awesome.com"
+                    ],
+                    "crid": "20",
+                    "w":320,
+                    "h":50
+                  }
+                ],
+              "type": "banner",
+                "seat": "krushmedia"
+              }
+            ],
+            "cur": "USD",
+            "ext": {
+              "responsetimemillis": {
+                "krushmedia": 154
+              },
+              "tmaxrequest": 1000
+            }
+          }
+        }
+      }
+    ],
+    "expectedBids": [
+      {
+        "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+        "impid": "1",
+        "price": 3.5,
+        "adm": "awesome-markup",
+        "adomain": [
+          "awesome.com"
+        ],
+        "crid": "20",
+        "w":320,
+        "h":50
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/krushmedia/krushmediatest/exemplary/banner-web.json b/adapters/krushmedia/krushmediatest/exemplary/banner-web.json
new file mode 100644
index 00000000..0af14203
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/exemplary/banner-web.json
@@ -0,0 +1,148 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "banner": {
+          "w":320,
+          "h":50
+        },
+        "ext": {
+          "bidder": {
+            "key": "2"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ],
+          "Accept-Language": [
+            "en"
+          ],
+          "Dnt": [
+            "0"
+          ]
+        },
+        "uri": "http://example.com/?c=rtb&m=req&key=2",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              
+              "banner": {
+                "w":320,
+                "h":50
+              }
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "0000-000-000-0000"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "1",
+                  "price": 3.5,
+                  "adm": "asesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 320,
+                  "h": 50
+                }
+              ],
+              "type": "banner",
+              "seat": "krushmedia"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+            "responsetimemillis": {
+              "krushmedia": 154
+            },
+            "tmaxrequest": 1000
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "1",
+            "price": 3.5,
+            "adm": "asesome-markup",
+            "crid": "20",
+            "adomain": [
+              "awesome.com"
+            ],
+            "w": 320,
+            "h": 50
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/krushmedia/krushmediatest/exemplary/native-app.json b/adapters/krushmedia/krushmediatest/exemplary/native-app.json
new file mode 100644
index 00000000..fa17bb6a
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/exemplary/native-app.json
@@ -0,0 +1,157 @@
+{
+    "mockBidRequest": {
+      "id": "some-request-id",
+      "device": {
+        "ua": "test-user-agent",
+        "ip": "123.123.123.123",
+        "language": "en",
+        "dnt": 0
+      },
+      "tmax": 1000,
+      "user": {
+        "buyeruid": "0000-000-000-0000"
+      },
+      "app": {
+        "publisher": {
+          "id": "123456789"
+        },
+        "cat": [
+          "IAB22-1"
+        ],
+        "bundle": "com.app.awesome",
+        "name": "Awesome App",
+        "domain": "awesomeapp.com",
+        "id": "123456789"
+      },
+      "imp": [
+        {
+          "id": "some-impression-id",
+          
+          "native": {
+            "ver":"1.1",
+            "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+          },
+          "ext": {
+            "bidder": {
+              "key": "2"
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "headers": {
+            "Content-Type": [
+              "application/json;charset=utf-8"
+            ],
+            "Accept": [
+              "application/json"
+            ],
+            "X-Openrtb-Version": [
+              "2.5"
+            ],
+            "User-Agent": [
+              "test-user-agent"
+            ],
+            "X-Forwarded-For": [
+              "123.123.123.123"
+            ],
+            "Accept-Language": [
+              "en"
+            ],
+            "Dnt": [
+              "0"
+            ]
+          },
+          "uri": "http://example.com/?c=rtb&m=req&key=2",
+          "body": {
+            "id": "some-request-id",
+            "device": {
+              "ua": "test-user-agent",
+              "ip": "123.123.123.123",
+              "language": "en",
+              "dnt": 0
+            },
+            "imp": [
+              {
+                "id": "some-impression-id",
+                "native": {
+                  "ver":"1.1",
+                  "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+                }
+              }
+            ],
+            "app": {
+              "id": "123456789",
+              "name": "Awesome App",
+              "bundle": "com.app.awesome",
+              "domain": "awesomeapp.com",
+              "cat": [
+                "IAB22-1"
+              ],
+              "publisher": {
+                "id": "123456789"
+              }
+            },
+            "user": {
+              "buyeruid": "0000-000-000-0000"
+            },
+            "tmax": 1000
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "id": "awesome-resp-id",
+            "seatbid": [
+              {
+                "bid": [
+                  {
+                    "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                    "impid": "some-impression-id",
+                    "price": 3.5,
+                    "adm": "asesome-markup",
+                    "adomain": [
+                      "awesome.com"
+                    ],
+                    "crid": "20"
+                  }
+                ],
+                "type": "native",
+                "seat": "krushmedia"
+              }
+            ],
+            "cur": "USD",
+            "ext": {
+              "responsetimemillis": {
+                "krushmedia": 154
+              },
+              "tmaxrequest": 1000
+            }
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "bids":[
+          {
+            "bid":  {
+              "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+              "impid": "some-impression-id",
+              "price": 3.5,
+              "adm": "asesome-markup",
+              "crid": "20",
+              "adomain": [
+                "awesome.com"
+              ]
+            },
+            "type": "native"
+          }
+        ]
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/krushmedia/krushmediatest/exemplary/native-web.json b/adapters/krushmedia/krushmediatest/exemplary/native-web.json
new file mode 100644
index 00000000..1811e8cd
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/exemplary/native-web.json
@@ -0,0 +1,144 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        
+        "native": {
+          "ver":"1.1",
+          "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+        },
+        "ext": {
+          "bidder": {
+            "key": "2"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ],
+          "Accept-Language": [
+            "en"
+          ],
+          "Dnt": [
+            "0"
+          ]
+        },
+        "uri": "http://example.com/?c=rtb&m=req&key=2",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              
+              "native": {
+                "ver":"1.1",
+                "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+              }
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "0000-000-000-0000"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 3.5,
+                  "adm": "asesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20"
+                }
+              ],
+              "seat": "krushmedia"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+            "responsetimemillis": {
+              "krushmedia": 154
+            },
+            "tmaxrequest": 1000
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 3.5,
+            "adm": "asesome-markup",
+            "crid": "20",
+            "adomain": [
+              "awesome.com"
+            ]
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/krushmedia/krushmediatest/exemplary/video-app.json b/adapters/krushmedia/krushmediatest/exemplary/video-app.json
new file mode 100644
index 00000000..c13923a5
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/exemplary/video-app.json
@@ -0,0 +1,169 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "key": "2"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ],
+          "Accept-Language": [
+            "en"
+          ],
+          "Dnt": [
+            "0"
+          ]
+        },
+        "uri": "http://example.com/?c=rtb&m=req&key=2",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              }
+            }
+          ],
+          "app": {
+            "id": "123456789",
+            "name": "Awesome App",
+            "bundle": "com.app.awesome",
+            "domain": "awesomeapp.com",
+            "cat": [
+              "IAB22-1"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "0000-000-000-0000"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 3.5,
+                  "adm": "asesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 1280,
+                  "h": 720
+                }
+              ],
+              "seat": "krushmedia"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+            "responsetimemillis": {
+              "krushmedia": 154
+            },
+            "tmaxrequest": 1000
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 3.5,
+            "adm": "asesome-markup",
+            "crid": "20",
+            "adomain": [
+              "awesome.com"
+            ],
+            "w": 1280,
+            "h": 720
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/krushmedia/krushmediatest/exemplary/video-web.json b/adapters/krushmedia/krushmediatest/exemplary/video-web.json
new file mode 100644
index 00000000..8bf6f8fb
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/exemplary/video-web.json
@@ -0,0 +1,161 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "key": "2"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ],
+          "Accept-Language": [
+            "en"
+          ],
+          "Dnt": [
+            "0"
+          ]
+        },
+        "uri": "http://example.com/?c=rtb&m=req&key=2",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              }
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "0000-000-000-0000"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "1",
+                  "price": 3.5,
+                  "adm": "asesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 1280,
+                  "h": 720,
+                  "ext": {
+                    "prebid": {
+                      "type": "video"
+                    }
+                  }
+                }
+              ],
+              "seat": "krushmedia"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+            "responsetimemillis": {
+              "krushmedia": 154
+            },
+            "tmaxrequest": 1000
+          }
+        }
+      }
+    }
+  ],
+  "expectedBids": [
+    {
+      "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+      "impid": "1",
+      "price": 3.5,
+      "adm": "asesome-markup",
+      "adomain": [
+        "awesome.com"
+      ],
+      "crid": "20",
+      "w": 1280,
+      "h": 720,
+      "ext": {
+        "prebid": {
+          "type": "video"
+        }
+      }
+    }
+  ]
+}
diff --git a/adapters/krushmedia/krushmediatest/params/race/banner.json b/adapters/krushmedia/krushmediatest/params/race/banner.json
new file mode 100644
index 00000000..8f4ee2f5
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/params/race/banner.json
@@ -0,0 +1,3 @@
+{
+  "key": "2"
+}
\ No newline at end of file
diff --git a/adapters/krushmedia/krushmediatest/params/race/native.json b/adapters/krushmedia/krushmediatest/params/race/native.json
new file mode 100644
index 00000000..3c55c54e
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/params/race/native.json
@@ -0,0 +1,3 @@
+{
+  "key": "2"
+}
diff --git a/adapters/krushmedia/krushmediatest/params/race/video.json b/adapters/krushmedia/krushmediatest/params/race/video.json
new file mode 100644
index 00000000..3c55c54e
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/params/race/video.json
@@ -0,0 +1,3 @@
+{
+  "key": "2"
+}
diff --git a/adapters/krushmedia/krushmediatest/supplemental/invalid-ext-object.json b/adapters/krushmedia/krushmediatest/supplemental/invalid-ext-object.json
new file mode 100644
index 00000000..2dda068b
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/supplemental/invalid-ext-object.json
@@ -0,0 +1,29 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Bidder extension not provided or can't be unmarshalled",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "my-adcode",
+        "video": {
+          "mimes": ["video/mp4"],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": ""
+      }
+    ],
+    "site": {
+      "page": "test.com"
+    }
+  },
+  "httpCalls": []
+}
diff --git a/adapters/krushmedia/krushmediatest/supplemental/invalid-response.json b/adapters/krushmedia/krushmediatest/supplemental/invalid-response.json
new file mode 100644
index 00000000..b60e5317
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/supplemental/invalid-response.json
@@ -0,0 +1,116 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        
+        "banner": {
+          "w":320,
+          "h":50
+        },
+        "ext": {
+          "bidder": {
+            "key": "2"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "headers": {
+        "Content-Type": [
+          "application/json;charset=utf-8"
+        ],
+        "Accept": [
+          "application/json"
+        ],
+        "X-Openrtb-Version": [
+          "2.5"
+        ],
+        "User-Agent": [
+          "test-user-agent"
+        ],
+        "X-Forwarded-For": [
+          "123.123.123.123"
+        ],
+        "Accept-Language": [
+          "en"
+        ],
+        "Dnt": [
+          "0"
+        ]
+      },
+      "uri": "http://example.com/?c=rtb&m=req&key=2",
+      "body": {
+        "id": "some-request-id",
+        "device": {
+          "ua": "test-user-agent",
+          "ip": "123.123.123.123",
+          "language": "en",
+          "dnt": 0
+        },
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "banner": {
+                "w":320,
+                "h":50
+            }
+          }
+        ],
+        "app": {
+          "id": "123456789",
+          "name": "Awesome App",
+          "bundle": "com.app.awesome",
+          "domain": "awesomeapp.com",
+          "cat": [
+            "IAB22-1"
+          ],
+          "publisher": {
+            "id": "123456789"
+          }
+        },
+        "user": {
+          "buyeruid": "0000-000-000-0000"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": "invalid response"
+    }
+  }],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bad Server Response",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/krushmedia/krushmediatest/supplemental/requires-imp-object.json b/adapters/krushmedia/krushmediatest/supplemental/requires-imp-object.json
new file mode 100644
index 00000000..1fcf1b48
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/supplemental/requires-imp-object.json
@@ -0,0 +1,16 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Missing Imp Object",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [],
+    "site": {
+      "page": "test.com"
+    }
+  },
+  "httpCalls": []
+}
diff --git a/adapters/krushmedia/krushmediatest/supplemental/status-code-bad-request.json b/adapters/krushmedia/krushmediatest/supplemental/status-code-bad-request.json
new file mode 100644
index 00000000..d8a091b0
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/supplemental/status-code-bad-request.json
@@ -0,0 +1,91 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "key": "2"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://example.com/?c=rtb&m=req&key=2",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 120,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            }
+          }
+        ],
+        "app":  {
+          "publisher": {
+            "id": "123456789"
+          },
+          "cat": [
+            "IAB22-1"
+          ],
+          "bundle": "com.app.awesome",
+          "name": "Awesome App",
+          "domain": "awesomeapp.com",
+          "id": "123456789"
+        },
+        "user": {
+          "buyeruid": "0000-000-000-0000"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 400
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: [ 400 ]",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/krushmedia/krushmediatest/supplemental/status-code-no-content.json b/adapters/krushmedia/krushmediatest/supplemental/status-code-no-content.json
new file mode 100644
index 00000000..7539f84a
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/supplemental/status-code-no-content.json
@@ -0,0 +1,73 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        
+        "video": {
+          "mimes": ["video/mp4"],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "key": "2"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://example.com/?c=rtb&m=req&key=2",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 120,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            }
+          }
+        ],
+        "site": {
+          "page": "test.com",
+          "publisher": {
+            "id": "123456789"
+          }
+        },
+        "user": {
+          "buyeruid": "0000-000-000-0000"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 204
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": []
+}
diff --git a/adapters/krushmedia/krushmediatest/supplemental/status-code-other-error.json b/adapters/krushmedia/krushmediatest/supplemental/status-code-other-error.json
new file mode 100644
index 00000000..b7a24f35
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/supplemental/status-code-other-error.json
@@ -0,0 +1,78 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        
+        "video": {
+          "mimes": ["video/mp4"],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "key": "2"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://example.com/?c=rtb&m=req&key=2",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 120,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            }
+          }
+        ],
+        "site": {
+          "page": "test.com",
+          "publisher": {
+            "id": "123456789"
+          }
+        },
+        "user": {
+          "buyeruid": "0000-000-000-0000"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 306
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Something went wrong, please contact your Account Manager. Status Code: [ 306 ] ",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/krushmedia/krushmediatest/supplemental/status-code-service-unavailable.json b/adapters/krushmedia/krushmediatest/supplemental/status-code-service-unavailable.json
new file mode 100644
index 00000000..99a813b2
--- /dev/null
+++ b/adapters/krushmedia/krushmediatest/supplemental/status-code-service-unavailable.json
@@ -0,0 +1,73 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        
+        "video": {
+          "mimes": ["video/mp4"],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "key": "2"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://example.com/?c=rtb&m=req&key=2",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 120,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            }
+          }
+        ],
+        "site": {
+          "page": "test.com",
+          "publisher": {
+            "id": "123456789"
+          }
+        },
+        "user": {
+          "buyeruid": "0000-000-000-0000"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 503
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": []
+}
diff --git a/adapters/krushmedia/params_test.go b/adapters/krushmedia/params_test.go
new file mode 100644
index 00000000..26daa56e
--- /dev/null
+++ b/adapters/krushmedia/params_test.go
@@ -0,0 +1,50 @@
+package krushmedia
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+var validParams = []string{
+	`{ "key": "2" }`,
+}
+
+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.BidderKrushmedia, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected Krushmedia params: %s", validParam)
+		}
+	}
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{ "key": 2 }`,
+	`{ "anyparam": "anyvalue" }`,
+}
+
+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.BidderKrushmedia, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
diff --git a/adapters/krushmedia/usersync_test.go b/adapters/krushmedia/usersync_test.go
new file mode 100644
index 00000000..b58f2f1b
--- /dev/null
+++ b/adapters/krushmedia/usersync_test.go
@@ -0,0 +1,34 @@
+package krushmedia
+
+import (
+	"testing"
+	"text/template"
+
+	"github.com/prebid/prebid-server/privacy"
+	"github.com/prebid/prebid-server/privacy/ccpa"
+	"github.com/prebid/prebid-server/privacy/gdpr"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestKrushmediaSyncer(t *testing.T) {
+	syncURL := "https://cs.krushmedia.com/4e4abdd5ecc661643458a730b1aa927d.gif?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dkrushmedia%26uid%3D%5BUID%5D"
+	syncURLTemplate := template.Must(
+		template.New("sync-template").Parse(syncURL),
+	)
+	syncer := NewKrushmediaSyncer(syncURLTemplate)
+	syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{
+		GDPR: gdpr.Policy{
+			Signal:  "0",
+			Consent: "allGdpr",
+		},
+		CCPA: ccpa.Policy{
+			Consent: "1-YY",
+		},
+	})
+
+	assert.NoError(t, err)
+	assert.Equal(t, "https://cs.krushmedia.com/4e4abdd5ecc661643458a730b1aa927d.gif?gdpr=0&gdpr_consent=allGdpr&us_privacy=1-YY&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dkrushmedia%26uid%3D%5BUID%5D", syncInfo.URL)
+	assert.Equal(t, "redirect", syncInfo.Type)
+	assert.EqualValues(t, 0, syncer.GDPRVendorID())
+	assert.Equal(t, false, syncInfo.SupportCORS)
+}
diff --git a/adapters/ucfunnel/ucfunneltest/exemplary/ucfunnel.json b/adapters/ucfunnel/ucfunneltest/exemplary/ucfunnel.json
index 2a7e4b2b..9a4dad9b 100644
--- a/adapters/ucfunnel/ucfunneltest/exemplary/ucfunnel.json
+++ b/adapters/ucfunnel/ucfunneltest/exemplary/ucfunnel.json
@@ -29,7 +29,8 @@
             "exp": 3600,
             "ext":{
                 "ucfunnel":{
-                    "adunitid":"ad-BE7E9EB323E9996218A733887B6E924"
+                    "adunitid": "test-ad-83444226E44368D1E32E49EEBE6D29",
+                    "partnerid": "par-2EDDB423AA24474188B843EE4842932"
                 }
             }
         }
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 820c578b..2b91f1b0 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -1282,6 +1282,12 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 		t.Fatalf("%s: Failed to parse aliases", filename)
 	}
 
+	var s struct{}
+	eeac := make(map[string]struct{})
+	for _, c := range []string{"FIN", "FRA", "GUF"} {
+		eeac[c] = s
+	}
+
 	privacyConfig := config.Privacy{
 		CCPA: config.CCPA{
 			Enforce: spec.EnforceCCPA,
@@ -1291,6 +1297,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 		},
 		GDPR: config.GDPR{
 			UsersyncIfAmbiguous: !spec.AssumeGDPRApplies,
+			EEACountriesMap:     eeac,
 		},
 	}
 
@@ -1409,12 +1416,6 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 		}
 	}
 
-	var s struct{}
-	eeac := make(map[string]struct{})
-	for _, c := range []string{"FIN", "FRA", "GUF"} {
-		eeac[c] = s
-	}
-
 	return &exchange{
 		adapterMap:          adapters,
 		me:                  metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.BidderList()),
@@ -1424,7 +1425,6 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 		currencyConverter:   currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)),
 		UsersyncIfAmbiguous: privacyConfig.GDPR.UsersyncIfAmbiguous,
 		privacyConfig:       privacyConfig,
-		eeaCountries:        eeac,
 	}
 }
 
diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go
index d5114454..793eb967 100644
--- a/gdpr/impl_test.go
+++ b/gdpr/impl_test.go
@@ -212,7 +212,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 	assertBoolsEqual(t, true, allowPI)
 
 	// Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array
-	perms.cfg.NonStandardPublisherMap = map[string]int{"appNexusAppID": 1}
+	perms.cfg.NonStandardPublisherMap = map[string]struct{}{"appNexusAppID": {}}
 	allowPI, _, _, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA")
 	assertNilErr(t, err)
 	assertBoolsEqual(t, true, allowPI)
@@ -343,7 +343,7 @@ func TestAllowPersonalInfoWhitelistTCF2(t *testing.T) {
 		},
 	}
 	// Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array
-	perms.cfg.NonStandardPublisherMap = map[string]int{"appNexusAppID": 1}
+	perms.cfg.NonStandardPublisherMap = map[string]struct{}{"appNexusAppID": {}}
 	allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
 	assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed")
 	assert.EqualValuesf(t, true, allowPI, "AllowPI failure")
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 0c005fe6..cd5f493f 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -49,6 +49,7 @@ func TestNewSyncerMap(t *testing.T) {
 			string(openrtb_ext.BidderImprovedigital):   syncConfig,
 			string(openrtb_ext.BidderInvibes):          syncConfig,
 			string(openrtb_ext.BidderIx):               syncConfig,
+			string(openrtb_ext.BidderKrushmedia):       syncConfig,
 			string(openrtb_ext.BidderLifestreet):       syncConfig,
 			string(openrtb_ext.BidderLockerDome):       syncConfig,
 			string(openrtb_ext.BidderLogicad):          syncConfig,