Repository: prebid/prebid-server
Pull request #1837 (2021-05-04 15:34:07)

IX: Set category in bid.cat
From context in https://prebid.slack.com/archives/C7UCUKKB8/p1619639305041100, we should be setting bid.cat, not PrimaryCategory in order to utilize category translation.
Update adapter to accept both for compatibility as we fully migrate to bid.cat in exchange response.


Patch:
diff --git a/adapters/interactiveoffers/interactiveoffers.go b/adapters/interactiveoffers/interactiveoffers.go
new file mode 100644
index 00000000..fd4cd580
--- /dev/null
+++ b/adapters/interactiveoffers/interactiveoffers.go
@@ -0,0 +1,79 @@
+package interactiveoffers
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	endpoint string
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	requestJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	requestData := &adapters.RequestData{
+		Method: "POST",
+		Uri:    a.endpoint,
+		Body:   requestJSON,
+	}
+
+	return []*adapters.RequestData{requestData}, nil
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if responseData.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if responseData.StatusCode == http.StatusBadRequest {
+		err := &errortypes.BadInput{
+			Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
+		}
+		return nil, []error{err}
+	}
+
+	if responseData.StatusCode != http.StatusOK {
+		err := &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
+		}
+		return nil, []error{err}
+	}
+
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(responseData.Body, &response); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+	bidResponse.Currency = response.Cur
+	for _, seatBid := range response.SeatBid {
+		for _, bid := range seatBid.Bid {
+			bid := bid // pin https://github.com/kyoh86/scopelint#whats-this
+			b := &adapters.TypedBid{
+				Bid:     &bid,
+				BidType: openrtb_ext.BidTypeBanner,
+			}
+			bidResponse.Bids = append(bidResponse.Bids, b)
+		}
+	}
+	return bidResponse, nil
+}
+
+// Builder builds a new instance of the Interactiveoffers adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+	return bidder, nil
+}
diff --git a/adapters/ix/ix.go b/adapters/ix/ix.go
index f8903008..5e10138f 100644
--- a/adapters/ix/ix.go
+++ b/adapters/ix/ix.go
@@ -409,8 +409,10 @@ func (a *IxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalReque
 				unmarshalExtErr := json.Unmarshal(bid.Ext, &bidExt)
 				if unmarshalExtErr == nil && bidExt.Prebid != nil && bidExt.Prebid.Video != nil {
 					bidExtVideo = &openrtb_ext.ExtBidPrebidVideo{
-						Duration:        bidExt.Prebid.Video.Duration,
-						PrimaryCategory: bidExt.Prebid.Video.PrimaryCategory,
+						Duration: bidExt.Prebid.Video.Duration,
+					}
+					if len(bid.Cat) == 0 {
+						bid.Cat = []string{bidExt.Prebid.Video.PrimaryCategory}
 					}
 				}
 			}
diff --git a/adapters/pubmatic/pubmatic.go b/adapters/pubmatic/pubmatic.go
index afc9263a..2e361fa5 100644
--- a/adapters/pubmatic/pubmatic.go
+++ b/adapters/pubmatic/pubmatic.go
@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"context"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"io/ioutil"
 	"net/http"
@@ -158,8 +157,8 @@ func (a *PubmaticAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder
 					}
 
 					pbReq.Imp[i].TagID = strings.TrimSpace(adSlot[0])
-					pbReq.Imp[i].Banner.H = openrtb2.Int64Ptr(int64(height))
 					pbReq.Imp[i].Banner.W = openrtb2.Int64Ptr(int64(width))
+					pbReq.Imp[i].Banner.H = openrtb2.Int64Ptr(int64(height))
 
 					if len(params.Keywords) != 0 {
 						kvstr := prepareImpressionExt(params.Keywords)
@@ -317,12 +316,11 @@ func (a *PubmaticAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder
 func (a *PubmaticAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
 	errs := make([]error, 0, len(request.Imp))
 
-	var err error
 	wrapExt := ""
 	pubID := ""
 
 	for i := 0; i < len(request.Imp); i++ {
-		err = parseImpressionObject(&request.Imp[i], &wrapExt, &pubID)
+		err := parseImpressionObject(&request.Imp[i], &wrapExt, &pubID)
 		// If the parsing is failed, remove imp and add the error.
 		if err != nil {
 			errs = append(errs, err)
@@ -331,6 +329,11 @@ func (a *PubmaticAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ad
 		}
 	}
 
+	// If all the requests are invalid, Call to adaptor is skipped
+	if len(request.Imp) == 0 {
+		return nil, errs
+	}
+
 	if wrapExt != "" {
 		rawExt := fmt.Sprintf("{\"wrapper\": %s}", wrapExt)
 		request.Ext = json.RawMessage(rawExt)
@@ -358,13 +361,6 @@ func (a *PubmaticAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ad
 		request.App = &appCopy
 	}
 
-	thisURI := a.URI
-
-	// If all the requests are invalid, Call to adaptor is skipped
-	if len(request.Imp) == 0 {
-		return nil, errs
-	}
-
 	reqJSON, err := json.Marshal(request)
 	if err != nil {
 		errs = append(errs, err)
@@ -376,7 +372,7 @@ func (a *PubmaticAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ad
 	headers.Add("Accept", "application/json")
 	return []*adapters.RequestData{{
 		Method:  "POST",
-		Uri:     thisURI,
+		Uri:     a.URI,
 		Body:    reqJSON,
 		Headers: headers,
 	}}, errs
@@ -402,26 +398,26 @@ func validateAdSlot(adslot string, imp *openrtb2.Imp) error {
 
 		adSize := strings.Split(strings.ToLower(adSlot[1]), "x")
 		if len(adSize) != 2 {
-			return errors.New(fmt.Sprintf("Invalid size provided in adSlot %v", adSlotStr))
+			return fmt.Errorf("Invalid size provided in adSlot %v", adSlotStr)
 		}
 
 		width, err := strconv.Atoi(strings.TrimSpace(adSize[0]))
 		if err != nil {
-			return errors.New(fmt.Sprintf("Invalid width provided in adSlot %v", adSlotStr))
+			return fmt.Errorf("Invalid width provided in adSlot %v", adSlotStr)
 		}
 
 		heightStr := strings.Split(adSize[1], ":")
 		height, err := strconv.Atoi(strings.TrimSpace(heightStr[0]))
 		if err != nil {
-			return errors.New(fmt.Sprintf("Invalid height provided in adSlot %v", adSlotStr))
+			return fmt.Errorf("Invalid height provided in adSlot %v", adSlotStr)
 		}
 
 		//In case of video, size could be derived from the player size
 		if imp.Banner != nil {
-			imp.Banner = assignBannerHeightAndWidth(imp.Banner, int64(height), int64(width))
+			imp.Banner = assignBannerWidthAndHeight(imp.Banner, int64(width), int64(height))
 		}
 	} else {
-		return errors.New(fmt.Sprintf("Invalid adSlot %v", adSlotStr))
+		return fmt.Errorf("Invalid adSlot %v", adSlotStr)
 	}
 
 	return nil
@@ -432,19 +428,13 @@ func assignBannerSize(banner *openrtb2.Banner) (*openrtb2.Banner, error) {
 		return banner, nil
 	}
 
-	if len(banner.Format) == 0 {
-		return nil, errors.New(fmt.Sprintf("No sizes provided for Banner %v", banner.Format))
-	}
-
-	return assignBannerHeightAndWidth(banner, banner.Format[0].H, banner.Format[0].H), nil
+	return assignBannerWidthAndHeight(banner, banner.Format[0].W, banner.Format[0].H), nil
 }
 
-func assignBannerHeightAndWidth(banner *openrtb2.Banner, h, w int64) *openrtb2.Banner {
+func assignBannerWidthAndHeight(banner *openrtb2.Banner, w, h int64) *openrtb2.Banner {
 	bannerCopy := *banner
-
 	bannerCopy.W = openrtb2.Int64Ptr(w)
 	bannerCopy.H = openrtb2.Int64Ptr(h)
-
 	return &bannerCopy
 }
 
diff --git a/config/config.go b/config/config.go
index b18fa3c5..36ec4dc0 100644
--- a/config/config.go
+++ b/config/config.go
@@ -859,6 +859,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.gumgum.endpoint", "https://g2.gumgum.com/providers/prbds2s/bid")
 	v.SetDefault("adapters.improvedigital.endpoint", "http://ad.360yield.com/pbs")
 	v.SetDefault("adapters.inmobi.endpoint", "https://api.w.inmobi.com/showad/openrtb/bidder/prebid")
+	v.SetDefault("adapters.interactiveoffers.endpoint", "https://rtb.ioadx.com/bidRequest/?partnerId=d9e56d418c4825d466ee96c7a31bf1da6b62fa04")
 	v.SetDefault("adapters.ix.disabled", true)
 	v.SetDefault("adapters.jixie.endpoint", "https://hb.jixie.io/v2/hbsvrpost")
 	v.SetDefault("adapters.krushmedia.endpoint", "http://ads4.krushmedia.com/?c=rtb&m=req&key={{.AccountID}}")
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 8fda6a28..0120005b 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -53,6 +53,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/gumgum"
 	"github.com/prebid/prebid-server/adapters/improvedigital"
 	"github.com/prebid/prebid-server/adapters/inmobi"
+	"github.com/prebid/prebid-server/adapters/interactiveoffers"
 	"github.com/prebid/prebid-server/adapters/invibes"
 	"github.com/prebid/prebid-server/adapters/ix"
 	"github.com/prebid/prebid-server/adapters/jixie"
@@ -115,112 +116,113 @@ import (
 
 func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 	return map[openrtb_ext.BidderName]adapters.Builder{
-		openrtb_ext.Bidder33Across:         ttx.Builder,
-		openrtb_ext.BidderAcuityAds:        acuityads.Builder,
-		openrtb_ext.BidderAdf:              adf.Builder,
-		openrtb_ext.BidderAdform:           adform.Builder,
-		openrtb_ext.BidderAdgeneration:     adgeneration.Builder,
-		openrtb_ext.BidderAdhese:           adhese.Builder,
-		openrtb_ext.BidderAdkernel:         adkernel.Builder,
-		openrtb_ext.BidderAdkernelAdn:      adkernelAdn.Builder,
-		openrtb_ext.BidderAdman:            adman.Builder,
-		openrtb_ext.BidderAdmixer:          admixer.Builder,
-		openrtb_ext.BidderAdOcean:          adocean.Builder,
-		openrtb_ext.BidderAdoppler:         adoppler.Builder,
-		openrtb_ext.BidderAdpone:           adpone.Builder,
-		openrtb_ext.BidderAdot:             adot.Builder,
-		openrtb_ext.BidderAdprime:          adprime.Builder,
-		openrtb_ext.BidderAdtarget:         adtarget.Builder,
-		openrtb_ext.BidderAdtelligent:      adtelligent.Builder,
-		openrtb_ext.BidderAdvangelists:     advangelists.Builder,
-		openrtb_ext.BidderAdxcg:            adxcg.Builder,
-		openrtb_ext.BidderAdyoulike:        adyoulike.Builder,
-		openrtb_ext.BidderAJA:              aja.Builder,
-		openrtb_ext.BidderAMX:              amx.Builder,
-		openrtb_ext.BidderApplogy:          applogy.Builder,
-		openrtb_ext.BidderAppnexus:         appnexus.Builder,
-		openrtb_ext.BidderAudienceNetwork:  audienceNetwork.Builder,
-		openrtb_ext.BidderAvocet:           avocet.Builder,
-		openrtb_ext.BidderBeachfront:       beachfront.Builder,
-		openrtb_ext.BidderBeintoo:          beintoo.Builder,
-		openrtb_ext.BidderBetween:          between.Builder,
-		openrtb_ext.BidderBidmachine:       bidmachine.Builder,
-		openrtb_ext.BidderBrightroll:       brightroll.Builder,
-		openrtb_ext.BidderColossus:         colossus.Builder,
-		openrtb_ext.BidderConnectAd:        connectad.Builder,
-		openrtb_ext.BidderConsumable:       consumable.Builder,
-		openrtb_ext.BidderConversant:       conversant.Builder,
-		openrtb_ext.BidderCpmstar:          cpmstar.Builder,
-		openrtb_ext.BidderCriteo:           criteo.Builder,
-		openrtb_ext.BidderDatablocks:       datablocks.Builder,
-		openrtb_ext.BidderDecenterAds:      decenterads.Builder,
-		openrtb_ext.BidderDeepintent:       deepintent.Builder,
-		openrtb_ext.BidderDmx:              dmx.Builder,
-		openrtb_ext.BidderEmxDigital:       emx_digital.Builder,
-		openrtb_ext.BidderEngageBDR:        engagebdr.Builder,
-		openrtb_ext.BidderEPlanning:        eplanning.Builder,
-		openrtb_ext.BidderEpom:             epom.Builder,
-		openrtb_ext.BidderGamma:            gamma.Builder,
-		openrtb_ext.BidderGamoshi:          gamoshi.Builder,
-		openrtb_ext.BidderGrid:             grid.Builder,
-		openrtb_ext.BidderGumGum:           gumgum.Builder,
-		openrtb_ext.BidderImprovedigital:   improvedigital.Builder,
-		openrtb_ext.BidderInMobi:           inmobi.Builder,
-		openrtb_ext.BidderInvibes:          invibes.Builder,
-		openrtb_ext.BidderIx:               ix.Builder,
-		openrtb_ext.BidderJixie:            jixie.Builder,
-		openrtb_ext.BidderKidoz:            kidoz.Builder,
-		openrtb_ext.BidderKrushmedia:       krushmedia.Builder,
-		openrtb_ext.BidderKubient:          kubient.Builder,
-		openrtb_ext.BidderLockerDome:       lockerdome.Builder,
-		openrtb_ext.BidderLogicad:          logicad.Builder,
-		openrtb_ext.BidderLunaMedia:        lunamedia.Builder,
-		openrtb_ext.BidderMarsmedia:        marsmedia.Builder,
-		openrtb_ext.BidderMediafuse:        adtelligent.Builder,
-		openrtb_ext.BidderMgid:             mgid.Builder,
-		openrtb_ext.BidderMobfoxpb:         mobfoxpb.Builder,
-		openrtb_ext.BidderMobileFuse:       mobilefuse.Builder,
-		openrtb_ext.BidderNanoInteractive:  nanointeractive.Builder,
-		openrtb_ext.BidderNinthDecimal:     ninthdecimal.Builder,
-		openrtb_ext.BidderNoBid:            nobid.Builder,
-		openrtb_ext.BidderOneTag:           onetag.Builder,
-		openrtb_ext.BidderOpenx:            openx.Builder,
-		openrtb_ext.BidderOrbidder:         orbidder.Builder,
-		openrtb_ext.BidderOutbrain:         outbrain.Builder,
-		openrtb_ext.BidderPangle:           pangle.Builder,
-		openrtb_ext.BidderPubmatic:         pubmatic.Builder,
-		openrtb_ext.BidderPubnative:        pubnative.Builder,
-		openrtb_ext.BidderPulsepoint:       pulsepoint.Builder,
-		openrtb_ext.BidderRevcontent:       revcontent.Builder,
-		openrtb_ext.BidderRhythmone:        rhythmone.Builder,
-		openrtb_ext.BidderRTBHouse:         rtbhouse.Builder,
-		openrtb_ext.BidderRubicon:          rubicon.Builder,
-		openrtb_ext.BidderSharethrough:     sharethrough.Builder,
-		openrtb_ext.BidderSilverMob:        silvermob.Builder,
-		openrtb_ext.BidderSmaato:           smaato.Builder,
-		openrtb_ext.BidderSmartAdserver:    smartadserver.Builder,
-		openrtb_ext.BidderSmartRTB:         smartrtb.Builder,
-		openrtb_ext.BidderSmartyAds:        smartyads.Builder,
-		openrtb_ext.BidderSomoaudience:     somoaudience.Builder,
-		openrtb_ext.BidderSonobi:           sonobi.Builder,
-		openrtb_ext.BidderSovrn:            sovrn.Builder,
-		openrtb_ext.BidderSynacormedia:     synacormedia.Builder,
-		openrtb_ext.BidderTappx:            tappx.Builder,
-		openrtb_ext.BidderTelaria:          telaria.Builder,
-		openrtb_ext.BidderTriplelift:       triplelift.Builder,
-		openrtb_ext.BidderTripleliftNative: triplelift_native.Builder,
-		openrtb_ext.BidderTrustX:           grid.Builder,
-		openrtb_ext.BidderUcfunnel:         ucfunnel.Builder,
-		openrtb_ext.BidderUnicorn:          unicorn.Builder,
-		openrtb_ext.BidderUnruly:           unruly.Builder,
-		openrtb_ext.BidderValueImpression:  valueimpression.Builder,
-		openrtb_ext.BidderVerizonMedia:     verizonmedia.Builder,
-		openrtb_ext.BidderVisx:             visx.Builder,
-		openrtb_ext.BidderVrtcal:           vrtcal.Builder,
-		openrtb_ext.BidderYeahmobi:         yeahmobi.Builder,
-		openrtb_ext.BidderYieldlab:         yieldlab.Builder,
-		openrtb_ext.BidderYieldmo:          yieldmo.Builder,
-		openrtb_ext.BidderYieldone:         yieldone.Builder,
-		openrtb_ext.BidderZeroClickFraud:   zeroclickfraud.Builder,
+		openrtb_ext.Bidder33Across:          ttx.Builder,
+		openrtb_ext.BidderAcuityAds:         acuityads.Builder,
+		openrtb_ext.BidderAdf:               adf.Builder,
+		openrtb_ext.BidderAdform:            adform.Builder,
+		openrtb_ext.BidderAdgeneration:      adgeneration.Builder,
+		openrtb_ext.BidderAdhese:            adhese.Builder,
+		openrtb_ext.BidderAdkernel:          adkernel.Builder,
+		openrtb_ext.BidderAdkernelAdn:       adkernelAdn.Builder,
+		openrtb_ext.BidderAdman:             adman.Builder,
+		openrtb_ext.BidderAdmixer:           admixer.Builder,
+		openrtb_ext.BidderAdOcean:           adocean.Builder,
+		openrtb_ext.BidderAdoppler:          adoppler.Builder,
+		openrtb_ext.BidderAdpone:            adpone.Builder,
+		openrtb_ext.BidderAdot:              adot.Builder,
+		openrtb_ext.BidderAdprime:           adprime.Builder,
+		openrtb_ext.BidderAdtarget:          adtarget.Builder,
+		openrtb_ext.BidderAdtelligent:       adtelligent.Builder,
+		openrtb_ext.BidderAdvangelists:      advangelists.Builder,
+		openrtb_ext.BidderAdxcg:             adxcg.Builder,
+		openrtb_ext.BidderAdyoulike:         adyoulike.Builder,
+		openrtb_ext.BidderAJA:               aja.Builder,
+		openrtb_ext.BidderAMX:               amx.Builder,
+		openrtb_ext.BidderApplogy:           applogy.Builder,
+		openrtb_ext.BidderAppnexus:          appnexus.Builder,
+		openrtb_ext.BidderAudienceNetwork:   audienceNetwork.Builder,
+		openrtb_ext.BidderAvocet:            avocet.Builder,
+		openrtb_ext.BidderBeachfront:        beachfront.Builder,
+		openrtb_ext.BidderBeintoo:           beintoo.Builder,
+		openrtb_ext.BidderBetween:           between.Builder,
+		openrtb_ext.BidderBidmachine:        bidmachine.Builder,
+		openrtb_ext.BidderBrightroll:        brightroll.Builder,
+		openrtb_ext.BidderColossus:          colossus.Builder,
+		openrtb_ext.BidderConnectAd:         connectad.Builder,
+		openrtb_ext.BidderConsumable:        consumable.Builder,
+		openrtb_ext.BidderConversant:        conversant.Builder,
+		openrtb_ext.BidderCpmstar:           cpmstar.Builder,
+		openrtb_ext.BidderCriteo:            criteo.Builder,
+		openrtb_ext.BidderDatablocks:        datablocks.Builder,
+		openrtb_ext.BidderDecenterAds:       decenterads.Builder,
+		openrtb_ext.BidderDeepintent:        deepintent.Builder,
+		openrtb_ext.BidderDmx:               dmx.Builder,
+		openrtb_ext.BidderEmxDigital:        emx_digital.Builder,
+		openrtb_ext.BidderEngageBDR:         engagebdr.Builder,
+		openrtb_ext.BidderEPlanning:         eplanning.Builder,
+		openrtb_ext.BidderEpom:              epom.Builder,
+		openrtb_ext.BidderGamma:             gamma.Builder,
+		openrtb_ext.BidderGamoshi:           gamoshi.Builder,
+		openrtb_ext.BidderGrid:              grid.Builder,
+		openrtb_ext.BidderGumGum:            gumgum.Builder,
+		openrtb_ext.BidderImprovedigital:    improvedigital.Builder,
+		openrtb_ext.BidderInMobi:            inmobi.Builder,
+		openrtb_ext.BidderInteractiveoffers: interactiveoffers.Builder,
+		openrtb_ext.BidderInvibes:           invibes.Builder,
+		openrtb_ext.BidderIx:                ix.Builder,
+		openrtb_ext.BidderJixie:             jixie.Builder,
+		openrtb_ext.BidderKidoz:             kidoz.Builder,
+		openrtb_ext.BidderKrushmedia:        krushmedia.Builder,
+		openrtb_ext.BidderKubient:           kubient.Builder,
+		openrtb_ext.BidderLockerDome:        lockerdome.Builder,
+		openrtb_ext.BidderLogicad:           logicad.Builder,
+		openrtb_ext.BidderLunaMedia:         lunamedia.Builder,
+		openrtb_ext.BidderMarsmedia:         marsmedia.Builder,
+		openrtb_ext.BidderMediafuse:         adtelligent.Builder,
+		openrtb_ext.BidderMgid:              mgid.Builder,
+		openrtb_ext.BidderMobfoxpb:          mobfoxpb.Builder,
+		openrtb_ext.BidderMobileFuse:        mobilefuse.Builder,
+		openrtb_ext.BidderNanoInteractive:   nanointeractive.Builder,
+		openrtb_ext.BidderNinthDecimal:      ninthdecimal.Builder,
+		openrtb_ext.BidderNoBid:             nobid.Builder,
+		openrtb_ext.BidderOneTag:            onetag.Builder,
+		openrtb_ext.BidderOpenx:             openx.Builder,
+		openrtb_ext.BidderOrbidder:          orbidder.Builder,
+		openrtb_ext.BidderOutbrain:          outbrain.Builder,
+		openrtb_ext.BidderPangle:            pangle.Builder,
+		openrtb_ext.BidderPubmatic:          pubmatic.Builder,
+		openrtb_ext.BidderPubnative:         pubnative.Builder,
+		openrtb_ext.BidderPulsepoint:        pulsepoint.Builder,
+		openrtb_ext.BidderRevcontent:        revcontent.Builder,
+		openrtb_ext.BidderRhythmone:         rhythmone.Builder,
+		openrtb_ext.BidderRTBHouse:          rtbhouse.Builder,
+		openrtb_ext.BidderRubicon:           rubicon.Builder,
+		openrtb_ext.BidderSharethrough:      sharethrough.Builder,
+		openrtb_ext.BidderSilverMob:         silvermob.Builder,
+		openrtb_ext.BidderSmaato:            smaato.Builder,
+		openrtb_ext.BidderSmartAdserver:     smartadserver.Builder,
+		openrtb_ext.BidderSmartRTB:          smartrtb.Builder,
+		openrtb_ext.BidderSmartyAds:         smartyads.Builder,
+		openrtb_ext.BidderSomoaudience:      somoaudience.Builder,
+		openrtb_ext.BidderSonobi:            sonobi.Builder,
+		openrtb_ext.BidderSovrn:             sovrn.Builder,
+		openrtb_ext.BidderSynacormedia:      synacormedia.Builder,
+		openrtb_ext.BidderTappx:             tappx.Builder,
+		openrtb_ext.BidderTelaria:           telaria.Builder,
+		openrtb_ext.BidderTriplelift:        triplelift.Builder,
+		openrtb_ext.BidderTripleliftNative:  triplelift_native.Builder,
+		openrtb_ext.BidderTrustX:            grid.Builder,
+		openrtb_ext.BidderUcfunnel:          ucfunnel.Builder,
+		openrtb_ext.BidderUnicorn:           unicorn.Builder,
+		openrtb_ext.BidderUnruly:            unruly.Builder,
+		openrtb_ext.BidderValueImpression:   valueimpression.Builder,
+		openrtb_ext.BidderVerizonMedia:      verizonmedia.Builder,
+		openrtb_ext.BidderVisx:              visx.Builder,
+		openrtb_ext.BidderVrtcal:            vrtcal.Builder,
+		openrtb_ext.BidderYeahmobi:          yeahmobi.Builder,
+		openrtb_ext.BidderYieldlab:          yieldlab.Builder,
+		openrtb_ext.BidderYieldmo:           yieldmo.Builder,
+		openrtb_ext.BidderYieldone:          yieldone.Builder,
+		openrtb_ext.BidderZeroClickFraud:    zeroclickfraud.Builder,
 	}
 }
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 77f14bbc..2fb50cf3 100755
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -73,114 +73,115 @@ func IsBidderNameReserved(name string) bool {
 //
 // Please keep this list alphabetized to minimize merge conflicts.
 const (
-	Bidder33Across         BidderName = "33across"
-	BidderAcuityAds        BidderName = "acuityads"
-	BidderAdf              BidderName = "adf"
-	BidderAdform           BidderName = "adform"
-	BidderAdgeneration     BidderName = "adgeneration"
-	BidderAdhese           BidderName = "adhese"
-	BidderAdkernel         BidderName = "adkernel"
-	BidderAdkernelAdn      BidderName = "adkernelAdn"
-	BidderAdman            BidderName = "adman"
-	BidderAdmixer          BidderName = "admixer"
-	BidderAdOcean          BidderName = "adocean"
-	BidderAdoppler         BidderName = "adoppler"
-	BidderAdot             BidderName = "adot"
-	BidderAdpone           BidderName = "adpone"
-	BidderAdprime          BidderName = "adprime"
-	BidderAdtarget         BidderName = "adtarget"
-	BidderAdtelligent      BidderName = "adtelligent"
-	BidderAdvangelists     BidderName = "advangelists"
-	BidderAdxcg            BidderName = "adxcg"
-	BidderAdyoulike        BidderName = "adyoulike"
-	BidderAJA              BidderName = "aja"
-	BidderAMX              BidderName = "amx"
-	BidderApplogy          BidderName = "applogy"
-	BidderAppnexus         BidderName = "appnexus"
-	BidderAudienceNetwork  BidderName = "audienceNetwork"
-	BidderAvocet           BidderName = "avocet"
-	BidderBeachfront       BidderName = "beachfront"
-	BidderBeintoo          BidderName = "beintoo"
-	BidderBetween          BidderName = "between"
-	BidderBidmachine       BidderName = "bidmachine"
-	BidderBrightroll       BidderName = "brightroll"
-	BidderColossus         BidderName = "colossus"
-	BidderConnectAd        BidderName = "connectad"
-	BidderConsumable       BidderName = "consumable"
-	BidderConversant       BidderName = "conversant"
-	BidderCpmstar          BidderName = "cpmstar"
-	BidderCriteo           BidderName = "criteo"
-	BidderDatablocks       BidderName = "datablocks"
-	BidderDmx              BidderName = "dmx"
-	BidderDecenterAds      BidderName = "decenterads"
-	BidderDeepintent       BidderName = "deepintent"
-	BidderEmxDigital       BidderName = "emx_digital"
-	BidderEngageBDR        BidderName = "engagebdr"
-	BidderEPlanning        BidderName = "eplanning"
-	BidderEpom             BidderName = "epom"
-	BidderGamma            BidderName = "gamma"
-	BidderGamoshi          BidderName = "gamoshi"
-	BidderGrid             BidderName = "grid"
-	BidderGumGum           BidderName = "gumgum"
-	BidderImprovedigital   BidderName = "improvedigital"
-	BidderInMobi           BidderName = "inmobi"
-	BidderInvibes          BidderName = "invibes"
-	BidderIx               BidderName = "ix"
-	BidderJixie            BidderName = "jixie"
-	BidderKidoz            BidderName = "kidoz"
-	BidderKrushmedia       BidderName = "krushmedia"
-	BidderKubient          BidderName = "kubient"
-	BidderLifestreet       BidderName = "lifestreet"
-	BidderLockerDome       BidderName = "lockerdome"
-	BidderLogicad          BidderName = "logicad"
-	BidderLunaMedia        BidderName = "lunamedia"
-	BidderMarsmedia        BidderName = "marsmedia"
-	BidderMediafuse        BidderName = "mediafuse"
-	BidderMgid             BidderName = "mgid"
-	BidderMobfoxpb         BidderName = "mobfoxpb"
-	BidderMobileFuse       BidderName = "mobilefuse"
-	BidderNanoInteractive  BidderName = "nanointeractive"
-	BidderNinthDecimal     BidderName = "ninthdecimal"
-	BidderNoBid            BidderName = "nobid"
-	BidderOneTag           BidderName = "onetag"
-	BidderOpenx            BidderName = "openx"
-	BidderOrbidder         BidderName = "orbidder"
-	BidderOutbrain         BidderName = "outbrain"
-	BidderPangle           BidderName = "pangle"
-	BidderPubmatic         BidderName = "pubmatic"
-	BidderPubnative        BidderName = "pubnative"
-	BidderPulsepoint       BidderName = "pulsepoint"
-	BidderRevcontent       BidderName = "revcontent"
-	BidderRhythmone        BidderName = "rhythmone"
-	BidderRTBHouse         BidderName = "rtbhouse"
-	BidderRubicon          BidderName = "rubicon"
-	BidderSharethrough     BidderName = "sharethrough"
-	BidderSilverMob        BidderName = "silvermob"
-	BidderSmaato           BidderName = "smaato"
-	BidderSmartAdserver    BidderName = "smartadserver"
-	BidderSmartRTB         BidderName = "smartrtb"
-	BidderSmartyAds        BidderName = "smartyads"
-	BidderSomoaudience     BidderName = "somoaudience"
-	BidderSonobi           BidderName = "sonobi"
-	BidderSovrn            BidderName = "sovrn"
-	BidderSynacormedia     BidderName = "synacormedia"
-	BidderTappx            BidderName = "tappx"
-	BidderTelaria          BidderName = "telaria"
-	BidderTriplelift       BidderName = "triplelift"
-	BidderTripleliftNative BidderName = "triplelift_native"
-	BidderTrustX           BidderName = "trustx"
-	BidderUcfunnel         BidderName = "ucfunnel"
-	BidderUnicorn          BidderName = "unicorn"
-	BidderUnruly           BidderName = "unruly"
-	BidderValueImpression  BidderName = "valueimpression"
-	BidderVerizonMedia     BidderName = "verizonmedia"
-	BidderVisx             BidderName = "visx"
-	BidderVrtcal           BidderName = "vrtcal"
-	BidderYeahmobi         BidderName = "yeahmobi"
-	BidderYieldlab         BidderName = "yieldlab"
-	BidderYieldmo          BidderName = "yieldmo"
-	BidderYieldone         BidderName = "yieldone"
-	BidderZeroClickFraud   BidderName = "zeroclickfraud"
+	Bidder33Across          BidderName = "33across"
+	BidderAcuityAds         BidderName = "acuityads"
+	BidderAdf               BidderName = "adf"
+	BidderAdform            BidderName = "adform"
+	BidderAdgeneration      BidderName = "adgeneration"
+	BidderAdhese            BidderName = "adhese"
+	BidderAdkernel          BidderName = "adkernel"
+	BidderAdkernelAdn       BidderName = "adkernelAdn"
+	BidderAdman             BidderName = "adman"
+	BidderAdmixer           BidderName = "admixer"
+	BidderAdOcean           BidderName = "adocean"
+	BidderAdoppler          BidderName = "adoppler"
+	BidderAdot              BidderName = "adot"
+	BidderAdpone            BidderName = "adpone"
+	BidderAdprime           BidderName = "adprime"
+	BidderAdtarget          BidderName = "adtarget"
+	BidderAdtelligent       BidderName = "adtelligent"
+	BidderAdvangelists      BidderName = "advangelists"
+	BidderAdxcg             BidderName = "adxcg"
+	BidderAdyoulike         BidderName = "adyoulike"
+	BidderAJA               BidderName = "aja"
+	BidderAMX               BidderName = "amx"
+	BidderApplogy           BidderName = "applogy"
+	BidderAppnexus          BidderName = "appnexus"
+	BidderAudienceNetwork   BidderName = "audienceNetwork"
+	BidderAvocet            BidderName = "avocet"
+	BidderBeachfront        BidderName = "beachfront"
+	BidderBeintoo           BidderName = "beintoo"
+	BidderBetween           BidderName = "between"
+	BidderBidmachine        BidderName = "bidmachine"
+	BidderBrightroll        BidderName = "brightroll"
+	BidderColossus          BidderName = "colossus"
+	BidderConnectAd         BidderName = "connectad"
+	BidderConsumable        BidderName = "consumable"
+	BidderConversant        BidderName = "conversant"
+	BidderCpmstar           BidderName = "cpmstar"
+	BidderCriteo            BidderName = "criteo"
+	BidderDatablocks        BidderName = "datablocks"
+	BidderDmx               BidderName = "dmx"
+	BidderDecenterAds       BidderName = "decenterads"
+	BidderDeepintent        BidderName = "deepintent"
+	BidderEmxDigital        BidderName = "emx_digital"
+	BidderEngageBDR         BidderName = "engagebdr"
+	BidderEPlanning         BidderName = "eplanning"
+	BidderEpom              BidderName = "epom"
+	BidderGamma             BidderName = "gamma"
+	BidderGamoshi           BidderName = "gamoshi"
+	BidderGrid              BidderName = "grid"
+	BidderGumGum            BidderName = "gumgum"
+	BidderImprovedigital    BidderName = "improvedigital"
+	BidderInMobi            BidderName = "inmobi"
+	BidderInteractiveoffers BidderName = "interactiveoffers"
+	BidderInvibes           BidderName = "invibes"
+	BidderIx                BidderName = "ix"
+	BidderJixie             BidderName = "jixie"
+	BidderKidoz             BidderName = "kidoz"
+	BidderKrushmedia        BidderName = "krushmedia"
+	BidderKubient           BidderName = "kubient"
+	BidderLifestreet        BidderName = "lifestreet"
+	BidderLockerDome        BidderName = "lockerdome"
+	BidderLogicad           BidderName = "logicad"
+	BidderLunaMedia         BidderName = "lunamedia"
+	BidderMarsmedia         BidderName = "marsmedia"
+	BidderMediafuse         BidderName = "mediafuse"
+	BidderMgid              BidderName = "mgid"
+	BidderMobfoxpb          BidderName = "mobfoxpb"
+	BidderMobileFuse        BidderName = "mobilefuse"
+	BidderNanoInteractive   BidderName = "nanointeractive"
+	BidderNinthDecimal      BidderName = "ninthdecimal"
+	BidderNoBid             BidderName = "nobid"
+	BidderOneTag            BidderName = "onetag"
+	BidderOpenx             BidderName = "openx"
+	BidderOrbidder          BidderName = "orbidder"
+	BidderOutbrain          BidderName = "outbrain"
+	BidderPangle            BidderName = "pangle"
+	BidderPubmatic          BidderName = "pubmatic"
+	BidderPubnative         BidderName = "pubnative"
+	BidderPulsepoint        BidderName = "pulsepoint"
+	BidderRevcontent        BidderName = "revcontent"
+	BidderRhythmone         BidderName = "rhythmone"
+	BidderRTBHouse          BidderName = "rtbhouse"
+	BidderRubicon           BidderName = "rubicon"
+	BidderSharethrough      BidderName = "sharethrough"
+	BidderSilverMob         BidderName = "silvermob"
+	BidderSmaato            BidderName = "smaato"
+	BidderSmartAdserver     BidderName = "smartadserver"
+	BidderSmartRTB          BidderName = "smartrtb"
+	BidderSmartyAds         BidderName = "smartyads"
+	BidderSomoaudience      BidderName = "somoaudience"
+	BidderSonobi            BidderName = "sonobi"
+	BidderSovrn             BidderName = "sovrn"
+	BidderSynacormedia      BidderName = "synacormedia"
+	BidderTappx             BidderName = "tappx"
+	BidderTelaria           BidderName = "telaria"
+	BidderTriplelift        BidderName = "triplelift"
+	BidderTripleliftNative  BidderName = "triplelift_native"
+	BidderTrustX            BidderName = "trustx"
+	BidderUcfunnel          BidderName = "ucfunnel"
+	BidderUnicorn           BidderName = "unicorn"
+	BidderUnruly            BidderName = "unruly"
+	BidderValueImpression   BidderName = "valueimpression"
+	BidderVerizonMedia      BidderName = "verizonmedia"
+	BidderVisx              BidderName = "visx"
+	BidderVrtcal            BidderName = "vrtcal"
+	BidderYeahmobi          BidderName = "yeahmobi"
+	BidderYieldlab          BidderName = "yieldlab"
+	BidderYieldmo           BidderName = "yieldmo"
+	BidderYieldone          BidderName = "yieldone"
+	BidderZeroClickFraud    BidderName = "zeroclickfraud"
 )
 
 // CoreBidderNames returns a slice of all core bidders.
@@ -237,6 +238,7 @@ func CoreBidderNames() []BidderName {
 		BidderGumGum,
 		BidderImprovedigital,
 		BidderInMobi,
+		BidderInteractiveoffers,
 		BidderInvibes,
 		BidderIx,
 		BidderJixie,
diff --git a/openrtb_ext/imp_interactiveoffers.go b/openrtb_ext/imp_interactiveoffers.go
new file mode 100644
index 00000000..2f8fdf03
--- /dev/null
+++ b/openrtb_ext/imp_interactiveoffers.go
@@ -0,0 +1,5 @@
+package openrtb_ext
+
+type ExtImpInteractiveoffers struct {
+	PubID int `json:"pubid"`
+}
diff --git a/static/bidder-info/interactiveoffers.yaml b/static/bidder-info/interactiveoffers.yaml
new file mode 100644
index 00000000..56d0d5de
--- /dev/null
+++ b/static/bidder-info/interactiveoffers.yaml
@@ -0,0 +1,9 @@
+maintainer:
+  email: "dev@interactiveoffers.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+  site:
+    mediaTypes:
+      - banner
\ No newline at end of file
diff --git a/static/bidder-params/interactiveoffers.json b/static/bidder-params/interactiveoffers.json
new file mode 100644
index 00000000..79338dcc
--- /dev/null
+++ b/static/bidder-params/interactiveoffers.json
@@ -0,0 +1,13 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Interactive Offers Adapter Params",
+  "description": "A schema which validates params accepted by Interactive Offers adapter",
+  "type": "object",
+  "properties": {
+    "pubid": {
+      "type": "integer",
+      "description": "The publisher id"
+    }
+  },
+  "required": ["pubid"]
+}
\ No newline at end of file


Test patch:
diff --git a/adapters/interactiveoffers/interactiveoffers_test.go b/adapters/interactiveoffers/interactiveoffers_test.go
new file mode 100644
index 00000000..5746f123
--- /dev/null
+++ b/adapters/interactiveoffers/interactiveoffers_test.go
@@ -0,0 +1,20 @@
+package interactiveoffers
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderInteractiveoffers, config.Adapter{
+		Endpoint: "https://rtb.ioadx.com/bidRequest/?partnerId=d9e56d418c4825d466ee96c7a31bf1da6b62fa04"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "interactiveofferstest", bidder)
+}
diff --git a/adapters/interactiveoffers/interactiveofferstest/exemplary/goodmultiplebidrequest.json b/adapters/interactiveoffers/interactiveofferstest/exemplary/goodmultiplebidrequest.json
new file mode 100644
index 00000000..946289e5
--- /dev/null
+++ b/adapters/interactiveoffers/interactiveofferstest/exemplary/goodmultiplebidrequest.json
@@ -0,0 +1,136 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "https://good.site/url"
+    },
+    "imp": [{
+      "id": "test-imp-id",
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      },
+      "ext": {
+        "bidder": {
+          "pubid": 35
+        }
+      }
+    },{
+      "id": "test-imp-id-2",
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      },
+      "ext": {
+        "bidder": {
+          "pubid": 35
+        }
+      }
+    }]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://rtb.ioadx.com/bidRequest/?partnerId=d9e56d418c4825d466ee96c7a31bf1da6b62fa04",
+      "body": {
+        "id": "test-request-id",
+        "site": {
+          "page": "https://good.site/url"
+        },
+        "imp": [{
+          "id": "test-imp-id",
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "ext": {
+            "bidder": {
+              "pubid": 35
+            }
+          }
+        },{
+          "id": "test-imp-id-2",
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "ext": {
+            "bidder": {
+              "pubid": 35
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "test-request-id",
+        "seatbid": [{
+          "seat": "interactiveoffers",
+          "bid": [{
+            "id": "randomid",
+            "impid": "test-imp-id",
+            "price": 0.500000,
+            "adid": "12345678",
+            "adm": "some-test-ad",
+            "cid": "987",
+            "crid": "12345678",
+            "h": 250,
+            "w": 300
+          },{
+            "id": "randomid2",
+            "impid": "test-imp-id-2",
+            "price": 0.500000,
+            "adid": "12345678",
+            "adm": "some-test-ad",
+            "cid": "987",
+            "crid": "12345678",
+            "h": 250,
+            "w": 300
+          }]
+        }],
+        "cur": "USD"
+      }
+    }
+  }],
+
+  "expectedBidResponses": [{
+    "currency": "USD",
+    "bids": [{
+      "bid": {
+        "id": "randomid",
+        "impid": "test-imp-id",
+        "price": 0.5,
+        "adm": "some-test-ad",
+        "adid": "12345678",
+        "cid": "987",
+        "crid": "12345678",
+        "w": 300,
+        "h": 250
+      },
+      "type": "banner"
+    },{
+      "bid": {
+        "id": "randomid2",
+        "impid": "test-imp-id-2",
+        "price": 0.5,
+        "adm": "some-test-ad",
+        "adid": "12345678",
+        "cid": "987",
+        "crid": "12345678",
+        "w": 300,
+        "h": 250
+      },
+      "type": "banner"
+    }]
+  }]
+}
\ No newline at end of file
diff --git a/adapters/interactiveoffers/interactiveofferstest/exemplary/goodsinglebidrequest.json b/adapters/interactiveoffers/interactiveofferstest/exemplary/goodsinglebidrequest.json
new file mode 100644
index 00000000..2f49d545
--- /dev/null
+++ b/adapters/interactiveoffers/interactiveofferstest/exemplary/goodsinglebidrequest.json
@@ -0,0 +1,87 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "https://good.site/url"
+    },
+    "imp": [{
+      "id": "test-imp-id",
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      },
+      "ext": {
+        "bidder": {
+          "pubid": 35
+        }
+      }
+    }]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://rtb.ioadx.com/bidRequest/?partnerId=d9e56d418c4825d466ee96c7a31bf1da6b62fa04",
+      "body": {
+        "id": "test-request-id",
+        "site": {
+          "page": "https://good.site/url"
+        },
+        "imp": [{
+          "id": "test-imp-id",
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "ext": {
+            "bidder": {
+              "pubid": 35
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "test-request-id",
+        "seatbid": [{
+          "seat": "interactiveoffers",
+          "bid": [{
+            "id": "randomid",
+            "impid": "test-imp-id",
+            "price": 0.500000,
+            "adid": "12345678",
+            "adm": "some-test-ad",
+            "cid": "987",
+            "crid": "12345678",
+            "h": 250,
+            "w": 300
+          }]
+        }],
+        "cur": "USD"
+      }
+    }
+  }],
+
+  "expectedBidResponses": [{
+    "currency": "USD",
+    "bids": [{
+      "bid": {
+        "id": "randomid",
+        "impid": "test-imp-id",
+        "price": 0.5,
+        "adm": "some-test-ad",
+        "adid": "12345678",
+        "cid": "987",
+        "crid": "12345678",
+        "w": 300,
+        "h": 250
+      },
+      "type": "banner"
+    }]
+  }]
+}
\ No newline at end of file
diff --git a/adapters/interactiveoffers/interactiveofferstest/params/race/banner.json b/adapters/interactiveoffers/interactiveofferstest/params/race/banner.json
new file mode 100644
index 00000000..d81c02a5
--- /dev/null
+++ b/adapters/interactiveoffers/interactiveofferstest/params/race/banner.json
@@ -0,0 +1,3 @@
+{
+  "pubid": 35
+}
diff --git a/adapters/interactiveoffers/interactiveofferstest/supplemental/204.json b/adapters/interactiveoffers/interactiveofferstest/supplemental/204.json
new file mode 100644
index 00000000..0d56311a
--- /dev/null
+++ b/adapters/interactiveoffers/interactiveofferstest/supplemental/204.json
@@ -0,0 +1,55 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "https://good.site/url"
+    },
+    "imp": [{
+      "id": "test-imp-id",
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      },
+      "ext": {
+        "bidder": {
+          "pubid": 35
+        }
+      }
+    }]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://rtb.ioadx.com/bidRequest/?partnerId=d9e56d418c4825d466ee96c7a31bf1da6b62fa04",
+      "body": {
+        "id": "test-request-id",
+        "site": {
+          "page": "https://good.site/url"
+        },
+        "imp": [{
+          "id": "test-imp-id",
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "ext": {
+            "bidder": {
+              "pubid": 35
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse": {
+      "status": 204,
+      "body": {}
+    }
+  }],
+
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": []
+}
diff --git a/adapters/interactiveoffers/interactiveofferstest/supplemental/400.json b/adapters/interactiveoffers/interactiveofferstest/supplemental/400.json
new file mode 100644
index 00000000..9aaf12cb
--- /dev/null
+++ b/adapters/interactiveoffers/interactiveofferstest/supplemental/400.json
@@ -0,0 +1,60 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "https://good.site/url"
+    },
+    "imp": [{
+      "id": "test-imp-id",
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      },
+      "ext": {
+        "bidder": {
+          "pubid": 35
+        }
+      }
+    }]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://rtb.ioadx.com/bidRequest/?partnerId=d9e56d418c4825d466ee96c7a31bf1da6b62fa04",
+      "body": {
+        "id": "test-request-id",
+        "site": {
+          "page": "https://good.site/url"
+        },
+        "imp": [{
+          "id": "test-imp-id",
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "ext": {
+            "bidder": {
+              "pubid": 35
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse": {
+      "status": 400,
+      "body": ""
+    }
+  }],
+
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/interactiveoffers/interactiveofferstest/supplemental/not200.json b/adapters/interactiveoffers/interactiveofferstest/supplemental/not200.json
new file mode 100644
index 00000000..222be912
--- /dev/null
+++ b/adapters/interactiveoffers/interactiveofferstest/supplemental/not200.json
@@ -0,0 +1,60 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "https://good.site/url"
+    },
+    "imp": [{
+      "id": "test-imp-id",
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      },
+      "ext": {
+        "bidder": {
+          "pubid": 35
+        }
+      }
+    }]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://rtb.ioadx.com/bidRequest/?partnerId=d9e56d418c4825d466ee96c7a31bf1da6b62fa04",
+      "body": {
+        "id": "test-request-id",
+        "site": {
+          "page": "https://good.site/url"
+        },
+        "imp": [{
+          "id": "test-imp-id",
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "ext": {
+            "bidder": {
+              "pubid": 35
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse": {
+      "status": 202,
+      "body": ""
+    }
+  }],
+
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 202. Run with request.debug = 1 for more info.",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/interactiveoffers/interactiveofferstest/supplemental/wrongjsonresponse.json b/adapters/interactiveoffers/interactiveofferstest/supplemental/wrongjsonresponse.json
new file mode 100644
index 00000000..7ae16d4a
--- /dev/null
+++ b/adapters/interactiveoffers/interactiveofferstest/supplemental/wrongjsonresponse.json
@@ -0,0 +1,60 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "https://good.site/url"
+    },
+    "imp": [{
+      "id": "test-imp-id",
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      },
+      "ext": {
+        "bidder": {
+          "pubid": 35
+        }
+      }
+    }]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://rtb.ioadx.com/bidRequest/?partnerId=d9e56d418c4825d466ee96c7a31bf1da6b62fa04",
+      "body": {
+        "id": "test-request-id",
+        "site": {
+          "page": "https://good.site/url"
+        },
+        "imp": [{
+          "id": "test-imp-id",
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "ext": {
+            "bidder": {
+              "pubid": 35
+            }
+          }
+        }]
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": ""
+    }
+  }],
+
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/interactiveoffers/params_test.go b/adapters/interactiveoffers/params_test.go
new file mode 100644
index 00000000..754be842
--- /dev/null
+++ b/adapters/interactiveoffers/params_test.go
@@ -0,0 +1,44 @@
+package interactiveoffers
+
+import (
+	"encoding/json"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"testing"
+)
+
+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.BidderInteractiveoffers, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected interactiveoffers params: %s", validParam)
+		}
+	}
+}
+
+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.BidderInteractiveoffers, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"pubid":35}`,
+}
+
+var invalidParams = []string{
+	`{"pubid":"35"}`,
+	`{"pubId":35}`,
+	`{"PubId":35}`,
+	`{}`,
+}
diff --git a/adapters/ix/ix_test.go b/adapters/ix/ix_test.go
index bc70f399..d292273a 100644
--- a/adapters/ix/ix_test.go
+++ b/adapters/ix/ix_test.go
@@ -792,8 +792,8 @@ func TestIxMakeBidsWithCategoryDuration(t *testing.T) {
 	if bidResponse.Bids[0].BidVideo.Duration != expectedBidDuration {
 		t.Errorf("video duration should be set")
 	}
-	if bidResponse.Bids[0].BidVideo.PrimaryCategory != expectedBidCategory {
-		t.Errorf("video category should be set")
+	if bidResponse.Bids[0].Bid.Cat[0] != expectedBidCategory {
+		t.Errorf("bid category should be set")
 	}
 	if len(errors) != expectedErrorCount {
 		t.Errorf("should not have any errors, errors=%v", errors)
diff --git a/adapters/pubmatic/pubmatictest/exemplary/simple-banner.json b/adapters/pubmatic/pubmatictest/exemplary/banner.json
similarity index 100%
rename from adapters/pubmatic/pubmatictest/exemplary/simple-banner.json
rename to adapters/pubmatic/pubmatictest/exemplary/banner.json
diff --git a/adapters/pubmatic/pubmatictest/supplemental/noAdSlot.json b/adapters/pubmatic/pubmatictest/supplemental/noAdSlot.json
new file mode 100644
index 00000000..c66a7189
--- /dev/null
+++ b/adapters/pubmatic/pubmatictest/supplemental/noAdSlot.json
@@ -0,0 +1,129 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [{
+            "id": "test-imp-id",
+            "banner": {
+                "format": [{
+                    "w": 300,
+                    "h": 250
+                }]
+            },
+            "ext": {
+                "bidder": {
+                    "publisherId": "999",
+                    "keywords": [{
+                            "key": "pmZoneID",
+                            "value": ["Zone1", "Zone2"]
+                        },
+                        {
+                            "key": "preference",
+                            "value": ["sports", "movies"]
+                        }
+                    ],
+                    "wrapper": {
+                        "version": 1,
+                        "profile": 5123
+                    }
+                }
+            }
+        }],
+        "device": {
+            "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
+        },
+        "site": {
+            "id": "siteID",
+            "publisher": {
+                "id": "1234"
+            }
+        }
+    },
+
+    "httpCalls": [{
+        "expectedRequest": {
+            "uri": "https://hbopenbid.pubmatic.com/translator?source=prebid-server",
+            "body": {
+                "id": "test-request-id",
+                "imp": [{
+                    "id": "test-imp-id",
+                    "banner": {
+                        "format": [{
+                            "w": 300,
+                            "h": 250
+                        }],
+                        "w": 300,
+                        "h": 250
+                    },
+                    "ext": {
+                        "pmZoneID": "Zone1,Zone2",
+                        "preference": "sports,movies"
+                    }
+                }],
+                "device": {
+                    "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
+                },
+                "site": {
+                    "id": "siteID",
+                    "publisher": {
+                        "id": "999"
+                    }
+                },
+                "ext": {
+                    "wrapper": {
+                        "profile": 5123,
+                        "version": 1
+                    }
+                }
+            }
+        },
+        "mockResponse": {
+            "status": 200,
+            "body": {
+                "id": "test-request-id",
+                "seatbid": [{
+                    "seat": "958",
+                    "bid": [{
+                        "id": "7706636740145184841",
+                        "impid": "test-imp-id",
+                        "price": 0.500000,
+                        "adid": "29681110",
+                        "adm": "some-test-ad",
+                        "adomain": ["pubmatic.com"],
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "dealid": "test deal",
+                        "ext": {
+                            "dspid": 6,
+                            "deal_channel": 1
+                        }
+                    }]
+                }],
+                "bidid": "5778926625248726496",
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedBidResponses": [{
+        "currency": "USD",
+        "bids": [{
+            "bid": {
+                "id": "7706636740145184841",
+                "impid": "test-imp-id",
+                "price": 0.5,
+                "adid": "29681110",
+                "adm": "some-test-ad",
+                "adomain": ["pubmatic.com"],
+                "crid": "29681110",
+                "w": 300,
+                "h": 250,
+                "dealid": "test deal",
+                "ext": {
+                    "dspid": 6,
+                    "deal_channel": 1
+                }
+            },
+            "type": "banner"
+        }]
+    }]
+}
\ No newline at end of file
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 6d46a4a4..79bf84e3 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -104,28 +104,29 @@ func TestNewSyncerMap(t *testing.T) {
 	}
 
 	adaptersWithoutSyncers := map[openrtb_ext.BidderName]bool{
-		openrtb_ext.BidderAdgeneration: true,
-		openrtb_ext.BidderAdhese:       true,
-		openrtb_ext.BidderAdoppler:     true,
-		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,
-		openrtb_ext.BidderKidoz:        true,
-		openrtb_ext.BidderKubient:      true,
-		openrtb_ext.BidderMobfoxpb:     true,
-		openrtb_ext.BidderMobileFuse:   true,
-		openrtb_ext.BidderOrbidder:     true,
-		openrtb_ext.BidderPangle:       true,
-		openrtb_ext.BidderPubnative:    true,
-		openrtb_ext.BidderRevcontent:   true,
-		openrtb_ext.BidderSilverMob:    true,
-		openrtb_ext.BidderSmaato:       true,
-		openrtb_ext.BidderUnicorn:      true,
-		openrtb_ext.BidderYeahmobi:     true,
+		openrtb_ext.BidderAdgeneration:      true,
+		openrtb_ext.BidderAdhese:            true,
+		openrtb_ext.BidderAdoppler:          true,
+		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,
+		openrtb_ext.BidderInteractiveoffers: true,
+		openrtb_ext.BidderKidoz:             true,
+		openrtb_ext.BidderKubient:           true,
+		openrtb_ext.BidderMobfoxpb:          true,
+		openrtb_ext.BidderMobileFuse:        true,
+		openrtb_ext.BidderOrbidder:          true,
+		openrtb_ext.BidderPangle:            true,
+		openrtb_ext.BidderPubnative:         true,
+		openrtb_ext.BidderRevcontent:        true,
+		openrtb_ext.BidderSilverMob:         true,
+		openrtb_ext.BidderSmaato:            true,
+		openrtb_ext.BidderUnicorn:           true,
+		openrtb_ext.BidderYeahmobi:          true,
 	}
 
 	for bidder, config := range cfg.Adapters {