Repository: prebid/prebid-server
Pull request #2308 (2022-07-15 18:00:23)

Add Request Level and Imp Level Passthrough Value to Bid Response
This PR addresses the issue outlined here https://github.com/prebid/prebid-server/issues/2186

The goal of the PR is to allow for a `passthrough` value to be defined at `bidrequest.ext.prebid.passthrough` and for that value to be added to the bid response at `bidresponse.ext.prebid.passthrough`

The other goal is to allow another `passthrough` value to be defined at the imp level in the request at `bidrequest.imp.ext.prebid.passthrough` and have that value be added to the bid response at `bidresponse.seatbid[].bid[].ext.prebid.passthrough ` where the `impId` of the `passthrough` value matches the `impId` of the `bid`

To main way we accomplish the first goal is to alter `makeExtBidResponse()` which is called during the `HoldAuction()` flow. We alter the `ExtRequestPrebid` struct to include the `passthrough` value of type `json.RawMessage`, and then we just pass the `requestExt.Prebid` object to the `makeExtBidResponse()` function and apply the `passthrough` value to the `bidresponseExt.Prebid `struct. 

To accomplish the imp level passthrough implementation, we just have to update the `processStoredRequests()` function, which parses through `ImpExtInfo` to build an `ImpExtInfoMap`, which maps the `impId` to the `ExtInfo` tied to that imp. We first update the `ExtImpPrebid` struct to include the `passthrough` value of type `json.RawMessage`, and when we get to the part of the function where the `ImpExtInfoMap` is being updated, we just also include the `passthrough` value in that mapping, so that we can use it later on when building the response.

So when the response is being built, `makeBidExtJSON()` is called, and here is where `ImpExtInfoMap` is used. We simply take the `ext.Prebid` object that is being populated, and add the `passthrough` value to the object using the map, `ImpExtInfoMap[impId].passthrough`.

Unit tests and end to end tests were updated or added to accompany the changes through `exchange_test.go 
`





Patch:
diff --git a/Dockerfile b/Dockerfile
index 9c869f6a..92cd74e7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,8 +16,8 @@ RUN apt-get update && \
     apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
 ENV CGO_ENABLED 0
 COPY ./ ./
-RUN go mod vendor
 RUN go mod tidy
+RUN go mod vendor
 ARG TEST="true"
 RUN if [ "$TEST" != "false" ]; then ./validate.sh ; fi
 RUN go build -mod=vendor -ldflags "-X github.com/prebid/prebid-server/version.Ver=`git describe --tags | sed 's/^v//'` -X github.com/prebid/prebid-server/version.Rev=`git rev-parse HEAD`" .
diff --git a/adapters/adnuntius/adnuntius.go b/adapters/adnuntius/adnuntius.go
index 9fe8cdbf..19c52166 100644
--- a/adapters/adnuntius/adnuntius.go
+++ b/adapters/adnuntius/adnuntius.go
@@ -22,8 +22,13 @@ type adapter struct {
 	endpoint string
 }
 type adnAdunit struct {
-	AuId     string `json:"auId"`
-	TargetId string `json:"targetId"`
+	AuId       string    `json:"auId"`
+	TargetId   string    `json:"targetId"`
+	Dimensions [][]int64 `json:"dimensions,omitempty"`
+}
+
+type extDeviceAdnuntius struct {
+	NoCookies bool `json:"noCookies,omitempty"`
 }
 
 type AdnResponse struct {
@@ -101,6 +106,13 @@ func makeEndpointUrl(ortbRequest openrtb2.BidRequest, a *adapter) (string, []err
 		return "", []error{fmt.Errorf("failed to parse Adnuntius endpoint: %v", err)}
 	}
 
+	var deviceExt extDeviceAdnuntius
+	if ortbRequest.Device != nil && ortbRequest.Device.Ext != nil {
+		if err := json.Unmarshal(ortbRequest.Device.Ext, &deviceExt); err != nil {
+			return "", []error{fmt.Errorf("failed to parse Adnuntius endpoint: %v", err)}
+		}
+	}
+
 	_, offset := a.time.Now().Zone()
 	tzo := -offset / minutesInHour
 
@@ -109,6 +121,11 @@ func makeEndpointUrl(ortbRequest openrtb2.BidRequest, a *adapter) (string, []err
 		q.Set("gdpr", gdpr)
 		q.Set("consentString", consent)
 	}
+
+	if deviceExt.NoCookies {
+		q.Set("noCookies", "true")
+	}
+
 	q.Set("tzo", fmt.Sprint(tzo))
 	q.Set("format", "json")
 
@@ -116,6 +133,26 @@ func makeEndpointUrl(ortbRequest openrtb2.BidRequest, a *adapter) (string, []err
 	return url, nil
 }
 
+func getImpSizes(imp openrtb2.Imp) [][]int64 {
+
+	if len(imp.Banner.Format) > 0 {
+		sizes := make([][]int64, len(imp.Banner.Format))
+		for i, format := range imp.Banner.Format {
+			sizes[i] = []int64{format.W, format.H}
+		}
+
+		return sizes
+	}
+
+	if imp.Banner.W != nil && imp.Banner.H != nil {
+		size := make([][]int64, 1)
+		size[0] = []int64{*imp.Banner.W, *imp.Banner.H}
+		return size
+	}
+
+	return nil
+}
+
 /*
 	Generate the requests to Adnuntius to reduce the amount of requests going out.
 */
@@ -159,8 +196,9 @@ func (a *adapter) generateRequests(ortbRequest openrtb2.BidRequest) ([]*adapters
 		networkAdunitMap[network] = append(
 			networkAdunitMap[network],
 			adnAdunit{
-				AuId:     adnuntiusExt.Auid,
-				TargetId: fmt.Sprintf("%s-%s", adnuntiusExt.Auid, imp.ID),
+				AuId:       adnuntiusExt.Auid,
+				TargetId:   fmt.Sprintf("%s-%s", adnuntiusExt.Auid, imp.ID),
+				Dimensions: getImpSizes(imp),
 			})
 	}
 
diff --git a/adapters/adrino/adrino.go b/adapters/adrino/adrino.go
new file mode 100644
index 00000000..d9fe448a
--- /dev/null
+++ b/adapters/adrino/adrino.go
@@ -0,0 +1,85 @@
+package adrino
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/mxmCherry/openrtb/v16/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, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var errs []error
+
+	reqJson, err := json.Marshal(request)
+	if err != nil {
+		errs = append(errs, err)
+		return nil, errs
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+
+	return []*adapters.RequestData{{
+		Method:  "POST",
+		Uri:     a.endpoint,
+		Body:    reqJson,
+		Headers: headers,
+	}}, errs
+}
+
+func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	var errs []error
+
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if response.StatusCode == http.StatusBadRequest {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
+		}}
+	}
+
+	if response.StatusCode != http.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
+		}}
+	}
+
+	var bidResp openrtb2.BidResponse
+
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponse()
+	bidResponse.Currency = bidResp.Cur
+
+	for _, seatBid := range bidResp.SeatBid {
+		for i := range seatBid.Bid {
+			bidType := openrtb_ext.BidTypeNative // our adserver supports only native ads
+			b := &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: bidType,
+			}
+			bidResponse.Bids = append(bidResponse.Bids, b)
+		}
+	}
+	return bidResponse, errs
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+	return bidder, nil
+}
diff --git a/adapters/adtrgtme/adtrgtme.go b/adapters/adtrgtme/adtrgtme.go
new file mode 100644
index 00000000..7a099372
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtme.go
@@ -0,0 +1,197 @@
+package adtrgtme
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/mxmCherry/openrtb/v16/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
+}
+
+// Builder builds a new instance of the Adtrgtme 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
+}
+
+func (v *adapter) MakeRequests(
+	openRTBRequest *openrtb2.BidRequest,
+	requestInfo *adapters.ExtraRequestInfo,
+) (
+	[]*adapters.RequestData,
+	[]error,
+) {
+	var requests []*adapters.RequestData
+	var errors []error
+
+	requestCopy := *openRTBRequest
+
+	for _, imp := range openRTBRequest.Imp {
+		requestCopy.Imp = []openrtb2.Imp{imp}
+
+		requestJSON, err := json.Marshal(openRTBRequest)
+		if err != nil {
+			errors = append(errors, err)
+			continue
+		}
+
+		requestURI, err := v.buildRequestURI(&requestCopy)
+		if err != nil {
+			errors = append(errors, err)
+			continue
+		}
+
+		requestData := &adapters.RequestData{
+			Method:  http.MethodPost,
+			Uri:     requestURI,
+			Body:    requestJSON,
+			Headers: makeRequestHeaders(&requestCopy),
+		}
+
+		requests = append(requests, requestData)
+	}
+
+	return requests, errors
+}
+
+func (v *adapter) buildRequestURI(openRTBRequest *openrtb2.BidRequest) (string, error) {
+	if openRTBRequest.Site != nil {
+		if openRTBRequest.Site.ID != "" {
+			return fmt.Sprintf("%s?s=%s&prebid", v.endpoint, openRTBRequest.Site.ID), nil
+		}
+		return "", &errortypes.BadInput{
+			Message: "request.Site.ID is not provided",
+		}
+	} else if openRTBRequest.App != nil {
+		if openRTBRequest.App.ID != "" {
+			return fmt.Sprintf("%s?s=%s&prebid", v.endpoint, openRTBRequest.App.ID), nil
+		}
+		return "", &errortypes.BadInput{
+			Message: "request.App.ID is not provided",
+		}
+	}
+	return "", &errortypes.BadInput{
+		Message: "request.Site or request.App are not provided",
+	}
+}
+
+func makeRequestHeaders(openRTBRequest *openrtb2.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 openRTBRequest.Device != nil {
+		if len(openRTBRequest.Device.UA) > 0 {
+			headers.Add("User-Agent", openRTBRequest.Device.UA)
+		}
+
+		if len(openRTBRequest.Device.IPv6) > 0 {
+			headers.Add("X-Forwarded-For", openRTBRequest.Device.IPv6)
+		}
+
+		if len(openRTBRequest.Device.IP) > 0 {
+			headers.Add("X-Forwarded-For", openRTBRequest.Device.IP)
+		}
+	}
+	return headers
+}
+
+func (v *adapter) checkResponseStatusCodes(response *adapters.ResponseData) error {
+	if response.StatusCode == http.StatusBadRequest {
+		return &errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: [ %d ]", response.StatusCode),
+		}
+	}
+
+	if response.StatusCode == http.StatusServiceUnavailable {
+		return &errortypes.BadInput{
+			Message: fmt.Sprintf("Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", response.StatusCode),
+		}
+	}
+
+	if response.StatusCode != http.StatusOK {
+		return &errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: [ %d ]. Run with request.debug = 1 for more info", response.StatusCode),
+		}
+	}
+
+	return nil
+}
+
+func (v *adapter) MakeBids(
+	openRTBRequest *openrtb2.BidRequest,
+	requestToBidder *adapters.RequestData,
+	bidderRawResponse *adapters.ResponseData,
+) (
+	*adapters.BidderResponse,
+	[]error,
+) {
+	if bidderRawResponse.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	httpStatusError := v.checkResponseStatusCodes(bidderRawResponse)
+	if httpStatusError != nil {
+		return nil, []error{httpStatusError}
+	}
+
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(bidderRawResponse.Body, &response); err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Bad Server Response",
+		}}
+	}
+
+	if len(response.SeatBid) == 0 {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Empty SeatBid array",
+		}}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(openRTBRequest.Imp))
+	bidResponse.Currency = response.Cur
+	for _, seatBid := range response.SeatBid {
+		for i, bid := range seatBid.Bid {
+			bidType, err := getMediaTypeForImp(bid.ImpID, openRTBRequest.Imp)
+			if err != nil {
+				return nil, []error{err}
+			}
+
+			b := &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: bidType,
+			}
+			bidResponse.Bids = append(bidResponse.Bids, b)
+		}
+	}
+	return bidResponse, nil
+}
+
+func getMediaTypeForImp(impId string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
+	for _, imp := range imps {
+		if imp.ID == impId {
+			if imp.Banner != nil {
+				return openrtb_ext.BidTypeBanner, nil
+			} else {
+				return "", &errortypes.BadInput{
+					Message: fmt.Sprintf("Unsupported bidtype for bid: \"%s\"", impId),
+				}
+			}
+		}
+	}
+	return "", &errortypes.BadInput{
+		Message: fmt.Sprintf("Failed to find impression: \"%s\"", impId),
+	}
+}
diff --git a/adapters/ix/ix.go b/adapters/ix/ix.go
index fd89fc95..099f75e8 100644
--- a/adapters/ix/ix.go
+++ b/adapters/ix/ix.go
@@ -148,18 +148,18 @@ func (a *IxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalReque
 		}}
 	}
 
-	// Until the time we support multi-format ad units, we'll use a bid request impression media type
-	// as a bid response bid type. They are linked by the impression id.
-	impMediaType := map[string]openrtb_ext.BidType{}
+	// Store media type per impression in a map for later use to set in bid.ext.prebid.type
+	// Won't work for multiple bid case with a multi-format ad unit. We expect to get type from exchange on such case.
+	impMediaTypeReq := map[string]openrtb_ext.BidType{}
 	for _, imp := range internalRequest.Imp {
 		if imp.Banner != nil {
-			impMediaType[imp.ID] = openrtb_ext.BidTypeBanner
+			impMediaTypeReq[imp.ID] = openrtb_ext.BidTypeBanner
 		} else if imp.Video != nil {
-			impMediaType[imp.ID] = openrtb_ext.BidTypeVideo
+			impMediaTypeReq[imp.ID] = openrtb_ext.BidTypeVideo
 		} else if imp.Native != nil {
-			impMediaType[imp.ID] = openrtb_ext.BidTypeNative
+			impMediaTypeReq[imp.ID] = openrtb_ext.BidTypeNative
 		} else if imp.Audio != nil {
-			impMediaType[imp.ID] = openrtb_ext.BidTypeAudio
+			impMediaTypeReq[imp.ID] = openrtb_ext.BidTypeAudio
 		}
 	}
 
@@ -170,9 +170,11 @@ func (a *IxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalReque
 
 	for _, seatBid := range bidResponse.SeatBid {
 		for _, bid := range seatBid.Bid {
-			bidType, ok := impMediaType[bid.ImpID]
-			if !ok {
-				errs = append(errs, fmt.Errorf("unmatched impression id: %s", bid.ImpID))
+
+			bidType, err := getMediaTypeForBid(bid, impMediaTypeReq)
+			if err != nil {
+				errs = append(errs, err)
+				continue
 			}
 
 			var bidExtVideo *openrtb_ext.ExtBidPrebidVideo
@@ -222,6 +224,36 @@ func (a *IxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalReque
 	return bidderResponse, errs
 }
 
+func getMediaTypeForBid(bid openrtb2.Bid, impMediaTypeReq map[string]openrtb_ext.BidType) (openrtb_ext.BidType, error) {
+	switch bid.MType {
+	case openrtb2.MarkupBanner:
+		return openrtb_ext.BidTypeBanner, nil
+	case openrtb2.MarkupVideo:
+		return openrtb_ext.BidTypeVideo, nil
+	case openrtb2.MarkupAudio:
+		return openrtb_ext.BidTypeAudio, nil
+	case openrtb2.MarkupNative:
+		return openrtb_ext.BidTypeNative, nil
+	}
+
+	if bid.Ext != nil {
+		var bidExt openrtb_ext.ExtBid
+		err := json.Unmarshal(bid.Ext, &bidExt)
+		if err == nil && bidExt.Prebid != nil {
+			prebidType := string(bidExt.Prebid.Type)
+			if prebidType != "" {
+				return openrtb_ext.ParseBidType(prebidType)
+			}
+		}
+	}
+
+	if bidType, ok := impMediaTypeReq[bid.ImpID]; ok {
+		return bidType, nil
+	} else {
+		return "", fmt.Errorf("unmatched impression id: %s", bid.ImpID)
+	}
+}
+
 // Builder builds a new instance of the Ix adapter for the given bidder with the given config.
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
 	bidder := &IxAdapter{
diff --git a/config/config.go b/config/config.go
index d0c2523c..591fded9 100644
--- a/config/config.go
+++ b/config/config.go
@@ -935,7 +935,9 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.adot.endpoint", "https://dsp.adotmob.com/headerbidding{PUBLISHER_PATH}/bidrequest")
 	v.SetDefault("adapters.adpone.endpoint", "http://rtb.adpone.com/bid-request?src=prebid_server")
 	v.SetDefault("adapters.adprime.endpoint", "http://delta.adprime.com/pserver")
+	v.SetDefault("adapters.adrino.endpoint", "https://prd-prebid-bidder.adrino.io/openrtb/bid")
 	v.SetDefault("adapters.adtarget.endpoint", "http://ghb.console.adtarget.com.tr/pbs/ortb")
+	v.SetDefault("adapters.adtrgtme.endpoint", "https://z.cdn.adtarget.market/ssp")
 	v.SetDefault("adapters.adtelligent.endpoint", "http://ghb.adtelligent.com/pbs/ortb")
 	v.SetDefault("adapters.advangelists.endpoint", "http://nep.advangelists.com/xp/get?pubid={{.PublisherID}}")
 	v.SetDefault("adapters.adview.endpoint", "https://bid.adview.com/agent/thirdAdxService/{{.AccountID}}")
@@ -1022,6 +1024,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.nextmillennium.endpoint", "https://pbs.nextmillmedia.com/openrtb2/auction")
 	v.SetDefault("adapters.ninthdecimal.endpoint", "http://rtb.ninthdecimal.com/xp/get?pubid={{.PublisherID}}")
 	v.SetDefault("adapters.nobid.endpoint", "https://ads.servenobid.com/ortb_adreq?tek=pbs&ver=1")
+	v.SetDefault("adapters.oftmedia.endpoint", "http://ghb.ortb.152media.info/pbs/ortb")
 	v.SetDefault("adapters.onetag.endpoint", "https://prebid-server.onetag-sys.com/prebid-server/{{.PublisherID}}")
 	v.SetDefault("adapters.openweb.endpoint", "http://ghb.spotim.market/pbs/ortb")
 	v.SetDefault("adapters.openx.endpoint", "http://rtb.openx.net/prebid")
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index c8e80682..20361187 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -139,7 +139,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 	w.Header().Set("Access-Control-Expose-Headers", "AMP-Access-Control-Allow-Source-Origin")
 	w.Header().Set("X-Prebid", version.BuildXPrebidHeader(version.Ver))
 
-	req, storedAuctionResponses, storedBidResponses, errL := deps.parseAmpRequest(r)
+	req, storedAuctionResponses, storedBidResponses, bidderImpReplaceImp, errL := deps.parseAmpRequest(r)
 	ao.Errors = append(ao.Errors, errL...)
 
 	if errortypes.ContainsFatalError(errL) {
@@ -204,6 +204,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 		GlobalPrivacyControlHeader: secGPC,
 		StoredAuctionResponses:     storedAuctionResponses,
 		StoredBidResponses:         storedBidResponses,
+		BidderImpReplaceImpID:      bidderImpReplaceImp,
 		PubID:                      labels.PubID,
 	}
 
@@ -305,9 +306,9 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 // possible, it will return errors with messages that suggest improvements.
 //
 // If the errors list has at least one element, then no guarantees are made about the returned request.
-func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openrtb2.BidRequest, storedAuctionResponses stored_responses.ImpsWithBidResponses, storedBidResponses stored_responses.ImpBidderStoredResp, errs []error) {
+func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openrtb2.BidRequest, storedAuctionResponses stored_responses.ImpsWithBidResponses, storedBidResponses stored_responses.ImpBidderStoredResp, bidderImpReplaceImp stored_responses.BidderImpReplaceImpID, errs []error) {
 	// Load the stored request for the AMP ID.
-	req, storedAuctionResponses, storedBidResponses, e := deps.loadRequestJSONForAmp(httpRequest)
+	req, storedAuctionResponses, storedBidResponses, bidderImpReplaceImp, e := deps.loadRequestJSONForAmp(httpRequest)
 	if errs = append(errs, e...); errortypes.ContainsFatalError(errs) {
 		return
 	}
@@ -332,13 +333,13 @@ func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openr
 }
 
 // Load the stored OpenRTB request for an incoming AMP request, or return the errors found.
-func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req *openrtb2.BidRequest, storedAuctionResponses stored_responses.ImpsWithBidResponses, storedBidResponses stored_responses.ImpBidderStoredResp, errs []error) {
+func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req *openrtb2.BidRequest, storedAuctionResponses stored_responses.ImpsWithBidResponses, storedBidResponses stored_responses.ImpBidderStoredResp, bidderImpReplaceImp stored_responses.BidderImpReplaceImpID, errs []error) {
 	req = &openrtb2.BidRequest{}
 	errs = nil
 
 	ampParams, err := amp.ParseParams(httpRequest)
 	if err != nil {
-		return nil, nil, nil, []error{err}
+		return nil, nil, nil, nil, []error{err}
 	}
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(storedRequestTimeoutMillis)*time.Millisecond)
@@ -346,7 +347,7 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req
 
 	storedRequests, _, errs := deps.storedReqFetcher.FetchRequests(ctx, []string{ampParams.StoredRequestID}, nil)
 	if len(errs) > 0 {
-		return nil, nil, nil, errs
+		return nil, nil, nil, nil, errs
 	}
 	if len(storedRequests) == 0 {
 		errs = []error{fmt.Errorf("No AMP config found for tag_id '%s'", ampParams.StoredRequestID)}
@@ -360,7 +361,7 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req
 		return
 	}
 
-	storedAuctionResponses, storedBidResponses, errs = stored_responses.ProcessStoredResponses(ctx, requestJSON, deps.storedRespFetcher, deps.bidderMap)
+	storedAuctionResponses, storedBidResponses, bidderImpReplaceImp, errs = stored_responses.ProcessStoredResponses(ctx, requestJSON, deps.storedRespFetcher, deps.bidderMap)
 	if err != nil {
 		errs = []error{err}
 		return
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 5250ac0d..57cffb6f 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -151,7 +151,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 
 	w.Header().Set("X-Prebid", version.BuildXPrebidHeader(version.Ver))
 
-	req, impExtInfoMap, storedAuctionResponses, storedBidResponses, errL := deps.parseRequest(r)
+	req, impExtInfoMap, storedAuctionResponses, storedBidResponses, bidderImpReplaceImp, errL := deps.parseRequest(r)
 	if errortypes.ContainsFatalError(errL) && writeError(errL, w, &labels) {
 		return
 	}
@@ -211,6 +211,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 		ImpExtInfoMap:              impExtInfoMap,
 		StoredAuctionResponses:     storedAuctionResponses,
 		StoredBidResponses:         storedBidResponses,
+		BidderImpReplaceImpID:      bidderImpReplaceImp,
 		PubID:                      labels.PubID,
 	}
 	response, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
@@ -256,7 +257,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 // possible, it will return errors with messages that suggest improvements.
 //
 // If the errors list has at least one element, then no guarantees are made about the returned request.
-func (deps *endpointDeps) parseRequest(httpRequest *http.Request) (req *openrtb_ext.RequestWrapper, impExtInfoMap map[string]exchange.ImpExtInfo, storedAuctionResponses stored_responses.ImpsWithBidResponses, storedBidResponses stored_responses.ImpBidderStoredResp, errs []error) {
+func (deps *endpointDeps) parseRequest(httpRequest *http.Request) (req *openrtb_ext.RequestWrapper, impExtInfoMap map[string]exchange.ImpExtInfo, storedAuctionResponses stored_responses.ImpsWithBidResponses, storedBidResponses stored_responses.ImpBidderStoredResp, bidderImpReplaceImpId stored_responses.BidderImpReplaceImpID, errs []error) {
 	req = &openrtb_ext.RequestWrapper{}
 	req.BidRequest = &openrtb2.BidRequest{}
 	errs = nil
@@ -285,7 +286,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request) (req *openrtb_
 
 	impInfo, errs := parseImpInfo(requestJson)
 	if len(errs) > 0 {
-		return nil, nil, nil, nil, errs
+		return nil, nil, nil, nil, nil, errs
 	}
 
 	// Fetch the Stored Request data and merge it into the HTTP request.
@@ -294,9 +295,9 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request) (req *openrtb_
 	}
 
 	//Stored auction responses should be processed after stored requests due to possible impression modification
-	storedAuctionResponses, storedBidResponses, errs = stored_responses.ProcessStoredResponses(ctx, requestJson, deps.storedRespFetcher, deps.bidderMap)
+	storedAuctionResponses, storedBidResponses, bidderImpReplaceImpId, errs = stored_responses.ProcessStoredResponses(ctx, requestJson, deps.storedRespFetcher, deps.bidderMap)
 	if len(errs) > 0 {
-		return nil, nil, nil, nil, errs
+		return nil, nil, nil, nil, nil, errs
 	}
 
 	if err := json.Unmarshal(requestJson, req.BidRequest); err != nil {
@@ -1317,7 +1318,7 @@ func (deps *endpointDeps) validateSite(req *openrtb_ext.RequestWrapper) error {
 		return err
 	}
 	siteAmp := siteExt.GetAmp()
-	if siteAmp < 0 || siteAmp > 1 {
+	if siteAmp != nil && (*siteAmp < 0 || *siteAmp > 1) {
 		return errors.New(`request.site.ext.amp must be either 1, 0, or undefined`)
 	}
 
@@ -1680,7 +1681,6 @@ func (deps *endpointDeps) processStoredRequests(ctx context.Context, requestJson
 				return nil, nil, []error{err}
 			}
 			resolvedImps = append(resolvedImps, resolvedImp)
-
 			impId, err := jsonparser.GetString(resolvedImp, "id")
 			if err != nil {
 				return nil, nil, []error{err}
@@ -1690,12 +1690,25 @@ func (deps *endpointDeps) processStoredRequests(ctx context.Context, requestJson
 			if impData.ImpExtPrebid.Options != nil {
 				echoVideoAttributes = impData.ImpExtPrebid.Options.EchoVideoAttrs
 			}
-			impExtInfoMap[impId] = exchange.ImpExtInfo{EchoVideoAttrs: echoVideoAttributes, StoredImp: storedImps[impData.ImpExtPrebid.StoredRequest.ID]}
+
+			// Extract Passthrough from Merged Imp
+			passthrough, _, _, err := jsonparser.Get(resolvedImp, "ext", "prebid", "passthrough")
+			if err != nil && err != jsonparser.KeyPathNotFoundError {
+				return nil, nil, []error{err}
+			}
+			impExtInfoMap[impId] = exchange.ImpExtInfo{EchoVideoAttrs: echoVideoAttributes, StoredImp: storedImps[impData.ImpExtPrebid.StoredRequest.ID], Passthrough: passthrough}
 
 		} else {
 			resolvedImps = append(resolvedImps, impData.Imp)
+			impId, err := jsonparser.GetString(impData.Imp, "id")
+			if err != nil {
+				if err == jsonparser.KeyPathNotFoundError {
+					err = fmt.Errorf("request.imp[%d] missing required field: \"id\"\n", i)
+				}
+				return nil, nil, []error{err}
+			}
+			impExtInfoMap[impId] = exchange.ImpExtInfo{Passthrough: impData.ImpExtPrebid.Passthrough}
 		}
-
 	}
 	if len(resolvedImps) > 0 {
 		newImpJson, err := json.Marshal(resolvedImps)
@@ -1713,7 +1726,6 @@ func (deps *endpointDeps) processStoredRequests(ctx context.Context, requestJson
 
 // parseImpInfo parses the request JSON and returns impression and unmarshalled imp.ext.prebid
 func parseImpInfo(requestJson []byte) (impData []ImpExtPrebidData, errs []error) {
-
 	if impArray, dataType, _, err := jsonparser.Get(requestJson, "imp"); err == nil && dataType == jsonparser.Array {
 		_, err = jsonparser.ArrayEach(impArray, func(imp []byte, _ jsonparser.ValueType, _ int, err error) {
 			impExtData, _, _, err := jsonparser.Get(imp, "ext", "prebid")
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index ffecf1a8..765e1715 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -19,8 +19,10 @@ import (
 	"github.com/prebid/prebid-server/adapters/adot"
 	"github.com/prebid/prebid-server/adapters/adpone"
 	"github.com/prebid/prebid-server/adapters/adprime"
+	"github.com/prebid/prebid-server/adapters/adrino"
 	"github.com/prebid/prebid-server/adapters/adtarget"
 	"github.com/prebid/prebid-server/adapters/adtelligent"
+	"github.com/prebid/prebid-server/adapters/adtrgtme"
 	"github.com/prebid/prebid-server/adapters/advangelists"
 	"github.com/prebid/prebid-server/adapters/adview"
 	"github.com/prebid/prebid-server/adapters/adxcg"
@@ -169,7 +171,9 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderAdot:              adot.Builder,
 		openrtb_ext.BidderAdpone:            adpone.Builder,
 		openrtb_ext.BidderAdprime:           adprime.Builder,
+		openrtb_ext.BidderAdrino:            adrino.Builder,
 		openrtb_ext.BidderAdtarget:          adtarget.Builder,
+		openrtb_ext.BidderAdtrgtme:          adtrgtme.Builder,
 		openrtb_ext.BidderAdtelligent:       adtelligent.Builder,
 		openrtb_ext.BidderAdvangelists:      advangelists.Builder,
 		openrtb_ext.BidderAdView:            adview.Builder,
@@ -249,6 +253,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderNextMillennium:    nextmillennium.Builder,
 		openrtb_ext.BidderNinthDecimal:      ninthdecimal.Builder,
 		openrtb_ext.BidderNoBid:             nobid.Builder,
+		openrtb_ext.BidderOFTMedia:          adtelligent.Builder,
 		openrtb_ext.BidderOneTag:            onetag.Builder,
 		openrtb_ext.BidderOpenWeb:           openweb.Builder,
 		openrtb_ext.BidderOpenx:             openx.Builder,
diff --git a/exchange/bidder.go b/exchange/bidder.go
index eb53a14b..99fb8949 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -300,7 +300,11 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde
 							reqBody := string(httpInfo.request.Body)
 							re := regexp.MustCompile(ImpIdReqBody)
 							reqBodySplit := re.Split(reqBody, -1)
-							bidResponse.Bids[i].Bid.ImpID = reqBodySplit[1]
+							reqImpId := reqBodySplit[1]
+							// replace impId if "replaceimpid" is true or not specified
+							if bidderRequest.ImpReplaceImpId[reqImpId] {
+								bidResponse.Bids[i].Bid.ImpID = reqImpId
+							}
 						}
 					}
 				}
diff --git a/exchange/exchange.go b/exchange/exchange.go
index b0851af2..f1a9a7b8 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -153,6 +153,7 @@ func NewExchange(adapters map[openrtb_ext.BidderName]AdaptedBidder, cache prebid
 type ImpExtInfo struct {
 	EchoVideoAttrs bool
 	StoredImp      []byte
+	Passthrough    json.RawMessage
 }
 
 // AuctionRequest holds the bid request for the auction
@@ -175,8 +176,9 @@ type AuctionRequest struct {
 	// map of imp id to stored response
 	StoredAuctionResponses stored_responses.ImpsWithBidResponses
 	// map of imp id to bidder to stored response
-	StoredBidResponses stored_responses.ImpBidderStoredResp
-	PubID              string
+	StoredBidResponses    stored_responses.ImpBidderStoredResp
+	BidderImpReplaceImpID stored_responses.BidderImpReplaceImpID
+	PubID                 string
 }
 
 // BidderRequest holds the bidder specific request and all other
@@ -187,6 +189,7 @@ type BidderRequest struct {
 	BidderCoreName        openrtb_ext.BidderName
 	BidderLabels          metrics.AdapterLabels
 	BidderStoredResponses map[string]json.RawMessage
+	ImpReplaceImpId       map[string]bool
 }
 
 func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *DebugLog) (*openrtb2.BidResponse, error) {
@@ -328,7 +331,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 				errs = append(errs, dealErrs...)
 			}
 
-			bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, errs)
+			bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, requestExt.Prebid.Passthrough, errs)
 			if debugLog.DebugEnabledOrOverridden {
 				if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil {
 					debugLog.Data.Response = string(bidRespExtBytes)
@@ -346,9 +349,9 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 			targData.setTargeting(auc, r.BidRequestWrapper.BidRequest.App != nil, bidCategory, r.Account.TruncateTargetAttribute)
 
 		}
-		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, errs)
+		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, requestExt.Prebid.Passthrough, errs)
 	} else {
-		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, errs)
+		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, requestExt.Prebid.Passthrough, errs)
 
 		if debugLog.DebugEnabledOrOverridden {
 
@@ -941,8 +944,7 @@ func getPrimaryAdServer(adServerId int) (string, error) {
 }
 
 // Extract all the data from the SeatBids and build the ExtBidResponse
-func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, r AuctionRequest, debugInfo bool, errList []error) *openrtb_ext.ExtBidResponse {
-
+func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, r AuctionRequest, debugInfo bool, passthrough json.RawMessage, errList []error) *openrtb_ext.ExtBidResponse {
 	bidResponseExt := &openrtb_ext.ExtBidResponse{
 		Errors:               make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage, len(adapterBids)),
 		Warnings:             make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage, len(adapterBids)),
@@ -962,6 +964,13 @@ func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pb
 		}
 	}
 
+	if passthrough != nil {
+		if bidResponseExt.Prebid == nil {
+			bidResponseExt.Prebid = &openrtb_ext.ExtResponsePrebid{}
+		}
+		bidResponseExt.Prebid.Passthrough = passthrough
+	}
+
 	for bidderName, responseExtra := range adapterExtra {
 
 		if debugInfo && len(responseExtra.HttpCalls) > 0 {
@@ -1070,20 +1079,22 @@ func makeBidExtJSON(ext json.RawMessage, prebid *openrtb_ext.ExtBidPrebid, impEx
 		}
 		prebid.Meta = &metaContainer.Prebid.Meta
 	}
-	extMap[openrtb_ext.PrebidExtKey] = prebid
 
-	// ext.storedrequestattributes
-	if impExtInfo, ok := impExtInfoMap[impId]; ok && impExtInfo.EchoVideoAttrs {
-		videoData, _, _, err := jsonparser.Get(impExtInfo.StoredImp, "video")
-		if err != nil && err != jsonparser.KeyPathNotFoundError {
-			return nil, err
-		}
-		//handler for case where EchoVideoAttrs is true, but video data is not found
-		if len(videoData) > 0 {
-			extMap[openrtb_ext.StoredRequestAttributes] = json.RawMessage(videoData)
+	// ext.prebid.storedrequestattributes and ext.prebid.passthrough
+	if impExtInfo, ok := impExtInfoMap[impId]; ok {
+		prebid.Passthrough = impExtInfoMap[impId].Passthrough
+		if impExtInfo.EchoVideoAttrs {
+			videoData, _, _, err := jsonparser.Get(impExtInfo.StoredImp, "video")
+			if err != nil && err != jsonparser.KeyPathNotFoundError {
+				return nil, err
+			}
+			//handler for case where EchoVideoAttrs is true, but video data is not found
+			if len(videoData) > 0 {
+				extMap[openrtb_ext.StoredRequestAttributes] = json.RawMessage(videoData)
+			}
 		}
 	}
-
+	extMap[openrtb_ext.PrebidExtKey] = prebid
 	return json.Marshal(extMap)
 }
 
diff --git a/exchange/utils.go b/exchange/utils.go
index 62fa210e..8a4e0b68 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -73,7 +73,7 @@ func cleanOpenRTBRequests(ctx context.Context,
 	var allBidderRequests []BidderRequest
 	allBidderRequests, errs = getAuctionBidderRequests(auctionReq, requestExt, bidderToSyncerKey, impsByBidder, aliases, hostSChainNode)
 
-	bidderNameToBidderReq := buildBidResponseRequest(req.BidRequest, bidderImpWithBidResp, aliases)
+	bidderNameToBidderReq := buildBidResponseRequest(req.BidRequest, bidderImpWithBidResp, aliases, auctionReq.BidderImpReplaceImpID)
 	//this function should be executed after getAuctionBidderRequests
 	allBidderRequests = mergeBidderRequests(allBidderRequests, bidderNameToBidderReq)
 
@@ -760,8 +760,11 @@ func applyFPD(fpd *firstpartydata.ResolvedFirstPartyData, bidReq *openrtb2.BidRe
 
 func buildBidResponseRequest(req *openrtb2.BidRequest,
 	bidderImpResponses stored_responses.BidderImpsWithBidResponses,
-	aliases map[string]string) map[openrtb_ext.BidderName]BidderRequest {
+	aliases map[string]string,
+	bidderImpReplaceImpID stored_responses.BidderImpReplaceImpID) map[openrtb_ext.BidderName]BidderRequest {
+
 	bidderToBidderResponse := make(map[openrtb_ext.BidderName]BidderRequest)
+
 	for bidderName, impResps := range bidderImpResponses {
 		resolvedBidder := resolveBidder(string(bidderName), aliases)
 		bidderToBidderResponse[bidderName] = BidderRequest{
@@ -769,6 +772,7 @@ func buildBidResponseRequest(req *openrtb2.BidRequest,
 			BidderCoreName:        resolvedBidder,
 			BidderName:            bidderName,
 			BidderStoredResponses: impResps,
+			ImpReplaceImpId:       bidderImpReplaceImpID[string(resolvedBidder)],
 			BidderLabels:          metrics.AdapterLabels{Adapter: resolvedBidder},
 		}
 	}
diff --git a/openrtb_ext/app.go b/openrtb_ext/app.go
index 65826253..3e55a980 100644
--- a/openrtb_ext/app.go
+++ b/openrtb_ext/app.go
@@ -6,10 +6,7 @@ type ExtApp struct {
 }
 
 // ExtAppPrebid further defines the contract for bidrequest.app.ext.prebid.
-// We are only enforcing that these two properties be strings if they are provided.
-// They are optional with no current constraints on value, so we don't need a custom
-// UnmarshalJSON() method at this time.
 type ExtAppPrebid struct {
-	Source  string `json:"source"`
-	Version string `json:"version"`
+	Source  string `json:"source,omitempty"`
+	Version string `json:"version,omitempty"`
 }
diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go
index b0cacf85..61c720fe 100644
--- a/openrtb_ext/bid.go
+++ b/openrtb_ext/bid.go
@@ -23,6 +23,7 @@ type ExtBidPrebid struct {
 	Video             *ExtBidPrebidVideo  `json:"video,omitempty"`
 	Events            *ExtBidPrebidEvents `json:"events,omitempty"`
 	BidId             string              `json:"bidid,omitempty"`
+	Passthrough       json.RawMessage     `json:"passthrough,omitempty"`
 }
 
 // ExtBidPrebidCache defines the contract for  bidresponse.seatbid.bid[i].ext.prebid.cache
@@ -173,4 +174,5 @@ const (
 	StoredRequestAttributes = "storedrequestattributes"
 	OriginalBidCpmKey       = "origbidcpm"
 	OriginalBidCurKey       = "origbidcur"
+	Passthrough             = "passthrough"
 )
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index b9bbb615..f9c93bf6 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -99,7 +99,9 @@ const (
 	BidderAdot              BidderName = "adot"
 	BidderAdpone            BidderName = "adpone"
 	BidderAdprime           BidderName = "adprime"
+	BidderAdrino            BidderName = "adrino"
 	BidderAdtarget          BidderName = "adtarget"
+	BidderAdtrgtme          BidderName = "adtrgtme"
 	BidderAdtelligent       BidderName = "adtelligent"
 	BidderAdvangelists      BidderName = "advangelists"
 	BidderAdView            BidderName = "adview"
@@ -179,6 +181,7 @@ const (
 	BidderNextMillennium    BidderName = "nextmillennium"
 	BidderNinthDecimal      BidderName = "ninthdecimal"
 	BidderNoBid             BidderName = "nobid"
+	BidderOFTMedia          BidderName = "oftmedia"
 	BidderOneTag            BidderName = "onetag"
 	BidderOpenWeb           BidderName = "openweb"
 	BidderOpenx             BidderName = "openx"
@@ -257,7 +260,9 @@ func CoreBidderNames() []BidderName {
 		BidderAdot,
 		BidderAdpone,
 		BidderAdprime,
+		BidderAdrino,
 		BidderAdtarget,
+		BidderAdtrgtme,
 		BidderAdtelligent,
 		BidderAdvangelists,
 		BidderAdView,
@@ -337,6 +342,7 @@ func CoreBidderNames() []BidderName {
 		BidderNextMillennium,
 		BidderNinthDecimal,
 		BidderNoBid,
+		BidderOFTMedia,
 		BidderOneTag,
 		BidderOpenWeb,
 		BidderOpenx,
diff --git a/openrtb_ext/imp.go b/openrtb_ext/imp.go
index 0afc3225..ae151435 100644
--- a/openrtb_ext/imp.go
+++ b/openrtb_ext/imp.go
@@ -22,6 +22,8 @@ type ExtImpPrebid struct {
 	Bidder map[string]json.RawMessage `json:"bidder"`
 
 	Options *Options `json:"options,omitempty"`
+
+	Passthrough json.RawMessage `json:"passthrough,omitempty"`
 }
 
 // ExtStoredRequest defines the contract for bidrequest.imp[i].ext.prebid.storedrequest
@@ -36,8 +38,9 @@ type ExtStoredAuctionResponse struct {
 
 // ExtStoredBidResponse defines the contract for bidrequest.imp[i].ext.prebid.storedbidresponse
 type ExtStoredBidResponse struct {
-	ID     string `json:"id"`
-	Bidder string `json:"bidder"`
+	ID           string `json:"id"`
+	Bidder       string `json:"bidder"`
+	ReplaceImpId *bool  `json:"replaceimpid"`
 }
 
 type Options struct {
diff --git a/openrtb_ext/imp_adrino.go b/openrtb_ext/imp_adrino.go
new file mode 100644
index 00000000..7c5c53b2
--- /dev/null
+++ b/openrtb_ext/imp_adrino.go
@@ -0,0 +1,5 @@
+package openrtb_ext
+
+type ExtImpAdrino struct {
+	Hash string `json:"hash"`
+}
diff --git a/openrtb_ext/imp_adtrgtme.go b/openrtb_ext/imp_adtrgtme.go
new file mode 100644
index 00000000..f44daae1
--- /dev/null
+++ b/openrtb_ext/imp_adtrgtme.go
@@ -0,0 +1,4 @@
+package openrtb_ext
+
+// ExtImpAdtrgtme defines the contract for bidrequest.imp[i].ext.adtrgtme
+type ExtImpAdtrgtme struct{}
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index 67cad55b..0fcbf4a7 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -57,6 +57,7 @@ type ExtRequestPrebid struct {
 
 	CurrencyConversions *ExtRequestCurrency `json:"currency,omitempty"`
 	BidderConfigs       []BidderConfig      `json:"bidderconfig,omitempty"`
+	Passthrough         json.RawMessage     `json:"passthrough,omitempty"`
 	Experiment          *Experiment         `json:"experiment,omitempty"`
 }
 
diff --git a/openrtb_ext/request_wrapper.go b/openrtb_ext/request_wrapper.go
index 8d2d2c72..51ce753d 100644
--- a/openrtb_ext/request_wrapper.go
+++ b/openrtb_ext/request_wrapper.go
@@ -41,6 +41,8 @@ type RequestWrapper struct {
 	sourceExt  *SourceExt
 }
 
+const jsonEmptyObjectLength = 2
+
 func (rw *RequestWrapper) GetUserExt() (*UserExt, error) {
 	if rw.userExt != nil {
 		return rw.userExt, nil
@@ -150,97 +152,131 @@ func (rw *RequestWrapper) RebuildRequest() error {
 }
 
 func (rw *RequestWrapper) rebuildUserExt() error {
-	if rw.BidRequest.User == nil && rw.userExt != nil && rw.userExt.Dirty() {
-		rw.User = &openrtb2.User{}
+	if rw.userExt == nil || !rw.userExt.Dirty() {
+		return nil
 	}
-	if rw.userExt != nil && rw.userExt.Dirty() {
-		userJson, err := rw.userExt.marshal()
-		if err != nil {
-			return err
-		}
+
+	userJson, err := rw.userExt.marshal()
+	if err != nil {
+		return err
+	}
+
+	if userJson != nil && rw.User == nil {
+		rw.User = &openrtb2.User{Ext: userJson}
+	} else if rw.User != nil {
 		rw.User.Ext = userJson
 	}
+
 	return nil
 }
 
 func (rw *RequestWrapper) rebuildDeviceExt() error {
-	if rw.Device == nil && rw.deviceExt != nil && rw.deviceExt.Dirty() {
-		rw.Device = &openrtb2.Device{}
+	if rw.deviceExt == nil || !rw.deviceExt.Dirty() {
+		return nil
 	}
-	if rw.deviceExt != nil && rw.deviceExt.Dirty() {
-		deviceJson, err := rw.deviceExt.marshal()
-		if err != nil {
-			return err
-		}
+
+	deviceJson, err := rw.deviceExt.marshal()
+	if err != nil {
+		return err
+	}
+
+	if deviceJson != nil && rw.Device == nil {
+		rw.Device = &openrtb2.Device{Ext: deviceJson}
+	} else if rw.Device != nil {
 		rw.Device.Ext = deviceJson
 	}
+
 	return nil
 }
 
 func (rw *RequestWrapper) rebuildRequestExt() error {
-	if rw.requestExt != nil && rw.requestExt.Dirty() {
-		requestJson, err := rw.requestExt.marshal()
-		if err != nil {
-			return err
-		}
-		rw.Ext = requestJson
+	if rw.requestExt == nil || !rw.requestExt.Dirty() {
+		return nil
 	}
+
+	requestJson, err := rw.requestExt.marshal()
+	if err != nil {
+		return err
+	}
+
+	rw.Ext = requestJson
+
 	return nil
 }
 
 func (rw *RequestWrapper) rebuildAppExt() error {
-	if rw.App == nil && rw.appExt != nil && rw.appExt.Dirty() {
-		rw.App = &openrtb2.App{}
+	if rw.appExt == nil || !rw.appExt.Dirty() {
+		return nil
 	}
-	if rw.appExt != nil && rw.appExt.Dirty() {
-		appJson, err := rw.appExt.marshal()
-		if err != nil {
-			return err
-		}
+
+	appJson, err := rw.appExt.marshal()
+	if err != nil {
+		return err
+	}
+
+	if appJson != nil && rw.App == nil {
+		rw.App = &openrtb2.App{Ext: appJson}
+	} else if rw.App != nil {
 		rw.App.Ext = appJson
 	}
+
 	return nil
 }
 
 func (rw *RequestWrapper) rebuildRegExt() error {
-	if rw.Regs == nil && rw.regExt != nil && rw.regExt.Dirty() {
-		rw.Regs = &openrtb2.Regs{}
+	if rw.regExt == nil || !rw.regExt.Dirty() {
+		return nil
 	}
-	if rw.regExt != nil && rw.regExt.Dirty() {
-		regsJson, err := rw.regExt.marshal()
-		if err != nil {
-			return err
-		}
+
+	regsJson, err := rw.regExt.marshal()
+	if err != nil {
+		return err
+	}
+
+	if regsJson != nil && rw.Regs == nil {
+		rw.Regs = &openrtb2.Regs{Ext: regsJson}
+	} else if rw.Regs != nil {
 		rw.Regs.Ext = regsJson
 	}
+
 	return nil
 }
 
 func (rw *RequestWrapper) rebuildSiteExt() error {
-	if rw.Site == nil && rw.siteExt != nil && rw.siteExt.Dirty() {
-		rw.Site = &openrtb2.Site{}
+	if rw.siteExt == nil || !rw.siteExt.Dirty() {
+		return nil
 	}
-	if rw.siteExt != nil && rw.siteExt.Dirty() {
-		siteJson, err := rw.siteExt.marshal()
-		if err != nil {
-			return err
-		}
+
+	siteJson, err := rw.siteExt.marshal()
+	if err != nil {
+		return err
+	}
+
+	if siteJson != nil && rw.Site == nil {
+		rw.Site = &openrtb2.Site{Ext: siteJson}
+	} else if rw.Site != nil {
 		rw.Site.Ext = siteJson
 	}
+
 	return nil
 }
 
 func (rw *RequestWrapper) rebuildSourceExt() error {
-	if rw.Source == nil && rw.sourceExt != nil && rw.sourceExt.Dirty() {
-		rw.Source = &openrtb2.Source{}
+	if rw.sourceExt == nil || !rw.sourceExt.Dirty() {
+		return nil
 	}
-	if rw.sourceExt != nil && rw.sourceExt.Dirty() {
-		sourceJson, err := rw.sourceExt.marshal()
-		if err != nil {
-			return err
-		}
+
+	sourceJson, err := rw.sourceExt.marshal()
+	if err != nil {
+		return err
+	}
+
+	if sourceJson != nil && rw.Source == nil {
+		rw.Source = &openrtb2.Source{Ext: sourceJson}
+	} else if rw.Source != nil {
 		rw.Source.Ext = sourceJson
 	}
+
 	return nil
 }
 
@@ -300,11 +336,11 @@ func (ue *UserExt) unmarshal(extJson json.RawMessage) error {
 
 func (ue *UserExt) marshal() (json.RawMessage, error) {
 	if ue.consentDirty {
-		consentJson, err := json.Marshal(ue.consent)
-		if err != nil {
-			return nil, err
-		}
-		if len(consentJson) > 2 {
+		if ue.consent != nil && len(*ue.consent) > 0 {
+			consentJson, err := json.Marshal(ue.consent)
+			if err != nil {
+				return nil, err
+			}
 			ue.ext["consent"] = json.RawMessage(consentJson)
 		} else {
 			delete(ue.ext, "consent")
@@ -313,12 +349,16 @@ func (ue *UserExt) marshal() (json.RawMessage, error) {
 	}
 
 	if ue.prebidDirty {
-		prebidJson, err := json.Marshal(ue.prebid)
-		if err != nil {
-			return nil, err
-		}
-		if len(prebidJson) > 2 {
-			ue.ext["prebid"] = json.RawMessage(prebidJson)
+		if ue.prebid != nil {
+			prebidJson, err := json.Marshal(ue.prebid)
+			if err != nil {
+				return nil, err
+			}
+			if len(prebidJson) > jsonEmptyObjectLength {
+				ue.ext["prebid"] = json.RawMessage(prebidJson)
+			} else {
+				delete(ue.ext, "prebid")
+			}
 		} else {
 			delete(ue.ext, "prebid")
 		}
@@ -326,7 +366,7 @@ func (ue *UserExt) marshal() (json.RawMessage, error) {
 	}
 
 	if ue.eidsDirty {
-		if len(*ue.eids) > 0 {
+		if ue.eids != nil && len(*ue.eids) > 0 {
 			eidsJson, err := json.Marshal(ue.eids)
 			if err != nil {
 				return nil, err
@@ -343,7 +383,6 @@ func (ue *UserExt) marshal() (json.RawMessage, error) {
 		return nil, nil
 	}
 	return json.Marshal(ue.ext)
-
 }
 
 func (ue *UserExt) Dirty() bool {
@@ -443,12 +482,16 @@ func (re *RequestExt) unmarshal(extJson json.RawMessage) error {
 
 func (re *RequestExt) marshal() (json.RawMessage, error) {
 	if re.prebidDirty {
-		prebidJson, err := json.Marshal(re.prebid)
-		if err != nil {
-			return nil, err
-		}
-		if len(prebidJson) > 2 {
-			re.ext["prebid"] = json.RawMessage(prebidJson)
+		if re.prebid != nil {
+			prebidJson, err := json.Marshal(re.prebid)
+			if err != nil {
+				return nil, err
+			}
+			if len(prebidJson) > jsonEmptyObjectLength {
+				re.ext["prebid"] = json.RawMessage(prebidJson)
+			} else {
+				delete(re.ext, "prebid")
+			}
 		} else {
 			delete(re.ext, "prebid")
 		}
@@ -456,15 +499,16 @@ func (re *RequestExt) marshal() (json.RawMessage, error) {
 	}
 
 	if re.schainDirty {
-		if re.schain == nil {
-		}
-
-		schainJson, err := json.Marshal(re.schain)
-		if err != nil {
-			return nil, err
-		}
-		if len(schainJson) > 2 && re.schain != nil {
-			re.ext["schain"] = json.RawMessage(schainJson)
+		if re.schain != nil {
+			schainJson, err := json.Marshal(re.schain)
+			if err != nil {
+				return nil, err
+			}
+			if len(schainJson) > jsonEmptyObjectLength {
+				re.ext["schain"] = json.RawMessage(schainJson)
+			} else {
+				delete(re.ext, "schain")
+			}
 		} else {
 			delete(re.ext, "schain")
 		}
@@ -562,12 +606,16 @@ func (de *DeviceExt) unmarshal(extJson json.RawMessage) error {
 
 func (de *DeviceExt) marshal() (json.RawMessage, error) {
 	if de.prebidDirty {
-		prebidJson, err := json.Marshal(de.prebid)
-		if err != nil {
-			return nil, err
-		}
-		if len(prebidJson) > 2 {
-			de.ext["prebid"] = json.RawMessage(prebidJson)
+		if de.prebid != nil {
+			prebidJson, err := json.Marshal(de.prebid)
+			if err != nil {
+				return nil, err
+			}
+			if len(prebidJson) > jsonEmptyObjectLength {
+				de.ext["prebid"] = json.RawMessage(prebidJson)
+			} else {
+				delete(de.ext, "prebid")
+			}
 		} else {
 			delete(de.ext, "prebid")
 		}
@@ -645,12 +693,16 @@ func (ae *AppExt) unmarshal(extJson json.RawMessage) error {
 
 func (ae *AppExt) marshal() (json.RawMessage, error) {
 	if ae.prebidDirty {
-		prebidJson, err := json.Marshal(ae.prebid)
-		if err != nil {
-			return nil, err
-		}
-		if len(prebidJson) > 2 {
-			ae.ext["prebid"] = json.RawMessage(prebidJson)
+		if ae.prebid != nil {
+			prebidJson, err := json.Marshal(ae.prebid)
+			if err != nil {
+				return nil, err
+			}
+			if len(prebidJson) > jsonEmptyObjectLength {
+				ae.ext["prebid"] = json.RawMessage(prebidJson)
+			} else {
+				delete(ae.ext, "prebid")
+			}
 		} else {
 			delete(ae.ext, "prebid")
 		}
@@ -780,7 +832,7 @@ func (re *RegExt) SetUSPrivacy(uSPrivacy string) {
 type SiteExt struct {
 	ext      map[string]json.RawMessage
 	extDirty bool
-	amp      int8
+	amp      *int8
 	ampDirty bool
 }
 
@@ -809,11 +861,11 @@ func (se *SiteExt) unmarshal(extJson json.RawMessage) error {
 
 func (se *SiteExt) marshal() (json.RawMessage, error) {
 	if se.ampDirty {
-		ampJson, err := json.Marshal(se.amp)
-		if err != nil {
-			return nil, err
-		}
-		if len(ampJson) > 2 {
+		if se.amp != nil {
+			ampJson, err := json.Marshal(se.amp)
+			if err != nil {
+				return nil, err
+			}
 			se.ext["amp"] = json.RawMessage(ampJson)
 		} else {
 			delete(se.ext, "amp")
@@ -845,11 +897,11 @@ func (se *SiteExt) SetExt(ext map[string]json.RawMessage) {
 	se.extDirty = true
 }
 
-func (se *SiteExt) GetAmp() int8 {
+func (se *SiteExt) GetAmp() *int8 {
 	return se.amp
 }
 
-func (se *SiteExt) SetUSPrivacy(amp int8) {
+func (se *SiteExt) SetAmp(amp *int8) {
 	se.amp = amp
 	se.ampDirty = true
 }
@@ -887,12 +939,16 @@ func (se *SourceExt) unmarshal(extJson json.RawMessage) error {
 
 func (se *SourceExt) marshal() (json.RawMessage, error) {
 	if se.schainDirty {
-		schainJson, err := json.Marshal(se.schain)
-		if err != nil {
-			return nil, err
-		}
-		if len(schainJson) > 2 {
-			se.ext["schain"] = json.RawMessage(schainJson)
+		if se.schain != nil {
+			schainJson, err := json.Marshal(se.schain)
+			if err != nil {
+				return nil, err
+			}
+			if len(schainJson) > jsonEmptyObjectLength {
+				se.ext["schain"] = json.RawMessage(schainJson)
+			} else {
+				delete(se.ext, "schain")
+			}
 		} else {
 			delete(se.ext, "schain")
 		}
diff --git a/openrtb_ext/response.go b/openrtb_ext/response.go
index 044bc0bd..b20e741c 100644
--- a/openrtb_ext/response.go
+++ b/openrtb_ext/response.go
@@ -39,7 +39,8 @@ type ExtResponseSyncData struct {
 
 // ExtResponsePrebid defines the contract for bidresponse.ext.prebid
 type ExtResponsePrebid struct {
-	AuctionTimestamp int64 `json:"auctiontimestamp,omitempty"`
+	AuctionTimestamp int64           `json:"auctiontimestamp,omitempty"`
+	Passthrough      json.RawMessage `json:"passthrough,omitempty"`
 }
 
 // ExtUserSync defines the contract for bidresponse.ext.usersync.{bidder}.syncs[i]
diff --git a/static/bidder-info/adrino.yaml b/static/bidder-info/adrino.yaml
new file mode 100644
index 00000000..24bdfb37
--- /dev/null
+++ b/static/bidder-info/adrino.yaml
@@ -0,0 +1,7 @@
+maintainer:
+  email: dev@adrino.pl
+gvlVendorID: 1072
+capabilities:
+  site:
+    mediaTypes:
+      - native
diff --git a/static/bidder-info/adtrgtme.yaml b/static/bidder-info/adtrgtme.yaml
new file mode 100644
index 00000000..69deda5b
--- /dev/null
+++ b/static/bidder-info/adtrgtme.yaml
@@ -0,0 +1,9 @@
+maintainer:
+  email: "info@adtarget.me"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+  site:
+    mediaTypes:
+      - banner
\ No newline at end of file
diff --git a/static/bidder-info/oftmedia.yaml b/static/bidder-info/oftmedia.yaml
new file mode 100644
index 00000000..8ec6862f
--- /dev/null
+++ b/static/bidder-info/oftmedia.yaml
@@ -0,0 +1,17 @@
+maintainer:
+  email: "admin@152media.com"
+gvlVendorID: 1111
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+  site:
+    mediaTypes:
+      - banner
+      - video
+userSync:
+  # oftmedia supports user syncing, but requires configuration by the host. contact this
+  # bidder directly at the email address in this file to ask about enabling user sync.
+  supports:
+    - iframe
diff --git a/static/bidder-params/adrino.json b/static/bidder-params/adrino.json
new file mode 100644
index 00000000..56f7404c
--- /dev/null
+++ b/static/bidder-params/adrino.json
@@ -0,0 +1,16 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "Adrino Adapter Params",
+    "description": "A schema which validates params accepted by the Adrino adapter",
+    "type": "object",
+    "properties": {
+        "hash": {
+            "type": "string",
+            "minLength": 1,
+            "description": "The placement id provided by Adrino."
+        }
+    },
+    "required": [
+        "hash"
+    ]
+}
diff --git a/static/bidder-params/adtrgtme.json b/static/bidder-params/adtrgtme.json
new file mode 100644
index 00000000..e0da860c
--- /dev/null
+++ b/static/bidder-params/adtrgtme.json
@@ -0,0 +1,8 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Adtrgtme adapter params",
+  "description": "A schema which validates params accepted by the Adtrgtme adapter",
+  "type": "object",
+
+  "properties": {}
+}
\ No newline at end of file
diff --git a/static/bidder-params/oftmedia.json b/static/bidder-params/oftmedia.json
new file mode 100644
index 00000000..dba2aa7f
--- /dev/null
+++ b/static/bidder-params/oftmedia.json
@@ -0,0 +1,26 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "OFTMedia Adapter Params",
+  "description": "A schema which validates params accepted by the OFTMedia adapter",
+
+  "type": "object",
+  "properties": {
+    "placementId": {
+      "type": "integer",
+      "description": "An ID which identifies this placement of the impression"
+    },
+    "siteId": {
+      "type": "integer",
+      "description": "An ID which identifies the site selling the impression"
+    },
+    "aid": {
+      "type": "integer",
+      "description": "An ID which identifies the channel"
+    },
+    "bidFloor": {
+      "type": "number",
+      "description": "BidFloor, US Dollars"
+    }
+  },
+  "required": ["aid"]
+}
diff --git a/stored_responses/stored_responses.go b/stored_responses/stored_responses.go
index 25984a7d..533ff2c7 100644
--- a/stored_responses/stored_responses.go
+++ b/stored_responses/stored_responses.go
@@ -17,6 +17,8 @@ type StoredResponseIdToStoredResponse map[string]json.RawMessage
 type BidderImpsWithBidResponses map[openrtb_ext.BidderName]map[string]json.RawMessage
 type ImpsWithBidResponses map[string]json.RawMessage
 type ImpBidderStoredResp map[string]map[string]json.RawMessage
+type ImpBidderReplaceImpID map[string]map[string]bool
+type BidderImpReplaceImpID map[string]map[string]bool
 
 func InitStoredBidResponses(req *openrtb2.BidRequest, storedBidResponses ImpBidderStoredResp) BidderImpsWithBidResponses {
 	removeImpsWithStoredResponses(req, storedBidResponses)
@@ -57,7 +59,9 @@ func extractStoredResponsesIds(impInfo []ImpExtPrebidData,
 	bidderMap map[string]openrtb_ext.BidderName) (
 	StoredResponseIDs,
 	ImpBiddersWithBidResponseIDs,
-	ImpsWithAuctionResponseIDs, error,
+	ImpsWithAuctionResponseIDs,
+	ImpBidderReplaceImpID,
+	error,
 ) {
 	// extractStoredResponsesIds returns:
 	// 1) all stored responses ids from all imps
@@ -66,16 +70,18 @@ func extractStoredResponsesIds(impInfo []ImpExtPrebidData,
 	impBiddersWithBidResponseIDs := ImpBiddersWithBidResponseIDs{}
 	// 3) imp id to stored resp id
 	impAuctionResponseIDs := ImpsWithAuctionResponseIDs{}
+	// 4) imp id to bidder to bool replace imp in response
+	impBidderReplaceImp := ImpBidderReplaceImpID{}
 
 	for index, impData := range impInfo {
 		impId, err := jsonparser.GetString(impData.Imp, "id")
 		if err != nil {
-			return nil, nil, nil, fmt.Errorf("request.imp[%d] missing required field: \"id\"", index)
+			return nil, nil, nil, nil, fmt.Errorf("request.imp[%d] missing required field: \"id\"", index)
 		}
 
 		if impData.ImpExtPrebid.StoredAuctionResponse != nil {
 			if len(impData.ImpExtPrebid.StoredAuctionResponse.ID) == 0 {
-				return nil, nil, nil, fmt.Errorf("request.imp[%d] has ext.prebid.storedauctionresponse specified, but \"id\" field is missing ", index)
+				return nil, nil, nil, nil, fmt.Errorf("request.imp[%d] has ext.prebid.storedauctionresponse specified, but \"id\" field is missing ", index)
 			}
 			allStoredResponseIDs = append(allStoredResponseIDs, impData.ImpExtPrebid.StoredAuctionResponse.ID)
 
@@ -85,24 +91,35 @@ func extractStoredResponsesIds(impInfo []ImpExtPrebidData,
 		if len(impData.ImpExtPrebid.StoredBidResponse) > 0 {
 
 			bidderStoredRespId := make(map[string]string)
+			bidderReplaceImpId := make(map[string]bool)
 			for _, bidderResp := range impData.ImpExtPrebid.StoredBidResponse {
 				if len(bidderResp.ID) == 0 || len(bidderResp.Bidder) == 0 {
-					return nil, nil, nil, fmt.Errorf("request.imp[%d] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ", index)
+					return nil, nil, nil, nil, fmt.Errorf("request.imp[%d] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ", index)
 				}
 				//check if bidder is valid/exists
 				if _, isValid := bidderMap[bidderResp.Bidder]; !isValid {
-					return nil, nil, nil, fmt.Errorf("request.imp[impId: %s].ext contains unknown bidder: %s. Did you forget an alias in request.ext.prebid.aliases?", impId, bidderResp.Bidder)
+					return nil, nil, nil, nil, fmt.Errorf("request.imp[impId: %s].ext contains unknown bidder: %s. Did you forget an alias in request.ext.prebid.aliases?", impId, bidderResp.Bidder)
 				}
 				// bidder is unique per one bid stored response
 				// if more than one bidder specified the last defined bidder id will take precedence
 				bidderStoredRespId[bidderResp.Bidder] = bidderResp.ID
 				impBiddersWithBidResponseIDs[impId] = bidderStoredRespId
+
+				// stored response config can specify if imp id should be replaced with imp id from request
+				replaceImpId := true
+				if bidderResp.ReplaceImpId != nil {
+					// replaceimpid is true if not specified
+					replaceImpId = *bidderResp.ReplaceImpId
+				}
+				bidderReplaceImpId[bidderResp.Bidder] = replaceImpId
+				impBidderReplaceImp[impId] = bidderReplaceImpId
+
 				//storedAuctionResponseIds are not unique, but fetch will return single data for repeated ids
 				allStoredResponseIDs = append(allStoredResponseIDs, bidderResp.ID)
 			}
 		}
 	}
-	return allStoredResponseIDs, impBiddersWithBidResponseIDs, impAuctionResponseIDs, nil
+	return allStoredResponseIDs, impBiddersWithBidResponseIDs, impAuctionResponseIDs, impBidderReplaceImp, nil
 }
 
 // ProcessStoredResponses takes the incoming request as JSON with any
@@ -111,26 +128,41 @@ func extractStoredResponsesIds(impInfo []ImpExtPrebidData,
 // Note that processStoredResponses must be called after processStoredRequests
 // because stored imps and stored requests can contain stored auction responses and stored bid responses
 // so the stored requests/imps have to be merged into the incoming request prior to processing stored auction responses.
-func ProcessStoredResponses(ctx context.Context, requestJson []byte, storedRespFetcher stored_requests.Fetcher, bidderMap map[string]openrtb_ext.BidderName) (ImpsWithBidResponses, ImpBidderStoredResp, []error) {
+func ProcessStoredResponses(ctx context.Context, requestJson []byte, storedRespFetcher stored_requests.Fetcher, bidderMap map[string]openrtb_ext.BidderName) (ImpsWithBidResponses, ImpBidderStoredResp, BidderImpReplaceImpID, []error) {
 	impInfo, errs := parseImpInfo(requestJson)
 	if len(errs) > 0 {
-		return nil, nil, errs
+		return nil, nil, nil, errs
 	}
-	storedResponsesIds, impBidderToStoredBidResponseId, impIdToRespId, err := extractStoredResponsesIds(impInfo, bidderMap)
+	storedResponsesIds, impBidderToStoredBidResponseId, impIdToRespId, impBidderReplaceImp, err := extractStoredResponsesIds(impInfo, bidderMap)
 	if err != nil {
-		return nil, nil, append(errs, err)
+		return nil, nil, nil, append(errs, err)
 	}
 
 	if len(storedResponsesIds) > 0 {
 		storedResponses, errs := storedRespFetcher.FetchResponses(ctx, storedResponsesIds)
 		if len(errs) > 0 {
-			return nil, nil, errs
+			return nil, nil, nil, errs
 		}
+		bidderImpIdReplaceImp := flipMap(impBidderReplaceImp)
 
 		impIdToStoredResp, impBidderToStoredBidResponse := buildStoredResponsesMaps(storedResponses, impBidderToStoredBidResponseId, impIdToRespId)
-		return impIdToStoredResp, impBidderToStoredBidResponse, nil
+		return impIdToStoredResp, impBidderToStoredBidResponse, bidderImpIdReplaceImp, nil
+	}
+	return nil, nil, nil, nil
+}
+
+// flipMap takes map[impID][bidderName]replaceImpId and modifies it to map[bidderName][impId]replaceImpId
+func flipMap(impBidderReplaceImpId ImpBidderReplaceImpID) BidderImpReplaceImpID {
+	flippedMap := BidderImpReplaceImpID{}
+	for impId, impData := range impBidderReplaceImpId {
+		for bidder, replaceImpId := range impData {
+			if _, ok := flippedMap[bidder]; !ok {
+				flippedMap[bidder] = make(map[string]bool)
+			}
+			flippedMap[bidder][impId] = replaceImpId
+		}
 	}
-	return nil, nil, nil
+	return flippedMap
 }
 
 func buildStoredResponsesMaps(storedResponses StoredResponseIdToStoredResponse, impBidderToStoredBidResponseId ImpBiddersWithBidResponseIDs, impIdToRespId ImpsWithAuctionResponseIDs) (ImpsWithBidResponses, ImpBidderStoredResp) {


Test patch:
diff --git a/adapters/adnuntius/adnuntiustest/exemplary/simple-banner.json b/adapters/adnuntius/adnuntiustest/exemplary/simple-banner.json
index 6e943810..3a50789e 100644
--- a/adapters/adnuntius/adnuntiustest/exemplary/simple-banner.json
+++ b/adapters/adnuntius/adnuntiustest/exemplary/simple-banner.json
@@ -36,7 +36,8 @@
                     "adUnits": [
                         {
                             "auId": "123",
-                            "targetId": "123-test-imp-id"
+                            "targetId": "123-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
                         }
                     ],
                     "context": "prebid.org",
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/banner-nil-check.json b/adapters/adnuntius/adnuntiustest/supplemental/banner-nil-check.json
new file mode 100644
index 00000000..ae6b7cd8
--- /dev/null
+++ b/adapters/adnuntius/adnuntiustest/supplemental/banner-nil-check.json
@@ -0,0 +1,29 @@
+{
+	"mockBidRequest": {
+		"id": "test-request-id",
+		"user": {
+			"id": "1kjh3429kjh295jkl"
+		},
+		"device":{
+			"ext":{
+				"noCookies" : true
+			}
+		},
+		"imp": [
+			{
+				"id": "test-imp-id",
+				"ext": {
+					"bidder": {
+						"auId": "123"
+					}
+				}
+			}
+		]
+	},
+	"expectedMakeRequestsErrors": [
+		{
+			"value": "ignoring imp id=test-imp-id, Adnuntius supports only Banner",
+			"comparison": "literal"
+		}
+	]
+}
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/check-gdpr.json b/adapters/adnuntius/adnuntiustest/supplemental/check-gdpr.json
index d1dfa0ad..578722bd 100644
--- a/adapters/adnuntius/adnuntiustest/supplemental/check-gdpr.json
+++ b/adapters/adnuntius/adnuntiustest/supplemental/check-gdpr.json
@@ -43,7 +43,8 @@
 					"adUnits": [
 						{
 							"auId": "123",
-							"targetId": "123-test-imp-id"
+							"targetId": "123-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
 						}
 					],
 					"metaData": {
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/check-noCookies.json b/adapters/adnuntius/adnuntiustest/supplemental/check-noCookies.json
new file mode 100644
index 00000000..f1ddd3f7
--- /dev/null
+++ b/adapters/adnuntius/adnuntiustest/supplemental/check-noCookies.json
@@ -0,0 +1,108 @@
+{
+	"mockBidRequest": {
+		"id": "test-request-id",
+		"user": {
+			"id": "1kjh3429kjh295jkl"
+		},
+		"device":{
+			"ext":{
+				"noCookies" : true
+			}
+		},
+		"imp": [
+			{
+				"id": "test-imp-id",
+				"banner": {
+					"format": [
+						{
+							"w": 300,
+							"h": 250
+						},
+						{
+							"w": 300,
+							"h": 600
+						}
+					]
+				},
+				"ext": {
+					"bidder": {
+						"auId": "123"
+					}
+				}
+			}
+		]
+	},
+	"httpCalls": [
+		{
+			"expectedRequest": {
+				"uri": "http://whatever.url?format=json&noCookies=true&tzo=0",
+				"body": {
+					"adUnits": [
+						{
+							"auId": "123",
+							"targetId": "123-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
+						}
+					],
+					"metaData": {
+						"usi": "1kjh3429kjh295jkl"
+					},
+					"context": "unknown"
+				}
+			},
+			"mockResponse": {
+				"status": 200,
+				"body": {
+					"adUnits": [
+						{
+							"auId": "0000000000000123",
+							"targetId": "123-test-imp-id",
+							"html": "<ADCODE>",
+							"responseId": "adn-rsp-900646517",
+							"ads": [
+								{
+									"destinationUrls": {
+										"url": "http://www.google.com"
+									},
+									"bid": {
+										"amount": 20.0,
+										"currency": "NOK"
+									},
+									"adId": "adn-id-1559784094",
+									"creativeWidth": "980",
+									"creativeHeight": "240",
+									"creativeId": "jn9hpzvlsf8cpdmm",
+									"lineItemId": "q7y9qm5b0xt9htrv"
+								}
+							]
+						}
+					]
+				}
+			}
+		}
+	],
+	"expectedBidResponses": [
+		{
+			"bids": [
+				{
+					"bid": {
+						"id": "adn-id-1559784094",
+						"impid": "test-imp-id",
+						"price": 20000,
+						"adm": "<ADCODE>",
+						"adid": "adn-id-1559784094",
+						"adomain": [
+							"google.com"
+						],
+						"cid": "q7y9qm5b0xt9htrv",
+						"crid": "jn9hpzvlsf8cpdmm",
+						"w": 980,
+						"h": 240
+					},
+					"type": "banner"
+				}
+			],
+			"currency": "NOK"
+		}
+	]
+}
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/check-userId.json b/adapters/adnuntius/adnuntiustest/supplemental/check-userId.json
index b4788ba6..4b8e6de3 100644
--- a/adapters/adnuntius/adnuntiustest/supplemental/check-userId.json
+++ b/adapters/adnuntius/adnuntiustest/supplemental/check-userId.json
@@ -35,7 +35,8 @@
 					"adUnits": [
 						{
 							"auId": "123",
-							"targetId": "123-test-imp-id"
+							"targetId": "123-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
 						}
 					],
 					"metaData": {
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/empty-regs-ext.json b/adapters/adnuntius/adnuntiustest/supplemental/empty-regs-ext.json
index f352e11d..f3aebd99 100644
--- a/adapters/adnuntius/adnuntiustest/supplemental/empty-regs-ext.json
+++ b/adapters/adnuntius/adnuntiustest/supplemental/empty-regs-ext.json
@@ -38,7 +38,8 @@
 					"adUnits": [
 						{
 							"auId": "123",
-							"targetId": "123-test-imp-id"
+							"targetId": "123-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
 						}
 					],
 					"metaData": {
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/empty-regs.json b/adapters/adnuntius/adnuntiustest/supplemental/empty-regs.json
index 9c56b4cd..06593630 100644
--- a/adapters/adnuntius/adnuntiustest/supplemental/empty-regs.json
+++ b/adapters/adnuntius/adnuntiustest/supplemental/empty-regs.json
@@ -37,7 +37,8 @@
 					"adUnits": [
 						{
 							"auId": "123",
-							"targetId": "123-test-imp-id"
+							"targetId": "123-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
 						}
 					],
 					"metaData": {
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/height-error.json b/adapters/adnuntius/adnuntiustest/supplemental/height-error.json
index 51204648..1f213a43 100644
--- a/adapters/adnuntius/adnuntiustest/supplemental/height-error.json
+++ b/adapters/adnuntius/adnuntiustest/supplemental/height-error.json
@@ -35,7 +35,8 @@
 					"adUnits": [
 						{
 							"auId": "123",
-							"targetId": "123-test-imp-id"
+							"targetId": "123-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
 						}
 					],
 					"context": "prebid.org",
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/send-header-information.json b/adapters/adnuntius/adnuntiustest/supplemental/send-header-information.json
index a0c87682..ea629fe6 100644
--- a/adapters/adnuntius/adnuntiustest/supplemental/send-header-information.json
+++ b/adapters/adnuntius/adnuntiustest/supplemental/send-header-information.json
@@ -56,7 +56,8 @@
 					"adUnits": [
 						{
 							"auId": "123",
-							"targetId": "123-test-imp-id"
+							"targetId": "123-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
 						}
 					],
 					"metaData": {
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/size-check.json b/adapters/adnuntius/adnuntiustest/supplemental/size-check.json
new file mode 100644
index 00000000..c05428c1
--- /dev/null
+++ b/adapters/adnuntius/adnuntiustest/supplemental/size-check.json
@@ -0,0 +1,101 @@
+{
+	"mockBidRequest": {
+		"id": "test-request-id",
+		"user": {
+			"id": "1kjh3429kjh295jkl"
+		},
+		"device":{
+			"ext":{
+				"noCookies" : true
+			}
+		},
+		"imp": [
+			{
+				"id": "test-imp-id",
+				"banner":{
+						"w": 300,
+						"h": 600
+					}
+				,
+				"ext": {
+					"bidder": {
+						"auId": "123"
+					}
+				}
+			}
+		]
+	},
+	"httpCalls": [
+		{
+			"expectedRequest": {
+				"uri": "http://whatever.url?format=json&noCookies=true&tzo=0",
+				"body": {
+					"adUnits": [
+						{
+							"auId": "123",
+							"targetId": "123-test-imp-id",
+							"dimensions": [[300,600]]
+						}
+					],
+					"metaData": {
+						"usi": "1kjh3429kjh295jkl"
+					},
+					"context": "unknown"
+				}
+			},
+			"mockResponse": {
+				"status": 200,
+				"body": {
+					"adUnits": [
+						{
+							"auId": "0000000000000123",
+							"targetId": "123-test-imp-id",
+							"html": "<ADCODE>",
+							"responseId": "adn-rsp-900646517",
+							"ads": [
+								{
+									"destinationUrls": {
+										"url": "http://www.google.com"
+									},
+									"bid": {
+										"amount": 20.0,
+										"currency": "NOK"
+									},
+									"adId": "adn-id-1559784094",
+									"creativeWidth": "980",
+									"creativeHeight": "240",
+									"creativeId": "jn9hpzvlsf8cpdmm",
+									"lineItemId": "q7y9qm5b0xt9htrv"
+								}
+							]
+						}
+					]
+				}
+			}
+		}
+	],
+	"expectedBidResponses": [
+		{
+			"bids": [
+				{
+					"bid": {
+						"id": "adn-id-1559784094",
+						"impid": "test-imp-id",
+						"price": 20000,
+						"adm": "<ADCODE>",
+						"adid": "adn-id-1559784094",
+						"adomain": [
+							"google.com"
+						],
+						"cid": "q7y9qm5b0xt9htrv",
+						"crid": "jn9hpzvlsf8cpdmm",
+						"w": 980,
+						"h": 240
+					},
+					"type": "banner"
+				}
+			],
+			"currency": "NOK"
+		}
+	]
+}
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/status-400.json b/adapters/adnuntius/adnuntiustest/supplemental/status-400.json
index 70441b7b..f8407b1d 100644
--- a/adapters/adnuntius/adnuntiustest/supplemental/status-400.json
+++ b/adapters/adnuntius/adnuntiustest/supplemental/status-400.json
@@ -32,7 +32,8 @@
 					"adUnits": [
 						{
 							"auId": "",
-							"targetId": "-test-imp-id"
+							"targetId": "-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
 						}
 					],
 					"context": "unknown",
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/test-networks.json b/adapters/adnuntius/adnuntiustest/supplemental/test-networks.json
index 28b892bd..bbeb9a66 100644
--- a/adapters/adnuntius/adnuntiustest/supplemental/test-networks.json
+++ b/adapters/adnuntius/adnuntiustest/supplemental/test-networks.json
@@ -33,7 +33,8 @@
 					"adUnits": [
 						{
 							"auId": "123",
-							"targetId": "123-test-network"
+							"targetId": "123-test-network",
+							"dimensions": [[300,250],[300,600]]
 						}
 					],
 					"context": "unknown",
diff --git a/adapters/adnuntius/adnuntiustest/supplemental/width-error.json b/adapters/adnuntius/adnuntiustest/supplemental/width-error.json
index 14a1e833..1fd8a83f 100644
--- a/adapters/adnuntius/adnuntiustest/supplemental/width-error.json
+++ b/adapters/adnuntius/adnuntiustest/supplemental/width-error.json
@@ -35,7 +35,8 @@
 					"adUnits": [
 						{
 							"auId": "123",
-							"targetId": "123-test-imp-id"
+							"targetId": "123-test-imp-id",
+							"dimensions": [[300,250],[300,600]]
 						}
 					],
 					"context": "prebid.org",
diff --git a/adapters/adrino/adrino_test.go b/adapters/adrino/adrino_test.go
new file mode 100644
index 00000000..a7c19d02
--- /dev/null
+++ b/adapters/adrino/adrino_test.go
@@ -0,0 +1,28 @@
+package adrino
+
+import (
+	"github.com/stretchr/testify/assert"
+	"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.BidderAdrino, config.Adapter{
+		Endpoint: "https://prd-prebid-bidder.adrino.io/openrtb/bid"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "adrinotest", bidder)
+}
+
+func TestEndpointTemplateMalformed(t *testing.T) {
+	_, buildErr := Builder(openrtb_ext.BidderAdrino, config.Adapter{
+		Endpoint: "{{Malformed}}"})
+
+	assert.Nil(t, buildErr)
+}
diff --git a/adapters/adrino/adrinotest/exemplary/no-bid.json b/adapters/adrino/adrinotest/exemplary/no-bid.json
new file mode 100644
index 00000000..66b64e14
--- /dev/null
+++ b/adapters/adrino/adrinotest/exemplary/no-bid.json
@@ -0,0 +1,73 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "ext": {
+                    "bidder": {
+                        "hash": "123456"
+                    }
+                },
+                "native": {
+                    "request": "{json string}",
+                    "ver": "1.2"
+                }
+            }
+        ],
+        "site": {
+            "publisher": {
+                "id": "1"
+            },
+            "page": "some-page-url"
+        },
+        "device": {
+            "w": 1920,
+            "h": 800
+        }
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://prd-prebid-bidder.adrino.io/openrtb/bid",
+                "headers": {
+                    "Content-Type": [
+                        "application/json;charset=utf-8"
+                    ]
+                },
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "ext": {
+                                "bidder": {
+                                    "hash": "123456"
+                                }
+                            },
+                            "native": {
+                                "request": "{json string}",
+                                "ver": "1.2"
+                            }
+                        }
+                    ],
+                    "site": {
+                        "publisher": {
+                            "id": "1"
+                        },
+                        "page": "some-page-url"
+                    },
+                    "device": {
+                        "w": 1920,
+                        "h": 800
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 204,
+                "body": {}
+            }
+        }
+    ],
+    "expectedBidResponses": []
+}
diff --git a/adapters/adrino/adrinotest/exemplary/single-native.json b/adapters/adrino/adrinotest/exemplary/single-native.json
new file mode 100644
index 00000000..41e4b5ef
--- /dev/null
+++ b/adapters/adrino/adrinotest/exemplary/single-native.json
@@ -0,0 +1,115 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "ext": {
+                    "bidder": {
+                        "hash": "123456"
+                    }
+                },
+                "native": {
+                    "request": "{json string}",
+                    "ver": "1.2"
+                }
+            }
+        ],
+        "site": {
+            "publisher": {
+                "id": "1"
+            },
+            "page": "some-page-url"
+        },
+        "device": {
+            "w": 1920,
+            "h": 800
+        }
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://prd-prebid-bidder.adrino.io/openrtb/bid",
+                "headers": {
+                    "Content-Type": [
+                        "application/json;charset=utf-8"
+                    ]
+                },
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "ext": {
+                                "bidder": {
+                                    "hash": "123456"
+                                }
+                            },
+                            "native": {
+                                "request": "{json string}",
+                                "ver": "1.2"
+                            }
+                        }
+                    ],
+                    "site": {
+                        "publisher": {
+                            "id": "1"
+                        },
+                        "page": "some-page-url"
+                    },
+                    "device": {
+                        "w": 1920,
+                        "h": 800
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test-bid-id",
+                                    "impid": "test-imp-id",
+                                    "price": 10,
+                                    "adm": "{json response string}",
+                                    "crid": "test-creative-id",
+                                    "ext": {
+                                        "prebid": {
+                                            "type": "native"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "cur": "PLN"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "PLN",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test-bid-id",
+                        "impid": "test-imp-id",
+                        "price": 10,
+                        "adm": "{json response string}",
+                        "crid": "test-creative-id",
+                        "ext": {
+                            "prebid": {
+                                "type": "native"
+                            }
+                        }
+                    },
+                    "type": "native"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/adrino/adrinotest/supplemental/unknown-hash.json b/adapters/adrino/adrinotest/supplemental/unknown-hash.json
new file mode 100644
index 00000000..e678b526
--- /dev/null
+++ b/adapters/adrino/adrinotest/supplemental/unknown-hash.json
@@ -0,0 +1,73 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "ext": {
+                    "bidder": {
+                        "hash": "some-unknown-hash"
+                    }
+                },
+                "native": {
+                    "request": "{json string}",
+                    "ver": "1.2"
+                }
+            }
+        ],
+        "site": {
+            "publisher": {
+                "id": "1"
+            },
+            "page": "some-page-url"
+        },
+        "device": {
+            "w": 1920,
+            "h": 800
+        }
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://prd-prebid-bidder.adrino.io/openrtb/bid",
+                "headers": {
+                    "Content-Type": [
+                        "application/json;charset=utf-8"
+                    ]
+                },
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "ext": {
+                                "bidder": {
+                                    "hash": "some-unknown-hash"
+                                }
+                            },
+                            "native": {
+                                "request": "{json string}",
+                                "ver": "1.2"
+                            }
+                        }
+                    ],
+                    "site": {
+                        "publisher": {
+                            "id": "1"
+                        },
+                        "page": "some-page-url"
+                    },
+                    "device": {
+                        "w": 1920,
+                        "h": 800
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 204,
+                "body": {}
+            }
+        }
+    ],
+    "expectedBidResponses": []
+}
diff --git a/adapters/adrino/params_test.go b/adapters/adrino/params_test.go
new file mode 100644
index 00000000..f82f08ce
--- /dev/null
+++ b/adapters/adrino/params_test.go
@@ -0,0 +1,51 @@
+package adrino
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/adrino.json
+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.BidderAdrino, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected adrino 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.BidderAdrino, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"hash":"abc123"}`,
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{"hash":""}`,
+	`{"aid":123, "placementId":"123", "siteId":"321"}`,
+}
diff --git a/adapters/adtrgtme/adtrgtme_test.go b/adapters/adtrgtme/adtrgtme_test.go
new file mode 100644
index 00000000..e3f87c20
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtme_test.go
@@ -0,0 +1,20 @@
+package adtrgtme
+
+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.BidderAdtrgtme, config.Adapter{
+		Endpoint: "http://localhost/ssp"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "adtrgtmetest", bidder)
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/exemplary/banner-app.json b/adapters/adtrgtme/adtrgtmetest/exemplary/banner-app.json
new file mode 100644
index 00000000..406c1863
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/exemplary/banner-app.json
@@ -0,0 +1,146 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "at": 1,
+    "tmax": 300,
+    "imp": [
+      {
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "id": "test-bid-id",
+        "bidfloor": 1,
+        "bidfloorcur": "USD",
+        "secure": 1
+      }
+    ],
+    "app": {
+      "publisher": {
+        "id": "test-site-publisher-id"
+      },
+      "cat": [
+        "test-cat"
+      ],
+      "bundle": "com.app.test",
+      "name": "Test App",
+      "domain": "testapp.com",
+      "id": "123456789"
+    },
+    "device": {
+      "ua": "test-device-user-agent",
+      "ip": "123.123.123.123",
+      "geo": {
+        "country": "TST"
+      },
+      "os": "Android",
+      "language": "en"
+    },
+    "user": {
+      "id": "test-user-id"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-device-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ]
+        },
+        "body": {
+          "id": "test-req-id",
+          "at": 1,
+          "tmax": 300,
+          "imp": [
+            {
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "id": "test-bid-id",
+              "bidfloor": 1,
+              "bidfloorcur": "USD",
+              "secure": 1
+            }
+          ],
+          "app": {
+            "publisher": {
+              "id": "test-site-publisher-id"
+            },
+            "cat": [
+              "test-cat"
+            ],
+            "bundle": "com.app.test",
+            "name": "Test App",
+            "domain": "testapp.com",
+            "id": "123456789"
+          },
+          "device": {
+            "ua": "test-device-user-agent",
+            "ip": "123.123.123.123",
+            "geo": {
+              "country": "TST"
+            },
+            "os": "Android",
+            "language": "en"
+          },
+          "user": {
+            "id": "test-user-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-req-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test-bid-id",
+                  "impid": "test-bid-id",
+                  "price": 1,
+                  "adm": "<div>test</div>",
+                  "crid": "test-creative-id"
+                }
+              ]
+            }
+          ],
+          "bidid": "test-seatbid-id",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-bid-id",
+            "impid": "test-bid-id",
+            "price": 1,
+            "adm": "<div>test</div>",
+            "crid": "test-creative-id"
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/exemplary/banner-web.json b/adapters/adtrgtme/adtrgtmetest/exemplary/banner-web.json
new file mode 100644
index 00000000..44f14aac
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/exemplary/banner-web.json
@@ -0,0 +1,142 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "at": 1,
+    "tmax": 300,
+    "imp": [
+      {
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "id": "test-bid-id",
+        "bidfloor": 1,
+        "bidfloorcur": "USD",
+        "secure": 1
+      }
+    ],
+    "site": {
+      "id": "123456789",
+      "cat": [
+        "test-cat"
+      ],
+      "page": "test-page-site",
+      "publisher": {
+        "id": "test-site-publisher-id"
+      }
+    },
+    "device": {
+      "ua": "test-device-user-agent",
+      "ip": "123.123.123.123",
+      "geo": {
+        "country": "TST"
+      },
+      "os": "Android",
+      "language": "en"
+    },
+    "user": {
+      "id": "test-user-id"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-device-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ]
+        },
+        "body": {
+          "id": "test-req-id",
+          "at": 1,
+          "tmax": 300,
+          "imp": [
+            {
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "id": "test-bid-id",
+              "bidfloor": 1,
+              "bidfloorcur": "USD",
+              "secure": 1
+            }
+          ],
+          "site": {
+            "id": "123456789",
+            "cat": [
+              "test-cat"
+            ],
+            "page": "test-page-site",
+            "publisher": {
+              "id": "test-site-publisher-id"
+            }
+          },
+          "device": {
+            "ua": "test-device-user-agent",
+            "ip": "123.123.123.123",
+            "geo": {
+              "country": "TST"
+            },
+            "os": "Android",
+            "language": "en"
+          },
+          "user": {
+            "id": "test-user-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-req-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test-bid-id",
+                  "impid": "test-bid-id",
+                  "price": 1,
+                  "adm": "<div>test</div>",
+                  "crid": "test-creative-id"
+                }
+              ]
+            }
+          ],
+          "bidid": "test-seatbid-id",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-bid-id",
+            "impid": "test-bid-id",
+            "price": 1,
+            "adm": "<div>test</div>",
+            "crid": "test-creative-id"
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/banner-app-headers-ipv6.json b/adapters/adtrgtme/adtrgtmetest/supplemental/banner-app-headers-ipv6.json
new file mode 100644
index 00000000..7537a902
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/banner-app-headers-ipv6.json
@@ -0,0 +1,146 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "at": 1,
+    "tmax": 300,
+    "imp": [
+      {
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "id": "test-bid-id",
+        "bidfloor": 1,
+        "bidfloorcur": "USD",
+        "secure": 1
+      }
+    ],
+    "app": {
+      "publisher": {
+        "id": "test-site-publisher-id"
+      },
+      "cat": [
+        "test-cat"
+      ],
+      "bundle": "com.app.test",
+      "name": "Test App",
+      "domain": "testapp.com",
+      "id": "123456789"
+    },
+    "device": {
+      "ua": "test-device-user-agent",
+      "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+      "geo": {
+        "country": "TST"
+      },
+      "os": "Android",
+      "language": "en"
+    },
+    "user": {
+      "id": "test-user-id"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-device-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
+          ]
+        },
+        "body": {
+          "id": "test-req-id",
+          "at": 1,
+          "tmax": 300,
+          "imp": [
+            {
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "id": "test-bid-id",
+              "bidfloor": 1,
+              "bidfloorcur": "USD",
+              "secure": 1
+            }
+          ],
+          "app": {
+            "publisher": {
+              "id": "test-site-publisher-id"
+            },
+            "cat": [
+              "test-cat"
+            ],
+            "bundle": "com.app.test",
+            "name": "Test App",
+            "domain": "testapp.com",
+            "id": "123456789"
+          },
+          "device": {
+            "ua": "test-device-user-agent",
+            "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+            "geo": {
+              "country": "TST"
+            },
+            "os": "Android",
+            "language": "en"
+          },
+          "user": {
+            "id": "test-user-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-req-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test-bid-id",
+                  "impid": "test-bid-id",
+                  "price": 1,
+                  "adm": "<div>test</div>",
+                  "crid": "test-creative-id"
+                }
+              ]
+            }
+          ],
+          "bidid": "test-seatbid-id",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-bid-id",
+            "impid": "test-bid-id",
+            "price": 1,
+            "adm": "<div>test</div>",
+            "crid": "test-creative-id"
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/banner-web-headers-ipv6.json b/adapters/adtrgtme/adtrgtmetest/supplemental/banner-web-headers-ipv6.json
new file mode 100644
index 00000000..3ad7cf11
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/banner-web-headers-ipv6.json
@@ -0,0 +1,142 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "at": 1,
+    "tmax": 300,
+    "imp": [
+      {
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "id": "test-bid-id",
+        "bidfloor": 1,
+        "bidfloorcur": "USD",
+        "secure": 1
+      }
+    ],
+    "site": {
+      "id": "123456789",
+      "cat": [
+        "test-cat"
+      ],
+      "page": "test-page-site",
+      "publisher": {
+        "id": "test-site-publisher-id"
+      }
+    },
+    "device": {
+      "ua": "test-device-user-agent",
+      "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+      "geo": {
+        "country": "TST"
+      },
+      "os": "Android",
+      "language": "en"
+    },
+    "user": {
+      "id": "test-user-id"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-device-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
+          ]
+        },
+        "body": {
+          "id": "test-req-id",
+          "at": 1,
+          "tmax": 300,
+          "imp": [
+            {
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "id": "test-bid-id",
+              "bidfloor": 1,
+              "bidfloorcur": "USD",
+              "secure": 1
+            }
+          ],
+          "site": {
+            "id": "123456789",
+            "cat": [
+              "test-cat"
+            ],
+            "page": "test-page-site",
+            "publisher": {
+              "id": "test-site-publisher-id"
+            }
+          },
+          "device": {
+            "ua": "test-device-user-agent",
+            "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+            "geo": {
+              "country": "TST"
+            },
+            "os": "Android",
+            "language": "en"
+          },
+          "user": {
+            "id": "test-user-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-req-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test-bid-id",
+                  "impid": "test-bid-id",
+                  "price": 1,
+                  "adm": "<div>test</div>",
+                  "crid": "test-creative-id"
+                }
+              ]
+            }
+          ],
+          "bidid": "test-seatbid-id",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-bid-id",
+            "impid": "test-bid-id",
+            "price": 1,
+            "adm": "<div>test</div>",
+            "crid": "test-creative-id"
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/empty-seatbid-array.json b/adapters/adtrgtme/adtrgtmetest/supplemental/empty-seatbid-array.json
new file mode 100644
index 00000000..0f2b55be
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/empty-seatbid-array.json
@@ -0,0 +1,125 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "test-buyer-id"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "test-cat"
+      ],
+      "bundle": "com.app.test",
+      "name": "test-app",
+      "domain": "test-app.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "tagid": "test-adtarget-test",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        }
+      }
+    ]
+  },
+  "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"
+          ]
+        },
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "body": {
+          "id": "test-req-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              },
+              "tagid": "test-adtarget-test"
+            }
+          ],
+          "app": {
+            "id": "123456789",
+            "name": "test-app",
+            "bundle": "com.app.test",
+            "domain": "test-app.com",
+            "cat": [
+              "test-cat"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "test-buyer-id"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-resp-id",
+          "seatbid": [
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "mockResponse": {
+    "status": 200,
+    "body": "invalid response"
+  },
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Empty SeatBid array",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/invalid-response.json b/adapters/adtrgtme/adtrgtmetest/supplemental/invalid-response.json
new file mode 100644
index 00000000..b25ff67d
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/invalid-response.json
@@ -0,0 +1,106 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "test-buyer-id"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.test",
+      "name": "test-app",
+      "domain": "test-app.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "tagid": "test-adtarget-test",
+        "banner": {
+          "w": 320,
+          "h": 50
+        }
+      }
+    ]
+  },
+  "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"
+          ]
+        },
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "body": {
+          "id": "test-req-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "w": 320,
+                "h": 50
+              },
+              "tagid": "test-adtarget-test"
+            }
+          ],
+          "app": {
+            "id": "123456789",
+            "name": "test-app",
+            "bundle": "com.app.test",
+            "domain": "test-app.com",
+            "cat": [
+              "IAB22-1"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "test-buyer-id"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": "invalid response"
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bad Server Response",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/not-found-imp.json b/adapters/adtrgtme/adtrgtmetest/supplemental/not-found-imp.json
new file mode 100644
index 00000000..8ca9a407
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/not-found-imp.json
@@ -0,0 +1,128 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "test-user-id"
+    },
+    "site": {
+      "id": "123456789",
+      "page": "mock-site.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-unsupported-imp-id",
+        "tagid": "test-adtarget-tag-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        }
+      }
+    ]
+  },
+  "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"
+          ]
+        },
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "body": {
+          "id": "test-req-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "test-unsupported-imp-id",
+              "tagid": "test-adtarget-tag-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              }
+            }
+          ],
+          "site": {
+            "id": "123456789",
+            "page": "mock-site.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "test-user-id"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test-imp-id",
+                  "impid": "test-not-found-imp-id",
+                  "price": 3.5,
+                  "adm": "test-imp-mrkp",
+                  "adomain": [
+                    "mock-site.com"
+                  ],
+                  "crid": "20",
+                  "w": 1280,
+                  "h": 720
+                }
+              ],
+              "seat": "aceex"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Failed to find impression: \"test-not-found-imp-id\"",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/not-provided-app-id.json b/adapters/adtrgtme/adtrgtmetest/supplemental/not-provided-app-id.json
new file mode 100644
index 00000000..49c7924a
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/not-provided-app-id.json
@@ -0,0 +1,48 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "at": 1,
+    "tmax": 300,
+    "imp": [
+      {
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "id": "test-bid-id",
+        "bidfloor": 1,
+        "bidfloorcur": "USD",
+        "secure": 1
+      }
+    ],
+    "device": {
+      "ua": "test-device-user-agent",
+      "ip": "123.123.123.123",
+      "geo": {
+        "country": "TST"
+      },
+      "os": "Android",
+      "language": "en"
+    },
+    "app": {
+      "publisher": {
+        "id": "test-site-publisher-id"
+      },
+      "cat": [
+        "test-cat"
+      ],
+      "bundle": "com.app.test",
+      "name": "Test App",
+      "domain": "testapp.com"
+    },
+    "user": {
+      "id": "test-user-id"
+    }
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "request.App.ID is not provided",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/not-provided-site-id.json b/adapters/adtrgtme/adtrgtmetest/supplemental/not-provided-site-id.json
new file mode 100644
index 00000000..bb38d8ef
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/not-provided-site-id.json
@@ -0,0 +1,46 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "at": 1,
+    "tmax": 300,
+    "imp": [
+      {
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "id": "test-bid-id",
+        "bidfloor": 1,
+        "bidfloorcur": "USD",
+        "secure": 1
+      }
+    ],
+    "device": {
+      "ua": "test-device-user-agent",
+      "ip": "123.123.123.123",
+      "geo": {
+        "country": "TST"
+      },
+      "os": "Android",
+      "language": "en"
+    },
+    "site": {
+      "cat": [
+        "test-cat"
+      ],
+      "page": "test-page-site",
+      "publisher": {
+        "id": "test-site-publisher-id"
+      }
+    },
+    "user": {
+      "id": "test-user-id"
+    }
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "request.Site.ID is not provided",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/not-provided-site-or-app.json b/adapters/adtrgtme/adtrgtmetest/supplemental/not-provided-site-or-app.json
new file mode 100644
index 00000000..b4320e17
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/not-provided-site-or-app.json
@@ -0,0 +1,37 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "at": 1,
+    "tmax": 300,
+    "imp": [
+      {
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "id": "test-bid-id",
+        "bidfloor": 1,
+        "bidfloorcur": "USD",
+        "secure": 1
+      }
+    ],
+    "device": {
+      "ua": "test-device-user-agent",
+      "ip": "123.123.123.123",
+      "geo": {
+        "country": "TST"
+      },
+      "os": "Android",
+      "language": "en"
+    },
+    "user": {
+      "id": "test-user-id"
+    }
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "request.Site or request.App are not provided",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-bad-request.json b/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-bad-request.json
new file mode 100644
index 00000000..2aab35e5
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-bad-request.json
@@ -0,0 +1,87 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "test-user"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.test",
+      "name": "test-app",
+      "domain": "test-app.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "tagid": "test-adtarget-tag-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "body": {
+          "id": "test-req-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              },
+              "tagid": "test-adtarget-tag-id"
+            }
+          ],
+          "app": {
+            "publisher": {
+              "id": "123456789"
+            },
+            "cat": [
+              "IAB22-1"
+            ],
+            "bundle": "com.app.test",
+            "name": "test-app",
+            "domain": "test-app.com",
+            "id": "123456789"
+          },
+          "user": {
+            "buyeruid": "test-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 400
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: [ 400 ]",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-internal-server-error.json b/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-internal-server-error.json
new file mode 100644
index 00000000..ed8348f8
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-internal-server-error.json
@@ -0,0 +1,77 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "test-user"
+    },
+    "site": {
+      "id": "123456789",
+      "page": "mock-site.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "tagid": "test-adtarget-tag-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "body": {
+          "id": "test-req-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "tagid": "test-adtarget-tag-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              }
+            }
+          ],
+          "site": {
+            "id": "123456789",
+            "page": "mock-site.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "test-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 500
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: [ 500 ]. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-no-content.json b/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-no-content.json
new file mode 100644
index 00000000..ba886cf3
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-no-content.json
@@ -0,0 +1,72 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "test-user"
+    },
+    "site": {
+      "id": "123456789",
+      "page": "mock-site.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "tagid": "test-adtarget-tag-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "body": {
+          "id": "test-req-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "tagid": "test-adtarget-tag-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              }
+            }
+          ],
+          "site": {
+            "id": "123456789",
+            "page": "mock-site.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "test-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 204
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": []
+}
\ No newline at end of file
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-service-unavaliable.json b/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-service-unavaliable.json
new file mode 100644
index 00000000..69911727
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-service-unavaliable.json
@@ -0,0 +1,77 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "test-user"
+    },
+    "site": {
+      "id": "123456789",
+      "page": "mock-site.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "tagid": "test-adtarget-tag-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "body": {
+          "id": "test-req-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "tagid": "test-adtarget-tag-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              }
+            }
+          ],
+          "site": {
+            "id": "123456789",
+            "page": "mock-site.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "test-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 503
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Something went wrong, please contact your Account Manager. Status Code: [ 503 ] ",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-temporary-redirect.json b/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-temporary-redirect.json
new file mode 100644
index 00000000..159de604
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/status-code-temporary-redirect.json
@@ -0,0 +1,77 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "test-user"
+    },
+    "site": {
+      "id": "123456789",
+      "page": "mock-site.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "tagid": "test-adtarget-tag-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "body": {
+          "id": "test-req-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "tagid": "test-adtarget-tag-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              }
+            }
+          ],
+          "site": {
+            "id": "123456789",
+            "page": "mock-site.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "test-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 307
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: [ 307 ]. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/unsupported-bid-type-native.json b/adapters/adtrgtme/adtrgtmetest/supplemental/unsupported-bid-type-native.json
new file mode 100644
index 00000000..e7765809
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/unsupported-bid-type-native.json
@@ -0,0 +1,118 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "test-user-id"
+    },
+    "site": {
+      "id": "123456789",
+      "page": "mock-site.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-unsupported-imp-id",
+        "tagid": "test-adtarget-tag-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}}}"
+        }
+      }
+    ]
+  },
+  "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"
+          ]
+        },
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "body": {
+          "id": "test-req-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "test-unsupported-imp-id",
+              "tagid": "test-adtarget-tag-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": {
+            "id": "123456789",
+            "page": "mock-site.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "test-user-id"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test-imp-id",
+                  "impid": "test-unsupported-imp-id",
+                  "price": 3.5,
+                  "adm": "test-imp-mrkp",
+                  "adomain": [
+                    "mock-site.com"
+                  ],
+                  "crid": "20",
+                  "w": 1280,
+                  "h": 720
+                }
+              ],
+              "seat": "aceex"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unsupported bidtype for bid: \"test-unsupported-imp-id\"",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adtrgtme/adtrgtmetest/supplemental/unsupported-bid-type-video.json b/adapters/adtrgtme/adtrgtmetest/supplemental/unsupported-bid-type-video.json
new file mode 100644
index 00000000..2394806d
--- /dev/null
+++ b/adapters/adtrgtme/adtrgtmetest/supplemental/unsupported-bid-type-video.json
@@ -0,0 +1,128 @@
+{
+  "mockBidRequest": {
+    "id": "test-req-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "test-user-id"
+    },
+    "site": {
+      "id": "123456789",
+      "page": "mock-site.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "test-unsupported-imp-id",
+        "tagid": "test-adtarget-tag-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        }
+      }
+    ]
+  },
+  "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"
+          ]
+        },
+        "uri": "http://localhost/ssp?s=123456789&prebid",
+        "body": {
+          "id": "test-req-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "test-unsupported-imp-id",
+              "tagid": "test-adtarget-tag-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              }
+            }
+          ],
+          "site": {
+            "id": "123456789",
+            "page": "mock-site.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "test-user-id"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "test-imp-id",
+                  "impid": "test-unsupported-imp-id",
+                  "price": 3.5,
+                  "adm": "test-imp-mrkp",
+                  "adomain": [
+                    "mock-site.com"
+                  ],
+                  "crid": "20",
+                  "w": 1280,
+                  "h": 720
+                }
+              ],
+              "seat": "aceex"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unsupported bidtype for bid: \"test-unsupported-imp-id\"",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/ix/ixtest/exemplary/multi-format-with-ext-prebid-type.json b/adapters/ix/ixtest/exemplary/multi-format-with-ext-prebid-type.json
new file mode 100644
index 00000000..51ea1fd1
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/multi-format-with-ext-prebid-type.json
@@ -0,0 +1,154 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "multi-format-test-imp-id",
+                "banner": {
+                    "w": 300,
+                    "h": 250
+                },
+                "video": {
+                    "mimes": [
+                        "video/mp4"
+                    ],
+                    "minduration": 15,
+                    "maxduration": 30,
+                    "protocols": [
+                        2,
+                        3,
+                        5,
+                        6,
+                        7,
+                        8
+                    ],
+                    "w": 940,
+                    "h": 560
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "multi-format-test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "video": {
+                                "mimes": [
+                                    "video/mp4"
+                                ],
+                                "minduration": 15,
+                                "maxduration": 30,
+                                "protocols": [
+                                    2,
+                                    3,
+                                    5,
+                                    6,
+                                    7,
+                                    8
+                                ],
+                                "w": 940,
+                                "h": 560
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "v-2",
+                                    "impid": "multi-format-test-imp-id",
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "cat": [
+                                        "IAB9-1"
+                                    ],
+                                    "ext": {
+                                        "ix": {},
+                                        "prebid": {
+                                            "type": "video"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "v-2",
+                        "impid": "multi-format-test-imp-id",
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "cat": [
+                            "IAB9-1"
+                        ],
+                        "ext": {
+                            "ix": {},
+                            "prebid": {
+                                "type": "video"
+                            }
+                        }
+                    },
+                    "type": "video"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/exemplary/multi-format-with-mtype.json b/adapters/ix/ixtest/exemplary/multi-format-with-mtype.json
new file mode 100644
index 00000000..ca8e8217
--- /dev/null
+++ b/adapters/ix/ixtest/exemplary/multi-format-with-mtype.json
@@ -0,0 +1,150 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "multi-format-test-imp-id",
+                "banner": {
+                    "w": 300,
+                    "h": 250
+                },
+                "video": {
+                    "mimes": [
+                        "video/mp4"
+                    ],
+                    "minduration": 15,
+                    "maxduration": 30,
+                    "protocols": [
+                        2,
+                        3,
+                        5,
+                        6,
+                        7,
+                        8
+                    ],
+                    "w": 940,
+                    "h": 560
+                },
+                "ext": {
+                    "bidder": {
+                        "siteId": "569749"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://host/endpoint",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "multi-format-test-imp-id",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ],
+                                "w": 300,
+                                "h": 250
+                            },
+                            "video": {
+                                "mimes": [
+                                    "video/mp4"
+                                ],
+                                "minduration": 15,
+                                "maxduration": 30,
+                                "protocols": [
+                                    2,
+                                    3,
+                                    5,
+                                    6,
+                                    7,
+                                    8
+                                ],
+                                "w": 940,
+                                "h": 560
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "siteId": "569749"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "958",
+                            "bid": [
+                                {
+                                    "id": "v-2",
+                                    "impid": "multi-format-test-imp-id",
+                                    "mtype": 2,
+                                    "price": 0.5,
+                                    "adid": "29681110",
+                                    "adm": "some-test-ad",
+                                    "adomain": [
+                                        "https://advertiser.example.com"
+                                    ],
+                                    "cid": "958",
+                                    "crid": "29681110",
+                                    "h": 250,
+                                    "w": 300,
+                                    "cat": [
+                                        "IAB9-1"
+                                    ],
+                                    "ext": {
+                                        "ix": {}
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "bidid": "5778926625248726496",
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "v-2",
+                        "impid": "multi-format-test-imp-id",
+                        "mtype": 2,
+                        "price": 0.5,
+                        "adm": "some-test-ad",
+                        "adid": "29681110",
+                        "adomain": [
+                            "https://advertiser.example.com"
+                        ],
+                        "cid": "958",
+                        "crid": "29681110",
+                        "w": 300,
+                        "h": 250,
+                        "cat": [
+                            "IAB9-1"
+                        ],
+                        "ext": {
+                            "ix": {}
+                        }
+                    },
+                    "type": "video"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/ix/ixtest/supplemental/bad-imp-id.json b/adapters/ix/ixtest/supplemental/bad-imp-id.json
index 1ca053b6..5ca8daf2 100644
--- a/adapters/ix/ixtest/supplemental/bad-imp-id.json
+++ b/adapters/ix/ixtest/supplemental/bad-imp-id.json
@@ -82,33 +82,6 @@
             }
         }
     ],
-    "expectedBidResponses": [
-        {
-            "currency": "USD",
-            "bids": [
-                {
-                    "bid": {
-                        "id": "7706636740145184841",
-                        "impid": "bad-imp-id",
-                        "price": 0.5,
-                        "adm": "some-test-ad",
-                        "adid": "29681110",
-                        "adomain": [
-                            "https://advertiser.example.com"
-                        ],
-                        "cid": "958",
-                        "crid": "29681110",
-                        "w": 300,
-                        "h": 250,
-                        "ext": {
-                            "ix": {}
-                        }
-                    },
-                    "type": ""
-                }
-            ]
-        }
-    ],
     "expectedMakeBidsErrors": [
         {
             "value": "unmatched impression id: bad-imp-id",
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index f0e9d245..d9c0710c 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -917,6 +917,10 @@ func TestParseImpInfoSingleImpression(t *testing.T) {
 			Imp:          json.RawMessage(`{"id": "some-static-imp","video":{"mimes":["video/mp4"]},"ext": {"appnexus": {"placementId": "abc","position": "below"}}}`),
 			ImpExtPrebid: openrtb_ext.ExtImpPrebid{},
 		},
+		{
+			Imp:          json.RawMessage(`{"id":"my-imp-id", "video":{"h":300, "w":200}, "ext":{"prebid":{"storedrequest": {"id": "6"}}}}`),
+			ImpExtPrebid: openrtb_ext.ExtImpPrebid{StoredRequest: &openrtb_ext.ExtStoredRequest{ID: "6"}},
+		},
 	}
 
 	for i, requestData := range testStoredRequests {
@@ -1010,7 +1014,7 @@ func TestStoredRequests(t *testing.T) {
 		empty_fetcher.EmptyFetcher{},
 	}
 
-	testStoreVideoAttr := []bool{true, true, false, false}
+	testStoreVideoAttr := []bool{true, true, false, false, false}
 
 	for i, requestData := range testStoredRequests {
 		impInfo, errs := parseImpInfo([]byte(requestData))
@@ -1026,7 +1030,6 @@ func TestStoredRequests(t *testing.T) {
 			}
 		}
 		expectJson := json.RawMessage(testFinalRequests[i])
-
 		assert.JSONEq(t, string(expectJson), string(newRequest), "Incorrect result request %d", i)
 
 		expectedImp := testStoredImpIds[i]
@@ -3099,7 +3102,7 @@ func TestParseRequestParseImpInfoError(t *testing.T) {
 
 	req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody))
 
-	resReq, impExtInfoMap, _, _, errL := deps.parseRequest(req)
+	resReq, impExtInfoMap, _, _, _, errL := deps.parseRequest(req)
 
 	assert.Nil(t, resReq, "Result request should be nil due to incorrect imp")
 	assert.Nil(t, impExtInfoMap, "Impression info map should be nil due to incorrect imp")
@@ -3672,7 +3675,7 @@ func TestParseRequestMergeBidderParams(t *testing.T) {
 
 			req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(test.givenRequestBody))
 
-			resReq, _, _, _, errL := deps.parseRequest(req)
+			resReq, _, _, _, _, errL := deps.parseRequest(req)
 
 			var expIExt, iExt map[string]interface{}
 			err := json.Unmarshal(test.expectedImpExt, &expIExt)
@@ -3772,7 +3775,7 @@ func TestParseRequestStoredResponses(t *testing.T) {
 
 			req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(test.givenRequestBody))
 
-			_, _, storedResponses, _, errL := deps.parseRequest(req)
+			_, _, storedResponses, _, _, errL := deps.parseRequest(req)
 
 			if test.expectedErrorCount == 0 {
 				assert.Equal(t, test.expectedStoredResponses, storedResponses, "stored responses should match")
@@ -3858,7 +3861,7 @@ func TestParseRequestStoredBidResponses(t *testing.T) {
 			}
 
 			req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(test.givenRequestBody))
-			_, _, _, storedBidResponses, errL := deps.parseRequest(req)
+			_, _, _, storedBidResponses, _, errL := deps.parseRequest(req)
 
 			if test.expectedErrorCount == 0 {
 				assert.Equal(t, test.expectedStoredBidResponses, storedBidResponses, "stored responses should match")
diff --git a/endpoints/openrtb2/test_utils.go b/endpoints/openrtb2/test_utils.go
index 82fd78fa..38497581 100644
--- a/endpoints/openrtb2/test_utils.go
+++ b/endpoints/openrtb2/test_utils.go
@@ -119,6 +119,17 @@ var testStoredRequestData = map[string]json.RawMessage{
 		}`),
 	// Valid JSON
 	"4": json.RawMessage(`{"id": "ThisID", "cur": ["USD"]}`),
+
+	// Stored Request with Root Ext Passthrough
+	"5": json.RawMessage(`{
+		"ext": {
+			"prebid": {
+				"passthrough": {
+					"root_ext_passthrough": 20
+				}
+			}
+		}
+	}`),
 }
 
 // Stored Imp Requests
@@ -197,6 +208,17 @@ var testStoredImpData = map[string]json.RawMessage{
 				}
 			}
 		}`),
+	// Stored Imp with Passthrough
+	"6": json.RawMessage(`{
+		"id": "my-imp-id",
+		"ext": {
+			"prebid": {
+				"passthrough": {
+					"imp_passthrough": 30
+				}
+			}
+		}
+	}`),
 }
 
 // Incoming requests with stored request IDs
@@ -323,6 +345,32 @@ var testStoredRequests = []string{
 			}
 		}
 	}`,
+	`{
+		"id": "ThisID",
+		"imp": [
+			{
+				"id": "my-imp-id",
+				"video":{
+					"h":300,
+					"w":200
+				},
+				"ext": {
+					"prebid": {
+						"storedrequest": {
+							"id": "6"
+						}
+					}
+				}
+			}
+		],
+		"ext": {
+			"prebid": {
+				"storedrequest": {
+					"id": "5"
+				}
+			}
+		}
+	}`,
 }
 
 // The expected requests after stored request processing
@@ -438,8 +486,7 @@ var testFinalRequests = []string{
   		  }
   		],
   		"tmax": 500
-	}
-`,
+	}`,
 	`{
 	"id": "ThisID",
 	"imp": [
@@ -489,11 +536,43 @@ var testFinalRequests = []string{
 			}
 		}
 	}
+}`,
+	`{
+	"id": "ThisID",
+	"imp": [
+		{
+			"ext":{
+			   "prebid":{
+				  "passthrough":{
+					 "imp_passthrough":30
+				  },
+				  "storedrequest":{
+					 "id":"6"
+				  }
+			   }
+			},
+			"id":"my-imp-id",
+			"video":{
+			   "h":300,
+			   "w":200
+			}
+		 }
+	],
+	"ext":{
+		"prebid":{
+		   "passthrough":{
+			  "root_ext_passthrough":20
+		   },
+		   "storedrequest":{
+			  "id":"5"
+		   }
+		}
+	 }
 }`,
 }
 
 var testStoredImpIds = []string{
-	"adUnit1", "adUnit2", "adUnit1", "some-static-imp",
+	"adUnit1", "adUnit2", "adUnit1", "some-static-imp", "my-imp-id",
 }
 
 var testStoredImps = []string{
@@ -545,6 +624,16 @@ var testStoredImps = []string{
 			}
 		}`,
 	``,
+	`{
+		"id": "my-imp-id",
+		"ext": {
+			"prebid": {
+				"passthrough": {
+					"imp_passthrough": 30
+				}
+			}
+		}
+	}`,
 }
 
 var testBidRequests = []string{
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index c6fc1626..6bea9ce2 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -1423,17 +1423,18 @@ func TestRequestBidsStoredBidResponses(t *testing.T) {
 	defer server.Close()
 
 	bidRespId1 := json.RawMessage(`{"id": "resp_id1", "seatbid": [{"bid": [{"id": "bid_id1"}], "seat": "testBidder1"}], "bidid": "123", "cur": "USD"}`)
-	bidRespId2 := json.RawMessage(`{"id": "resp_id2", "seatbid": [{"bid": [{"id": "bid_id2_1"},{"id": "bid_id2_2"}], "seat": "testBidder2"}], "bidid": "124", "cur": "USD"}`)
+	bidRespId2 := json.RawMessage(`{"id": "resp_id2", "seatbid": [{"bid": [{"id": "bid_id2_1", "impid": "bid1impid1"},{"id": "bid_id2_2", "impid": "bid2impid2"}], "seat": "testBidder2"}], "bidid": "124", "cur": "USD"}`)
 
 	testCases := []struct {
 		description           string
 		mockBidderRequest     *openrtb2.BidRequest
 		bidderStoredResponses map[string]json.RawMessage
+		impReplaceImpId       map[string]bool
 		expectedBidIds        []string
 		expectedImpIds        []string
 	}{
 		{
-			description: "Single imp with stored bid response",
+			description: "Single imp with stored bid response, replace impid is true",
 			mockBidderRequest: &openrtb2.BidRequest{
 				Imp: nil,
 				App: &openrtb2.App{},
@@ -1441,11 +1442,14 @@ func TestRequestBidsStoredBidResponses(t *testing.T) {
 			bidderStoredResponses: map[string]json.RawMessage{
 				"bidResponseId1": bidRespId1,
 			},
+			impReplaceImpId: map[string]bool{
+				"bidResponseId1": true,
+			},
 			expectedBidIds: []string{"bid_id1"},
 			expectedImpIds: []string{"bidResponseId1"},
 		},
 		{
-			description: "Single imp with multiple stored bid responses",
+			description: "Single imp with multiple stored bid responses, replace impid is true",
 			mockBidderRequest: &openrtb2.BidRequest{
 				Imp: nil,
 				App: &openrtb2.App{},
@@ -1453,9 +1457,44 @@ func TestRequestBidsStoredBidResponses(t *testing.T) {
 			bidderStoredResponses: map[string]json.RawMessage{
 				"bidResponseId2": bidRespId2,
 			},
+			impReplaceImpId: map[string]bool{
+				"bidResponseId2": true,
+			},
 			expectedBidIds: []string{"bid_id2_1", "bid_id2_2"},
 			expectedImpIds: []string{"bidResponseId2", "bidResponseId2"},
 		},
+		{
+			description: "Single imp with multiple stored bid responses, replace impid is false",
+			mockBidderRequest: &openrtb2.BidRequest{
+				Imp: nil,
+				App: &openrtb2.App{},
+			},
+			bidderStoredResponses: map[string]json.RawMessage{
+				"bidResponseId2": bidRespId2,
+			},
+			impReplaceImpId: map[string]bool{
+				"bidResponseId2": false,
+			},
+			expectedBidIds: []string{"bid_id2_1", "bid_id2_2"},
+			expectedImpIds: []string{"bid1impid1", "bid2impid2"},
+		},
+		{
+			description: "Two imp with multiple stored bid responses, replace impid is true and false",
+			mockBidderRequest: &openrtb2.BidRequest{
+				Imp: nil,
+				App: &openrtb2.App{},
+			},
+			bidderStoredResponses: map[string]json.RawMessage{
+				"bidResponseId1": bidRespId1,
+				"bidResponseId2": bidRespId2,
+			},
+			impReplaceImpId: map[string]bool{
+				"bidResponseId1": true,
+				"bidResponseId2": false,
+			},
+			expectedBidIds: []string{"bid_id2_1", "bid_id2_2", "bid_id1"},
+			expectedImpIds: []string{"bid1impid1", "bid2impid2", "bidResponseId1"},
+		},
 	}
 
 	for _, tc := range testCases {
@@ -1468,6 +1507,7 @@ func TestRequestBidsStoredBidResponses(t *testing.T) {
 			BidRequest:            tc.mockBidderRequest,
 			BidderName:            openrtb_ext.BidderAppnexus,
 			BidderStoredResponses: tc.bidderStoredResponses,
+			ImpReplaceImpId:       tc.impReplaceImpId,
 		}
 		bidAdjustments := map[string]float64{string(openrtb_ext.BidderAppnexus): 1.0}
 		seatBids, _ := bidder.requestBid(
@@ -1487,9 +1527,9 @@ func TestRequestBidsStoredBidResponses(t *testing.T) {
 		assert.Len(t, seatBids, 1)
 
 		assert.Len(t, seatBids[0].bids, len(tc.expectedBidIds), "Incorrect bids number for test case ", tc.description)
-		for index, bid := range seatBids[0].bids {
-			assert.Equal(t, tc.expectedBidIds[index], bid.bid.ID, tc.description)
-			assert.Equal(t, tc.expectedImpIds[index], bid.bid.ImpID, tc.description)
+		for _, bid := range seatBids[0].bids {
+			assert.Contains(t, tc.expectedBidIds, bid.bid.ID, tc.description)
+			assert.Contains(t, tc.expectedImpIds, bid.bid.ImpID, tc.description)
 		}
 	}
 
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 9c22add4..6c649d30 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -1818,9 +1818,10 @@ func TestBidResponseImpExtInfo(t *testing.T) {
 	impExtInfo := make(map[string]ImpExtInfo, 1)
 	impExtInfo["some-impression-id"] = ImpExtInfo{
 		true,
-		[]byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`)}
+		[]byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`),
+		json.RawMessage(`{"imp_passthrough_val": 1}`)}
 
-	expectedBidResponseExt := `{"origbidcpm":0,"prebid":{"type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]}}`
+	expectedBidResponseExt := `{"origbidcpm":0,"prebid":{"type":"video","passthrough":{"imp_passthrough_val":1}},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]}}`
 
 	actualBidResp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, nil, nil, nil, true, impExtInfo, errList)
 	assert.NoError(t, err, fmt.Sprintf("imp ext info was not passed through correctly: %s", err))
@@ -2216,6 +2217,16 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 		debugLog.Regexp = regexp.MustCompile(`[<>]`)
 	}
 
+	// Passthrough JSON Testing
+	impExtInfoMap := make(map[string]ImpExtInfo)
+	if spec.PassthroughFlag {
+		impPassthrough, impID, err := getInfoFromImp(&openrtb_ext.RequestWrapper{BidRequest: &spec.IncomingRequest.OrtbRequest})
+		if err != nil {
+			t.Errorf("%s: Exchange returned an unexpected error. Got %s", filename, err.Error())
+		}
+		impExtInfoMap[impID] = ImpExtInfo{Passthrough: impPassthrough}
+	}
+
 	auctionRequest := AuctionRequest{
 		BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &spec.IncomingRequest.OrtbRequest},
 		Account: config.Account{
@@ -2223,8 +2234,10 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 			EventsEnabled: spec.EventsEnabled,
 			DebugAllow:    true,
 		},
-		UserSyncs: mockIdFetcher(spec.IncomingRequest.Usersyncs),
+		UserSyncs:     mockIdFetcher(spec.IncomingRequest.Usersyncs),
+		ImpExtInfoMap: impExtInfoMap,
 	}
+
 	if spec.StartTime > 0 {
 		auctionRequest.StartTime = time.Unix(0, spec.StartTime*1e+6)
 	}
@@ -2273,6 +2286,22 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 		//compare debug info
 		assert.JSONEq(t, string(bid.Ext), string(spec.Response.Ext), "Debug info modified")
 	}
+
+	if spec.PassthroughFlag {
+		var actualBidRespExt openrtb_ext.ExtBidResponse
+		var expectedBidRespExt openrtb_ext.ExtBidResponse
+		if bid.Ext != nil {
+			if err := json.Unmarshal(bid.Ext, &actualBidRespExt); err != nil {
+				assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err))
+			}
+		}
+		if spec.Response.Ext != nil {
+			if err := json.Unmarshal(spec.Response.Ext, &expectedBidRespExt); err != nil {
+				assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err))
+			}
+		}
+		assert.Equalf(t, expectedBidRespExt.Prebid, actualBidRespExt.Prebid, "Expected bid response extension is incorrect")
+	}
 }
 
 func findBiddersInAuction(t *testing.T, context string, req *openrtb2.BidRequest) []string {
@@ -3607,18 +3636,18 @@ func TestMakeBidExtJSON(t *testing.T) {
 		{
 			description:        "Valid extension, non empty extBidPrebid, valid imp ext info, meta from adapter",
 			ext:                json.RawMessage(`{"video":{"h":100}}`),
-			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video"), Meta: &openrtb_ext.ExtBidPrebidMeta{BrandName: "foo"}},
-			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`)}},
+			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video"), Meta: &openrtb_ext.ExtBidPrebidMeta{BrandName: "foo"}, Passthrough: nil},
+			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}},
 			origbidcpm:         10.0000,
 			origbidcur:         "USD",
-			expectedBidExt:     `{"prebid":{"meta": {"brandName": "foo"}, "type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`,
+			expectedBidExt:     `{"prebid":{"meta": {"brandName": "foo"}, "passthrough":{"imp_passthrough_val":1}, "type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`,
 			expectedErrMessage: "",
 		},
 		{
-			description:        "Valid extension, non empty extBidPrebid, valid imp ext info, meta from response",
+			description:        "Valid extension, non empty extBidPrebid, valid imp ext info, meta from response, imp passthrough is nil",
 			ext:                json.RawMessage(`{"video":{"h":100},"prebid":{"meta": {"brandName": "foo"}}}`),
 			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video")},
-			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`)}},
+			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), nil}},
 			origbidcpm:         10.0000,
 			origbidcur:         "USD",
 			expectedBidExt:     `{"prebid":{"meta": {"brandName": "foo"}, "type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`,
@@ -3628,16 +3657,16 @@ func TestMakeBidExtJSON(t *testing.T) {
 			description:        "Empty extension, non empty extBidPrebid and valid imp ext info",
 			ext:                nil,
 			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video")},
-			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`)}},
+			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}},
 			origbidcpm:         0,
-			expectedBidExt:     `{"origbidcpm": 0,"prebid":{"type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]}}`,
+			expectedBidExt:     `{"origbidcpm": 0,"prebid":{"passthrough":{"imp_passthrough_val":1}, "type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]}}`,
 			expectedErrMessage: "",
 		},
 		{
 			description:        "Valid extension, non empty extBidPrebid and imp ext info not found",
 			ext:                json.RawMessage(`{"video":{"h":100}}`),
 			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video")},
-			impExtInfo:         map[string]ImpExtInfo{"another_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`)}},
+			impExtInfo:         map[string]ImpExtInfo{"another_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}},
 			origbidcpm:         10.0000,
 			origbidcur:         "USD",
 			expectedBidExt:     `{"prebid":{"type":"video"},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`,
@@ -3649,8 +3678,8 @@ func TestMakeBidExtJSON(t *testing.T) {
 			extBidPrebid:       openrtb_ext.ExtBidPrebid{},
 			origbidcpm:         10.0000,
 			origbidcur:         "USD",
-			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`)}},
-			expectedBidExt:     `{"prebid":{"type":""},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`,
+			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}},
+			expectedBidExt:     `{"prebid":{"passthrough":{"imp_passthrough_val":1}, "type":""},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`,
 			expectedErrMessage: "",
 		},
 		{
@@ -3669,8 +3698,8 @@ func TestMakeBidExtJSON(t *testing.T) {
 			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video")},
 			origbidcpm:         10.0000,
 			origbidcur:         "USD",
-			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"banner":{"h":480}}`)}},
-			expectedBidExt:     `{"prebid":{"type":"video"},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`,
+			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"banner":{"h":480}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}},
+			expectedBidExt:     `{"prebid":{"passthrough":{"imp_passthrough_val":1}, "type":"video"},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`,
 			expectedErrMessage: "",
 		},
 		{
@@ -3679,8 +3708,8 @@ func TestMakeBidExtJSON(t *testing.T) {
 			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video")},
 			origbidcpm:         10.0000,
 			origbidcur:         "USD",
-			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"banner":{"h":480}}`)}},
-			expectedBidExt:     `{"prebid":{"type":"video"}, "origbidcpm": 10, "origbidcur": "USD"}`,
+			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"banner":{"h":480}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}},
+			expectedBidExt:     `{"prebid":{"passthrough":{"imp_passthrough_val":1}, "type":"video"}, "origbidcpm": 10, "origbidcur": "USD"}`,
 			expectedErrMessage: "",
 		},
 		{
@@ -3689,8 +3718,8 @@ func TestMakeBidExtJSON(t *testing.T) {
 			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video")},
 			origbidcpm:         10.0000,
 			origbidcur:         "USD",
-			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`)}},
-			expectedBidExt:     `{"prebid":{"type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]}, "origbidcpm": 10, "origbidcur": "USD"}`,
+			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}},
+			expectedBidExt:     `{"prebid":{"passthrough":{"imp_passthrough_val":1}, "type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]}, "origbidcpm": 10, "origbidcur": "USD"}`,
 			expectedErrMessage: "",
 		},
 		{
@@ -3758,7 +3787,7 @@ func TestMakeBidExtJSON(t *testing.T) {
 			description:        "Invalid extension, valid extBidPrebid and valid imp ext info",
 			ext:                json.RawMessage(`{invalid json}`),
 			extBidPrebid:       openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video")},
-			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`)}},
+			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`"prebid": {"passthrough": {"imp_passthrough_val": some_val}}"`)}},
 			expectedBidExt:     ``,
 			expectedErrMessage: "invalid character",
 		},
@@ -3766,7 +3795,7 @@ func TestMakeBidExtJSON(t *testing.T) {
 			description:        "Valid extension, empty extBidPrebid and invalid imp ext info",
 			ext:                json.RawMessage(`{"video":{"h":100}}`),
 			extBidPrebid:       openrtb_ext.ExtBidPrebid{},
-			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{!}}`)}},
+			impExtInfo:         map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{!}}`), nil}},
 			expectedBidExt:     ``,
 			expectedErrMessage: "invalid character",
 		},
@@ -4211,6 +4240,7 @@ type exchangeSpec struct {
 	StartTime         int64                  `json:"start_time_ms,omitempty"`
 	BidIDGenerator    *mockBidIDGenerator    `json:"bidIDGenerator,omitempty"`
 	RequestType       *metrics.RequestType   `json:"requestType,omitempty"`
+	PassthroughFlag   bool                   `json:"passthrough_flag,omitempty"`
 	HostSChainFlag    bool                   `json:"host_schain_flag,omitempty"`
 }
 
@@ -4375,7 +4405,6 @@ func diffOrtbResponses(t *testing.T, description string, expected *openrtb2.BidR
 	if err != nil {
 		t.Fatalf("%s failed to marshal expected BidResponse into JSON. %v", description, err)
 	}
-
 	assert.JSONEq(t, string(expectedJSON), string(actualJSON), description)
 }
 
@@ -4494,3 +4523,22 @@ func (m *mockBidder) MakeBids(internalRequest *openrtb2.BidRequest, externalRequ
 	args := m.Called(internalRequest, externalRequest, response)
 	return args.Get(0).(*adapters.BidderResponse), args.Get(1).([]error)
 }
+
+func getInfoFromImp(req *openrtb_ext.RequestWrapper) (json.RawMessage, string, error) {
+	bidRequest := req.BidRequest
+	imp := bidRequest.Imp[0]
+	impID := imp.ID
+
+	var bidderExts map[string]json.RawMessage
+	if err := json.Unmarshal(imp.Ext, &bidderExts); err != nil {
+		return nil, "", err
+	}
+
+	var extPrebid openrtb_ext.ExtImpPrebid
+	if bidderExts[openrtb_ext.PrebidExtKey] != nil {
+		if err := json.Unmarshal(bidderExts[openrtb_ext.PrebidExtKey], &extPrebid); err != nil {
+			return nil, "", err
+		}
+	}
+	return extPrebid.Passthrough, impID, nil
+}
diff --git a/exchange/exchangetest/passthrough_imp_only.json b/exchange/exchangetest/passthrough_imp_only.json
new file mode 100644
index 00000000..84e1a72b
--- /dev/null
+++ b/exchange/exchangetest/passthrough_imp_only.json
@@ -0,0 +1,153 @@
+{
+    "passthrough_flag": true,
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [{
+                "id": "my-imp-id-2",
+                "video": {
+                    "mimes": ["video/mp4"]
+                },
+                "ext": {
+                    "appnexus": {
+                        "placementId": 1
+                    },
+                    "prebid": {
+                        "passthrough": {
+                            "imp_passthrough_val": 20
+                        }
+                    }
+                }
+            },
+            {
+                "id": "my-imp-id",
+                "video": {
+                    "mimes": ["video/mp4"]
+                },
+                "ext": {
+                    "appnexus": {
+                        "placementId": 1
+                    }
+                }
+            }]
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [{
+                        "id": "my-imp-id-2",
+                        "video": {
+                            "mimes": ["video/mp4"]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": 1
+                            },
+                            "prebid": {
+                                "passthrough": {
+                                    "imp_passthrough_val": 20
+                                }
+                            }
+                        }
+                    },
+                    {
+                        "id": "my-imp-id",
+                        "video": {
+                            "mimes": ["video/mp4"]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": 1
+                            }
+                        }
+                    }]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [{
+                    "pbsBids": [{
+                        "ortbBid": {
+                            "id": "apn-bid",
+                            "impid": "my-imp-id-2",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 250,
+                            "crid": "creative-1",
+                            "ext": {
+                                "someField": "someValue",
+                                "origbidcpm": 0.3
+                            }
+                        },
+                        "bidType": "video"
+                    },
+                    {
+                        "ortbBid": {
+                            "id": "apn-bid",
+                            "impid": "my-imp-id",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 250,
+                            "crid": "creative-1",
+                            "ext": {
+                                "someField": "someValue",
+                                "origbidcpm": 0.3
+                            }
+                        },
+                        "bidType": "video"
+                    }],
+                    "seat": "appnexus"
+                }]
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [{
+                "seat": "appnexus",
+                "bid": [{
+                    "id": "apn-bid",
+                    "impid": "my-imp-id-2",
+                    "price": 0.3,
+                    "w": 200,
+                    "h": 250,
+                    "crid": "creative-1",
+                    "ext": {
+                        "someField": "someValue",
+                        "origbidcpm": 0.3,
+                        "prebid": {
+                            "type": "video",
+                            "passthrough": {
+                                "imp_passthrough_val": 20
+                            }
+                        }
+                    }
+                },
+                {
+                    "id": "apn-bid",
+                    "impid": "my-imp-id",
+                    "price": 0.3,
+                    "w": 200,
+                    "h": 250,
+                    "crid": "creative-1",
+                    "ext": {
+                        "someField": "someValue",
+                        "origbidcpm": 0.3,
+                        "prebid": {
+                            "type": "video"
+                        }
+                    }
+                }]
+            }]
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/passthrough_root_and_imp.json b/exchange/exchangetest/passthrough_root_and_imp.json
new file mode 100644
index 00000000..d71bc5f2
--- /dev/null
+++ b/exchange/exchangetest/passthrough_root_and_imp.json
@@ -0,0 +1,116 @@
+{
+    "passthrough_flag": true,
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [{
+                "id": "my-imp-id",
+                "video": {
+                    "mimes": ["video/mp4"]
+                },
+                "ext": {
+                    "appnexus": {
+                        "placementId": 1
+                    },
+                    "prebid": {
+                        "passthrough": {
+                            "imp_passthrough_val": 20
+                        }
+                    }
+                }
+            }],
+            "ext": {
+                "prebid": {
+                    "passthrough": {"bid_response_passthrough":20}
+                }
+            }
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [{
+                        "id": "my-imp-id",
+                        "video": {
+                            "mimes": ["video/mp4"]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": 1
+                            },
+                            "prebid": {
+                                "passthrough": {
+                                    "imp_passthrough_val": 20
+                                }
+                            }
+                        }
+                    }],
+                    "ext": {
+                        "prebid": {
+                            "passthrough": {"bid_response_passthrough":20}
+                        }
+                    }
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [{
+                    "pbsBids": [{
+                        "ortbBid": {
+                            "id": "apn-bid",
+                            "impid": "my-imp-id",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 250,
+                            "crid": "creative-1",
+                            "ext": {
+                                "someField": "someValue",
+                                "origbidcpm": 0.3
+                            }
+                        },
+                        "bidType": "video"
+                    }],
+                    "seat": "appnexus"
+                }]
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [{
+                "seat": "appnexus",
+                "bid": [{
+                    "id": "apn-bid",
+                    "impid": "my-imp-id",
+                    "price": 0.3,
+                    "w": 200,
+                    "h": 250,
+                    "crid": "creative-1",
+                    "ext": {
+                        "someField": "someValue",
+                        "origbidcpm": 0.3,
+                        "prebid": {
+                            "type": "video",
+                            "passthrough": {
+                                "imp_passthrough_val": 20
+                            }
+                        }
+                    }
+                }]
+            }]
+        },
+        "ext": {
+            "prebid": {
+                "passthrough": {"bid_response_passthrough":20}
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/passthrough_root_only.json b/exchange/exchangetest/passthrough_root_only.json
new file mode 100644
index 00000000..6b759e7a
--- /dev/null
+++ b/exchange/exchangetest/passthrough_root_only.json
@@ -0,0 +1,103 @@
+{
+    "passthrough_flag": true,
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [{
+                "id": "my-imp-id",
+                "video": {
+                    "mimes": ["video/mp4"]
+                },
+                "ext": {
+                    "appnexus": {
+                        "placementId": 1
+                    }
+                }
+            }],
+            "ext": {
+                "prebid": {
+                    "passthrough": {"bid_response_passthrough":20}
+                }
+            }
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [{
+                        "id": "my-imp-id",
+                        "video": {
+                            "mimes": ["video/mp4"]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": 1
+                            }
+                        }
+                    }],
+                    "ext": {
+                        "prebid": {
+                            "passthrough": {"bid_response_passthrough":20}
+                        }
+                    }
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [{
+                    "pbsBids": [{
+                        "ortbBid": {
+                            "id": "apn-bid",
+                            "impid": "my-imp-id",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 250,
+                            "crid": "creative-1",
+                            "ext": {
+                                "someField": "someValue",
+                                "origbidcpm": 0.3
+                            }
+                        },
+                        "bidType": "video"
+                    }],
+                    "seat": "appnexus"
+                }]
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [{
+                "seat": "appnexus",
+                "bid": [{
+                    "id": "apn-bid",
+                    "impid": "my-imp-id",
+                    "price": 0.3,
+                    "w": 200,
+                    "h": 250,
+                    "crid": "creative-1",
+                    "ext": {
+                        "prebid": {
+                            "type": "video"
+                        },
+                        "someField": "someValue",
+                        "origbidcpm": 0.3
+                    }
+                }]
+            }]
+        },
+        "ext": {
+            "prebid": {
+                "passthrough": {"bid_response_passthrough":20}
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/openrtb_ext/request_wrapper_test.go b/openrtb_ext/request_wrapper_test.go
index f5cdb090..337de0a3 100644
--- a/openrtb_ext/request_wrapper_test.go
+++ b/openrtb_ext/request_wrapper_test.go
@@ -43,3 +43,477 @@ func TestUserExt(t *testing.T) {
 
 	assert.Equal(t, false, userExt.Dirty(), "UserExt should not be dirty after marshalling")
 }
+
+func TestRebuildUserExt(t *testing.T) {
+	strA := "a"
+	strB := "b"
+
+	testCases := []struct {
+		description           string
+		request               openrtb2.BidRequest
+		requestUserExtWrapper UserExt
+		expectedRequest       openrtb2.BidRequest
+	}{
+		{
+			description:           "Nil - Not Dirty",
+			request:               openrtb2.BidRequest{},
+			requestUserExtWrapper: UserExt{},
+			expectedRequest:       openrtb2.BidRequest{},
+		},
+		{
+			description:           "Nil - Dirty",
+			request:               openrtb2.BidRequest{},
+			requestUserExtWrapper: UserExt{consent: &strA, consentDirty: true},
+			expectedRequest:       openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}},
+		},
+		{
+			description:           "Nil - Dirty - No Change",
+			request:               openrtb2.BidRequest{},
+			requestUserExtWrapper: UserExt{consent: nil, consentDirty: true},
+			expectedRequest:       openrtb2.BidRequest{},
+		},
+		{
+			description:           "Empty - Not Dirty",
+			request:               openrtb2.BidRequest{User: &openrtb2.User{}},
+			requestUserExtWrapper: UserExt{},
+			expectedRequest:       openrtb2.BidRequest{User: &openrtb2.User{}},
+		},
+		{
+			description:           "Empty - Dirty",
+			request:               openrtb2.BidRequest{User: &openrtb2.User{}},
+			requestUserExtWrapper: UserExt{consent: &strB, consentDirty: true},
+			expectedRequest:       openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"b"}`)}},
+		},
+		{
+			description:           "Empty - Dirty - No Change",
+			request:               openrtb2.BidRequest{User: &openrtb2.User{}},
+			requestUserExtWrapper: UserExt{consent: nil, consentDirty: true},
+			expectedRequest:       openrtb2.BidRequest{User: &openrtb2.User{}},
+		},
+		{
+			description:           "Populated - Not Dirty",
+			request:               openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}},
+			requestUserExtWrapper: UserExt{},
+			expectedRequest:       openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}},
+		},
+		{
+			description:           "Populated - Dirty",
+			request:               openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}},
+			requestUserExtWrapper: UserExt{consent: &strB, consentDirty: true},
+			expectedRequest:       openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"b"}`)}},
+		},
+		{
+			description:           "Populated - Dirty - No Change",
+			request:               openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}},
+			requestUserExtWrapper: UserExt{consent: &strA, consentDirty: true},
+			expectedRequest:       openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}},
+		},
+		{
+			description:           "Populated - Dirty - Cleared",
+			request:               openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"a"}`)}},
+			requestUserExtWrapper: UserExt{consent: nil, consentDirty: true},
+			expectedRequest:       openrtb2.BidRequest{User: &openrtb2.User{}},
+		},
+	}
+
+	for _, test := range testCases {
+		// create required filed in the test loop to keep test declaration easier to read
+		test.requestUserExtWrapper.ext = make(map[string]json.RawMessage)
+
+		w := RequestWrapper{BidRequest: &test.request, userExt: &test.requestUserExtWrapper}
+		w.RebuildRequest()
+		assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
+	}
+}
+
+func TestRebuildDeviceExt(t *testing.T) {
+	prebidContent1 := ExtDevicePrebid{Interstitial: &ExtDeviceInt{MinWidthPerc: 1}}
+	prebidContent2 := ExtDevicePrebid{Interstitial: &ExtDeviceInt{MinWidthPerc: 2}}
+
+	testCases := []struct {
+		description             string
+		request                 openrtb2.BidRequest
+		requestDeviceExtWrapper DeviceExt
+		expectedRequest         openrtb2.BidRequest
+	}{
+		{
+			description:             "Nil - Not Dirty",
+			request:                 openrtb2.BidRequest{},
+			requestDeviceExtWrapper: DeviceExt{},
+			expectedRequest:         openrtb2.BidRequest{},
+		},
+		{
+			description:             "Nil - Dirty",
+			request:                 openrtb2.BidRequest{},
+			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+		},
+		{
+			description:             "Nil - Dirty - No Change",
+			request:                 openrtb2.BidRequest{},
+			requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true},
+			expectedRequest:         openrtb2.BidRequest{},
+		},
+		{
+			description:             "Empty - Not Dirty",
+			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{}},
+			requestDeviceExtWrapper: DeviceExt{},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{}},
+		},
+		{
+			description:             "Empty - Dirty",
+			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{}},
+			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+		},
+		{
+			description:             "Empty - Dirty - No Change",
+			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{}},
+			requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{}},
+		},
+		{
+			description:             "Populated - Not Dirty",
+			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			requestDeviceExtWrapper: DeviceExt{},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+		},
+		{
+			description:             "Populated - Dirty",
+			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent2, prebidDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":2,"minheightperc":0}}}`)}},
+		},
+		{
+			description:             "Populated - Dirty - No Change",
+			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+		},
+		{
+			description:             "Populated - Dirty - Cleared",
+			request:                 openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}},
+			requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Device: &openrtb2.Device{}},
+		},
+	}
+
+	for _, test := range testCases {
+		// create required filed in the test loop to keep test declaration easier to read
+		test.requestDeviceExtWrapper.ext = make(map[string]json.RawMessage)
+
+		w := RequestWrapper{BidRequest: &test.request, deviceExt: &test.requestDeviceExtWrapper}
+		w.RebuildRequest()
+		assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
+	}
+}
+
+func TestRebuildRequestExt(t *testing.T) {
+	prebidContent1 := ExtRequestPrebid{Integration: "1"}
+	prebidContent2 := ExtRequestPrebid{Integration: "2"}
+
+	testCases := []struct {
+		description              string
+		request                  openrtb2.BidRequest
+		requestRequestExtWrapper RequestExt
+		expectedRequest          openrtb2.BidRequest
+	}{
+		{
+			description:              "Empty - Not Dirty",
+			request:                  openrtb2.BidRequest{},
+			requestRequestExtWrapper: RequestExt{},
+			expectedRequest:          openrtb2.BidRequest{},
+		},
+		{
+			description:              "Empty - Dirty",
+			request:                  openrtb2.BidRequest{},
+			requestRequestExtWrapper: RequestExt{prebid: &prebidContent1, prebidDirty: true},
+			expectedRequest:          openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)},
+		},
+		{
+			description:              "Empty - Dirty - No Change",
+			request:                  openrtb2.BidRequest{},
+			requestRequestExtWrapper: RequestExt{prebid: nil, prebidDirty: true},
+			expectedRequest:          openrtb2.BidRequest{},
+		},
+		{
+			description:              "Populated - Not Dirty",
+			request:                  openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)},
+			requestRequestExtWrapper: RequestExt{},
+			expectedRequest:          openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)},
+		},
+		{
+			description:              "Populated - Dirty",
+			request:                  openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)},
+			requestRequestExtWrapper: RequestExt{prebid: &prebidContent2, prebidDirty: true},
+			expectedRequest:          openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"2"}}`)},
+		},
+		{
+			description:              "Populated - Dirty - No Change",
+			request:                  openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)},
+			requestRequestExtWrapper: RequestExt{prebid: &prebidContent1, prebidDirty: true},
+			expectedRequest:          openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)},
+		},
+		{
+			description:              "Populated - Dirty - Cleared",
+			request:                  openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"integration":"1"}}`)},
+			requestRequestExtWrapper: RequestExt{prebid: nil, prebidDirty: true},
+			expectedRequest:          openrtb2.BidRequest{},
+		},
+	}
+
+	for _, test := range testCases {
+		// create required filed in the test loop to keep test declaration easier to read
+		test.requestRequestExtWrapper.ext = make(map[string]json.RawMessage)
+
+		w := RequestWrapper{BidRequest: &test.request, requestExt: &test.requestRequestExtWrapper}
+		w.RebuildRequest()
+		assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
+	}
+}
+
+func TestRebuildAppExt(t *testing.T) {
+	prebidContent1 := ExtAppPrebid{Source: "1"}
+	prebidContent2 := ExtAppPrebid{Source: "2"}
+
+	testCases := []struct {
+		description          string
+		request              openrtb2.BidRequest
+		requestAppExtWrapper AppExt
+		expectedRequest      openrtb2.BidRequest
+	}{
+		{
+			description:          "Nil - Not Dirty",
+			request:              openrtb2.BidRequest{},
+			requestAppExtWrapper: AppExt{},
+			expectedRequest:      openrtb2.BidRequest{},
+		},
+		{
+			description:          "Nil - Dirty",
+			request:              openrtb2.BidRequest{},
+			requestAppExtWrapper: AppExt{prebid: &prebidContent1, prebidDirty: true},
+			expectedRequest:      openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}},
+		},
+		{
+			description:          "Nil - Dirty - No Change",
+			request:              openrtb2.BidRequest{},
+			requestAppExtWrapper: AppExt{prebid: nil, prebidDirty: true},
+			expectedRequest:      openrtb2.BidRequest{},
+		},
+		{
+			description:          "Empty - Not Dirty",
+			request:              openrtb2.BidRequest{App: &openrtb2.App{}},
+			requestAppExtWrapper: AppExt{},
+			expectedRequest:      openrtb2.BidRequest{App: &openrtb2.App{}},
+		},
+		{
+			description:          "Empty - Dirty",
+			request:              openrtb2.BidRequest{App: &openrtb2.App{}},
+			requestAppExtWrapper: AppExt{prebid: &prebidContent1, prebidDirty: true},
+			expectedRequest:      openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}},
+		},
+		{
+			description:          "Empty - Dirty - No Change",
+			request:              openrtb2.BidRequest{App: &openrtb2.App{}},
+			requestAppExtWrapper: AppExt{prebid: nil, prebidDirty: true},
+			expectedRequest:      openrtb2.BidRequest{App: &openrtb2.App{}},
+		},
+		{
+			description:          "Populated - Not Dirty",
+			request:              openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}},
+			requestAppExtWrapper: AppExt{},
+			expectedRequest:      openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}},
+		},
+		{
+			description:          "Populated - Dirty",
+			request:              openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}},
+			requestAppExtWrapper: AppExt{prebid: &prebidContent2, prebidDirty: true},
+			expectedRequest:      openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"2"}}`)}},
+		},
+		{
+			description:          "Populated - Dirty - No Change",
+			request:              openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}},
+			requestAppExtWrapper: AppExt{prebid: &prebidContent1, prebidDirty: true},
+			expectedRequest:      openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}},
+		},
+		{
+			description:          "Populated - Dirty - Cleared",
+			request:              openrtb2.BidRequest{App: &openrtb2.App{Ext: json.RawMessage(`{"prebid":{"source":"1"}}`)}},
+			requestAppExtWrapper: AppExt{prebid: nil, prebidDirty: true},
+			expectedRequest:      openrtb2.BidRequest{App: &openrtb2.App{}},
+		},
+	}
+
+	for _, test := range testCases {
+		// create required filed in the test loop to keep test declaration easier to read
+		test.requestAppExtWrapper.ext = make(map[string]json.RawMessage)
+
+		w := RequestWrapper{BidRequest: &test.request, appExt: &test.requestAppExtWrapper}
+		w.RebuildRequest()
+		assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
+	}
+}
+
+func TestRebuildSiteExt(t *testing.T) {
+	int1 := int8(1)
+	int2 := int8(2)
+
+	testCases := []struct {
+		description           string
+		request               openrtb2.BidRequest
+		requestSiteExtWrapper SiteExt
+		expectedRequest       openrtb2.BidRequest
+	}{
+		{
+			description:           "Nil - Not Dirty",
+			request:               openrtb2.BidRequest{},
+			requestSiteExtWrapper: SiteExt{},
+			expectedRequest:       openrtb2.BidRequest{},
+		},
+		{
+			description:           "Nil - Dirty",
+			request:               openrtb2.BidRequest{},
+			requestSiteExtWrapper: SiteExt{amp: &int1, ampDirty: true},
+			expectedRequest:       openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}},
+		},
+		{
+			description:           "Nil - Dirty - No Change",
+			request:               openrtb2.BidRequest{},
+			requestSiteExtWrapper: SiteExt{amp: nil, ampDirty: true},
+			expectedRequest:       openrtb2.BidRequest{},
+		},
+		{
+			description:           "Empty - Not Dirty",
+			request:               openrtb2.BidRequest{Site: &openrtb2.Site{}},
+			requestSiteExtWrapper: SiteExt{},
+			expectedRequest:       openrtb2.BidRequest{Site: &openrtb2.Site{}},
+		},
+		{
+			description:           "Empty - Dirty",
+			request:               openrtb2.BidRequest{Site: &openrtb2.Site{}},
+			requestSiteExtWrapper: SiteExt{amp: &int1, ampDirty: true},
+			expectedRequest:       openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}},
+		},
+		{
+			description:           "Empty - Dirty - No Change",
+			request:               openrtb2.BidRequest{Site: &openrtb2.Site{}},
+			requestSiteExtWrapper: SiteExt{amp: nil, ampDirty: true},
+			expectedRequest:       openrtb2.BidRequest{Site: &openrtb2.Site{}},
+		},
+		{
+			description:           "Populated - Not Dirty",
+			request:               openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}},
+			requestSiteExtWrapper: SiteExt{},
+			expectedRequest:       openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}},
+		},
+		{
+			description:           "Populated - Dirty",
+			request:               openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}},
+			requestSiteExtWrapper: SiteExt{amp: &int2, ampDirty: true},
+			expectedRequest:       openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":2}`)}},
+		},
+		{
+			description:           "Populated - Dirty - No Change",
+			request:               openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}},
+			requestSiteExtWrapper: SiteExt{amp: &int1, ampDirty: true},
+			expectedRequest:       openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}},
+		},
+		{
+			description:           "Populated - Dirty - Cleared",
+			request:               openrtb2.BidRequest{Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}},
+			requestSiteExtWrapper: SiteExt{amp: nil, ampDirty: true},
+			expectedRequest:       openrtb2.BidRequest{Site: &openrtb2.Site{}},
+		},
+	}
+
+	for _, test := range testCases {
+		// create required filed in the test loop to keep test declaration easier to read
+		test.requestSiteExtWrapper.ext = make(map[string]json.RawMessage)
+
+		w := RequestWrapper{BidRequest: &test.request, siteExt: &test.requestSiteExtWrapper}
+		w.RebuildRequest()
+		assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
+	}
+}
+
+func TestRebuildSourceExt(t *testing.T) {
+	schainContent1 := openrtb2.SupplyChain{Ver: "1"}
+	schainContent2 := openrtb2.SupplyChain{Ver: "2"}
+
+	testCases := []struct {
+		description             string
+		request                 openrtb2.BidRequest
+		requestSourceExtWrapper SourceExt
+		expectedRequest         openrtb2.BidRequest
+	}{
+		{
+			description:             "Nil - Not Dirty",
+			request:                 openrtb2.BidRequest{},
+			requestSourceExtWrapper: SourceExt{},
+			expectedRequest:         openrtb2.BidRequest{},
+		},
+		{
+			description:             "Nil - Dirty",
+			request:                 openrtb2.BidRequest{},
+			requestSourceExtWrapper: SourceExt{schain: &schainContent1, schainDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}},
+		},
+		{
+			description:             "Nil - Dirty - No Change",
+			request:                 openrtb2.BidRequest{},
+			requestSourceExtWrapper: SourceExt{schain: nil, schainDirty: true},
+			expectedRequest:         openrtb2.BidRequest{},
+		},
+		{
+			description:             "Empty - Not Dirty",
+			request:                 openrtb2.BidRequest{Source: &openrtb2.Source{}},
+			requestSourceExtWrapper: SourceExt{},
+			expectedRequest:         openrtb2.BidRequest{Source: &openrtb2.Source{}},
+		},
+		{
+			description:             "Empty - Dirty",
+			request:                 openrtb2.BidRequest{Source: &openrtb2.Source{}},
+			requestSourceExtWrapper: SourceExt{schain: &schainContent1, schainDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}},
+		},
+		{
+			description:             "Empty - Dirty - No Change",
+			request:                 openrtb2.BidRequest{Source: &openrtb2.Source{}},
+			requestSourceExtWrapper: SourceExt{schain: nil, schainDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Source: &openrtb2.Source{}},
+		},
+		{
+			description:             "Populated - Not Dirty",
+			request:                 openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}},
+			requestSourceExtWrapper: SourceExt{},
+			expectedRequest:         openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}},
+		},
+		{
+			description:             "Populated - Dirty",
+			request:                 openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}},
+			requestSourceExtWrapper: SourceExt{schain: &schainContent2, schainDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"2"}}`)}},
+		},
+		{
+			description:             "Populated - Dirty - No Change",
+			request:                 openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}},
+			requestSourceExtWrapper: SourceExt{schain: &schainContent1, schainDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}},
+		},
+		{
+			description:             "Populated - Dirty - Cleared",
+			request:                 openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage(`{"schain":{"complete":0,"nodes":null,"ver":"1"}}`)}},
+			requestSourceExtWrapper: SourceExt{schain: nil, schainDirty: true},
+			expectedRequest:         openrtb2.BidRequest{Source: &openrtb2.Source{}},
+		},
+	}
+
+	for _, test := range testCases {
+		// create required filed in the test loop to keep test declaration easier to read
+		test.requestSourceExtWrapper.ext = make(map[string]json.RawMessage)
+
+		w := RequestWrapper{BidRequest: &test.request, sourceExt: &test.requestSourceExtWrapper}
+		w.RebuildRequest()
+		assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description)
+	}
+}
diff --git a/privacy/ccpa/policy_test.go b/privacy/ccpa/policy_test.go
index a7ede21e..8646ce85 100644
--- a/privacy/ccpa/policy_test.go
+++ b/privacy/ccpa/policy_test.go
@@ -298,7 +298,7 @@ func TestBuildRegsClear(t *testing.T) {
 		{
 			description: "Nil Regs",
 			regs:        nil,
-			expected:    &openrtb2.Regs{Ext: nil},
+			expected:    nil,
 		},
 		{
 			description: "Nil Regs.Ext",
diff --git a/stored_responses/stored_responses_test.go b/stored_responses/stored_responses_test.go
index 2e7b7af4..3175e04f 100644
--- a/stored_responses/stored_responses_test.go
+++ b/stored_responses/stored_responses_test.go
@@ -285,7 +285,7 @@ func TestProcessStoredAuctionAndBidResponsesErrors(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		_, _, errorList := ProcessStoredResponses(nil, test.requestJson, nil, bidderMap)
+		_, _, _, errorList := ProcessStoredResponses(nil, test.requestJson, nil, bidderMap)
 		assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description)
 	}
 
@@ -308,6 +308,7 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
 		requestJson                    []byte
 		expectedStoredAuctionResponses ImpsWithBidResponses
 		expectedStoredBidResponses     ImpBidderStoredResp
+		expectedBidderImpReplaceImpID  BidderImpReplaceImpID
 	}{
 		{
 			description: "No stored responses",
@@ -323,6 +324,7 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
     			]}`),
 			expectedStoredAuctionResponses: nil,
 			expectedStoredBidResponses:     nil,
+			expectedBidderImpReplaceImpID:  nil,
 		},
 		{
 			description: "Stored auction response one imp",
@@ -344,7 +346,8 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
 			expectedStoredAuctionResponses: ImpsWithBidResponses{
 				"imp-id1": bidStoredResp1,
 			},
-			expectedStoredBidResponses: ImpBidderStoredResp{},
+			expectedStoredBidResponses:    ImpBidderStoredResp{},
+			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{},
 		},
 		{
 			description: "Stored bid response one imp",
@@ -367,6 +370,9 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
 			expectedStoredBidResponses: ImpBidderStoredResp{
 				"imp-id1": {"bidderA": bidStoredResp1},
 			},
+			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{
+				"bidderA": map[string]bool{"imp-id1": true},
+			},
 		},
 		{
 			description: "Stored bid responses two bidders one imp",
@@ -379,8 +385,8 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
                 		},
                 		"prebid": {
                     		"storedbidresponse": [
-                        		{"bidder":"bidderA", "id": "1"},
-                        		{"bidder":"bidderB", "id": "2"}
+                        		{"bidder":"bidderA", "id": "1", "replaceimpid": true},
+                        		{"bidder":"bidderB", "id": "2", "replaceimpid": false}
                     		]
                 		}
             		}
@@ -390,6 +396,10 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
 			expectedStoredBidResponses: ImpBidderStoredResp{
 				"imp-id1": {"bidderA": bidStoredResp1, "bidderB": bidStoredResp2},
 			},
+			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{
+				"bidderA": map[string]bool{"imp-id1": true},
+				"bidderB": map[string]bool{"imp-id1": false},
+			},
 		},
 		{
 			//This is not a valid scenario for real auction request, added for testing purposes
@@ -419,6 +429,10 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
 			expectedStoredBidResponses: ImpBidderStoredResp{
 				"imp-id1": {"bidderA": bidStoredResp1, "bidderB": bidStoredResp2},
 			},
+			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{
+				"bidderA": map[string]bool{"imp-id1": true},
+				"bidderB": map[string]bool{"imp-id1": true},
+			},
 		},
 		{
 			description: "Stored auction response three imps",
@@ -468,7 +482,8 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
 				"imp-id2": bidStoredResp2,
 				"imp-id3": bidStoredResp3,
 			},
-			expectedStoredBidResponses: ImpBidderStoredResp{},
+			expectedStoredBidResponses:    ImpBidderStoredResp{},
+			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{},
 		},
 		{
 			description: "Stored auction response three imps duplicated stored auction response",
@@ -518,7 +533,8 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
 				"imp-id2": bidStoredResp2,
 				"imp-id3": bidStoredResp2,
 			},
-			expectedStoredBidResponses: ImpBidderStoredResp{},
+			expectedStoredBidResponses:    ImpBidderStoredResp{},
+			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{},
 		},
 		{
 			description: "Stored bid responses two bidders two imp",
@@ -531,7 +547,7 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
                 		},
                 		"prebid": {
                     		"storedbidresponse": [
-                        		{"bidder":"bidderA", "id": "1"},
+                        		{"bidder":"bidderA", "id": "1", "replaceimpid": false},
                         		{"bidder":"bidderB", "id": "2"}
                     		]
                 		}
@@ -546,7 +562,7 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
                 		"prebid": {
                     		"storedbidresponse": [
                         		{"bidder":"bidderA", "id": "3"},
-                        		{"bidder":"bidderB", "id": "2"}
+                        		{"bidder":"bidderB", "id": "2", "replaceimpid": false}
                     		]
                 		}
             		}
@@ -557,18 +573,78 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
 				"imp-id1": {"bidderA": bidStoredResp1, "bidderB": bidStoredResp2},
 				"imp-id2": {"bidderA": bidStoredResp3, "bidderB": bidStoredResp2},
 			},
+			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{
+				"bidderA": map[string]bool{"imp-id1": false, "imp-id2": true},
+				"bidderB": map[string]bool{"imp-id1": true, "imp-id2": false},
+			},
 		},
 	}
 
 	for _, test := range testCases {
-		storedAuctionResponses, storedBidResponses, errorList := ProcessStoredResponses(nil, test.requestJson, fetcher, bidderMap)
+		storedAuctionResponses, storedBidResponses, bidderImpReplaceImpId, errorList := ProcessStoredResponses(nil, test.requestJson, fetcher, bidderMap)
 		assert.Equal(t, test.expectedStoredAuctionResponses, storedAuctionResponses, "storedAuctionResponses doesn't match: %s\n", test.description)
 		assert.Equalf(t, test.expectedStoredBidResponses, storedBidResponses, "storedBidResponses doesn't match: %s\n", test.description)
+		assert.Equal(t, test.expectedBidderImpReplaceImpID, bidderImpReplaceImpId, "bidderImpReplaceImpId doesn't match: %s\n", test.description)
 		assert.Nil(t, errorList, "Error should be nil")
 	}
 
 }
 
+func TestFlipMap(t *testing.T) {
+	testCases := []struct {
+		description              string
+		inImpBidderReplaceImpID  ImpBidderReplaceImpID
+		outBidderImpReplaceImpID BidderImpReplaceImpID
+	}{
+		{
+			description:              "Empty ImpBidderReplaceImpID",
+			inImpBidderReplaceImpID:  ImpBidderReplaceImpID{},
+			outBidderImpReplaceImpID: BidderImpReplaceImpID{},
+		},
+		{
+			description:              "Nil ImpBidderReplaceImpID",
+			inImpBidderReplaceImpID:  nil,
+			outBidderImpReplaceImpID: BidderImpReplaceImpID{},
+		},
+		{
+			description:              "ImpBidderReplaceImpID has a one element map with single element",
+			inImpBidderReplaceImpID:  ImpBidderReplaceImpID{"imp-id": {"bidderA": true}},
+			outBidderImpReplaceImpID: BidderImpReplaceImpID{"bidderA": {"imp-id": true}},
+		},
+		{
+			description:              "ImpBidderReplaceImpID has a one element map with multiple elements",
+			inImpBidderReplaceImpID:  ImpBidderReplaceImpID{"imp-id": {"bidderA": true, "bidderB": false}},
+			outBidderImpReplaceImpID: BidderImpReplaceImpID{"bidderA": {"imp-id": true}, "bidderB": {"imp-id": false}},
+		},
+		{
+			description: "ImpBidderReplaceImpID has multiple elements map with single element",
+			inImpBidderReplaceImpID: ImpBidderReplaceImpID{
+				"imp-id1": {"bidderA": true},
+				"imp-id2": {"bidderB": false}},
+			outBidderImpReplaceImpID: BidderImpReplaceImpID{
+				"bidderA": {"imp-id1": true},
+				"bidderB": {"imp-id2": false}},
+		},
+		{
+			description: "ImpBidderReplaceImpID has multiple elements map with multiple elements",
+			inImpBidderReplaceImpID: ImpBidderReplaceImpID{
+				"imp-id1": {"bidderA": true, "bidderB": false, "bidderC": false, "bidderD": true},
+				"imp-id2": {"bidderA": false, "bidderB": false, "bidderC": true, "bidderD": true},
+				"imp-id3": {"bidderA": false, "bidderB": true, "bidderC": true, "bidderD": false}},
+			outBidderImpReplaceImpID: BidderImpReplaceImpID{
+				"bidderA": {"imp-id1": true, "imp-id2": false, "imp-id3": false},
+				"bidderB": {"imp-id1": false, "imp-id2": false, "imp-id3": true},
+				"bidderC": {"imp-id1": false, "imp-id2": true, "imp-id3": true},
+				"bidderD": {"imp-id1": true, "imp-id2": true, "imp-id3": false}},
+		},
+	}
+
+	for _, test := range testCases {
+		actualResult := flipMap(test.inImpBidderReplaceImpID)
+		assert.Equal(t, test.outBidderImpReplaceImpID, actualResult, "Incorrect flipped map for test case %s\n", test.description)
+	}
+}
+
 type mockStoredBidResponseFetcher struct {
 	data map[string]json.RawMessage
 }