Repository: prebid/prebid-server
Pull request #2540 (2023-01-24 16:23:10)

Initial infrastructure to support bidder openRTB support flags
This does not make use of the new flags. It also pushes BidderInfos down to cleanOpenRTBRequests() so that the individual "cleaned" requests to the bidders can be modified as per bidder support of openrtb features.

hhhjort: On pushing down `BidderInfos` to `getAuctionBidderRequests(...)`, I don't think it is needed for GPP as all the privacy handling is done outside of this function. Do you think we need it there for OpenRTB downgrades?
guscarreon: > On pushing down `BidderInfos` to `getAuctionBidderRequests(...)`, I don't think it is needed for GPP as all the privacy handling is done outside of this function. Do you think we need it there for OpenRTB downgrades?

For the OpenRTB I believe we need that info and it makes sense to me to downgrade to 2.5 in that place. But I agree we can takle this later and it's definitely not in the scope of this PR. I'm ok with ignoring that comment.

Patch:
diff --git a/.github/workflows/cross-repo-issue.yml b/.github/workflows/cross-repo-issue.yml
new file mode 100644
index 00000000..1c9c0a7e
--- /dev/null
+++ b/.github/workflows/cross-repo-issue.yml
@@ -0,0 +1,28 @@
+name: Cross-repo Issue Creation
+
+on:
+  pull_request:
+    types: [closed]
+    branches:
+      - "master"
+
+jobs:
+  cross-repo:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Generate token
+        id: generate_token
+        uses: tibdex/github-app-token@v1
+        with:
+          app_id: ${{ secrets.XREPO_APP_ID }}
+          private_key: ${{ secrets.XREPO_PEM }}
+      - name: create issue in other repo
+        if: "!contains(github.event.pull_request.labels.*.name, 'do not port') && github.event.pull_request.merged"
+        env:
+          GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
+        run: |
+          echo -e "A PR was merged over on PBS-Go\n\n- [https://github.com/prebid/prebid-server/pull/${{github.event.number}}](https://github.com/prebid/prebid-server/pull/${{github.event.number}})\n- timestamp: ${{ github.event.pull_request.merged_at}}" > msg
+          export msg=$(cat msg)
+          gh issue create --repo prebid/prebid-server-java --title "Port PR from PBS-Go: ${{ github.event.pull_request.title }}" \
+              --body "$msg" \
+              --label auto
diff --git a/.github/workflows/issue_prioritization.yml b/.github/workflows/issue_prioritization.yml
index 9a178577..3843507b 100644
--- a/.github/workflows/issue_prioritization.yml
+++ b/.github/workflows/issue_prioritization.yml
@@ -25,21 +25,30 @@ jobs:
           gh api graphql -f query='
             query($org: String!, $number: Int!) {
               organization(login: $org){
-                projectNext(number: $number) {
+                projectV2(number: $number) {
                   id
                   fields(first:100) {
                     nodes {
-                      id
-                      name
-                      settings
+                      ... on ProjectV2Field {
+                        id
+                        name
+                      }
+                      ... on ProjectV2SingleSelectField {
+                        id
+                        name
+                        options {
+                          id
+                          name
+                        }
+                      }
                     }
                   }
                 }
               }
             }' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
 
-          echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
-          echo 'DATE_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "'"$DATE_FIELD"'") | .id' project_data.json) >> $GITHUB_ENV
+          echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV
+          echo 'DATE_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name=="'"$DATE_FIELD"'") | .id' project_data.json) >> $GITHUB_ENV
 
       - name: Add issue to project
         env:
@@ -48,9 +57,9 @@ jobs:
         run: |
           gh api graphql -f query='
             mutation($project:ID!, $issue:ID!) {
-              addProjectNextItem(input: {projectId: $project, contentId: $issue}) {
-                projectNextItem {
-                  id,
+              addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) {
+                item {
+                  id
                   content {
                      ... on Issue {
                         createdAt
@@ -63,8 +72,8 @@ jobs:
               }
             }' -f project=$PROJECT_ID -f issue=$ISSUE_ID > issue_data.json
           
-          echo 'ITEM_ID='$(jq '.data.addProjectNextItem.projectNextItem.id' issue_data.json) >> $GITHUB_ENV
-          echo 'ITEM_CREATION_DATE='$(jq '.data.addProjectNextItem.projectNextItem.content.createdAt' issue_data.json) >> $GITHUB_ENV
+          echo 'ITEM_ID='$(jq '.data.addProjectV2ItemById.item.id' issue_data.json) >> $GITHUB_ENV
+          echo 'ITEM_CREATION_DATE='$(jq '.data.addProjectV2ItemById.item.content.createdAt' issue_data.json | cut -c 2-11) >> $GITHUB_ENV
 
       - name: Set fields
         env:
@@ -75,15 +84,17 @@ jobs:
               $project: ID!
               $item: ID!
               $date_field: ID!
-              $date_value: String!
+              $date_value: Date!
             ) {
-              set_creation_date: updateProjectNextItemField(input: {
+              set_creation_date: updateProjectV2ItemFieldValue(input: {
                 projectId: $project
                 itemId: $item
                 fieldId: $date_field
-                value: $date_value
+                value: {
+                  date: $date_value
+                }
               }) {
-                projectNextItem {
+                projectV2Item {
                   id
                 }
               }
diff --git a/adapters/adnuntius/adnuntius.go b/adapters/adnuntius/adnuntius.go
index 3b01dab5..084ffc22 100644
--- a/adapters/adnuntius/adnuntius.go
+++ b/adapters/adnuntius/adnuntius.go
@@ -76,8 +76,9 @@ const minutesInHour = 60
 // Builder builds a new instance of the Adnuntius adapter for the given bidder with the given config.
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
 	bidder := &adapter{
-		time:     &timeutil.RealTime{},
-		endpoint: config.Endpoint,
+		time:      &timeutil.RealTime{},
+		endpoint:  config.Endpoint,
+		extraInfo: config.ExtraAdapterInfo,
 	}
 
 	return bidder, nil
diff --git a/adapters/adsinteractive/adsinteractive.go b/adapters/adsinteractive/adsinteractive.go
new file mode 100644
index 00000000..503c61d2
--- /dev/null
+++ b/adapters/adsinteractive/adsinteractive.go
@@ -0,0 +1,78 @@
+package adsinteractive
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/prebid/openrtb/v17/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 Adsinteractive adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+	return bidder, nil
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	requestJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	requestData := &adapters.RequestData{
+		Method: "POST",
+		Uri:    a.endpoint,
+		Body:   requestJSON,
+	}
+
+	return []*adapters.RequestData{requestData}, nil
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if responseData.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if responseData.StatusCode == http.StatusBadRequest {
+		err := &errortypes.BadInput{
+			Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
+		}
+		return nil, []error{err}
+	}
+
+	if responseData.StatusCode != http.StatusOK {
+		err := &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
+		}
+		return nil, []error{err}
+	}
+
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(responseData.Body, &response); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+	bidResponse.Currency = response.Cur
+	for _, seatBid := range response.SeatBid {
+		for i := range seatBid.Bid {
+			b := &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: "banner",
+			}
+			bidResponse.Bids = append(bidResponse.Bids, b)
+		}
+	}
+	return bidResponse, nil
+}
diff --git a/adapters/brave/brave.go b/adapters/brave/brave.go
new file mode 100755
index 00000000..627b6547
--- /dev/null
+++ b/adapters/brave/brave.go
@@ -0,0 +1,157 @@
+package brave
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"text/template"
+
+	"github.com/prebid/openrtb/v17/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	endpoint *template.Template
+}
+
+// Builder builds a new instance of the Brave adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	uri, err := template.New("endpointTemplate").Parse(config.Endpoint)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
+	}
+
+	bidder := &adapter{
+		endpoint: uri,
+	}
+	return bidder, nil
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var braveExt *openrtb_ext.ExtImpBrave
+	var err error
+
+	braveExt, err = a.getImpressionExt(&request.Imp[0])
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	request.Imp[0].Ext = nil
+
+	reqJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+
+	url, err := a.buildEndpointURL(braveExt)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	return []*adapters.RequestData{{
+		Method:  http.MethodPost,
+		Body:    reqJSON,
+		Uri:     url,
+		Headers: headers,
+	}}, nil
+}
+
+func (a *adapter) getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpBrave, error) {
+	var bidderExt adapters.ExtImpBidder
+	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "ext.bidder not provided",
+		}
+	}
+	var braveExt openrtb_ext.ExtImpBrave
+	if err := json.Unmarshal(bidderExt.Bidder, &braveExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "ext.bidder not provided",
+		}
+	}
+	return &braveExt, nil
+}
+
+func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtImpBrave) (string, error) {
+	endpointParams := macros.EndpointTemplateParams{PublisherID: params.PlacementID}
+	return macros.ResolveMacros(a.endpoint, endpointParams)
+}
+
+func (a *adapter) MakeBids(
+	receivedRequest *openrtb2.BidRequest,
+	bidderRequest *adapters.RequestData,
+	bidderResponse *adapters.ResponseData,
+) (
+	*adapters.BidderResponse,
+	[]error,
+) {
+
+	if bidderResponse.StatusCode == http.StatusNoContent {
+		return nil, []error{&errortypes.BadInput{Message: "No bid"}}
+	}
+
+	if bidderResponse.StatusCode == http.StatusBadRequest {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", bidderResponse.StatusCode),
+		}}
+	}
+
+	if bidderResponse.StatusCode == http.StatusServiceUnavailable {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Service Unavailable. Status Code: [ %d ] ", bidderResponse.StatusCode),
+		}}
+	}
+
+	if bidderResponse.StatusCode != http.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", bidderResponse.StatusCode),
+		}}
+	}
+
+	var bidResponse openrtb2.BidResponse
+	if err := json.Unmarshal(bidderResponse.Body, &bidResponse); err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Bad Server Response",
+		}}
+	}
+
+	if len(bidResponse.SeatBid) == 0 {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Empty SeatBid array",
+		}}
+	}
+
+	bidResponseFinal := adapters.NewBidderResponseWithBidsCapacity(len(bidResponse.SeatBid[0].Bid))
+	sb := bidResponse.SeatBid[0]
+
+	for _, bid := range sb.Bid {
+		bidResponseFinal.Bids = append(bidResponseFinal.Bids, &adapters.TypedBid{
+			Bid:     &bid,
+			BidType: getMediaTypeForImp(bid.ImpID, receivedRequest.Imp),
+		})
+	}
+	return bidResponseFinal, nil
+}
+
+func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
+	mediaType := openrtb_ext.BidTypeBanner
+	for _, imp := range imps {
+		if imp.ID == impId {
+			if imp.Video != nil {
+				mediaType = openrtb_ext.BidTypeVideo
+			} else if imp.Native != nil {
+				mediaType = openrtb_ext.BidTypeNative
+			}
+			return mediaType
+		}
+	}
+	return mediaType
+}
diff --git a/adapters/kiviads/kiviads.go b/adapters/kiviads/kiviads.go
new file mode 100644
index 00000000..c947c2bf
--- /dev/null
+++ b/adapters/kiviads/kiviads.go
@@ -0,0 +1,154 @@
+package kiviads
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/prebid/openrtb/v17/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
+}
+
+type reqBodyExt struct {
+	KiviadsBidderExt reqBodyExtBidder `json:"bidder"`
+}
+
+type reqBodyExtBidder struct {
+	Type        string `json:"type"`
+	PlacementID string `json:"placementId,omitempty"`
+	EndpointID  string `json:"endpointId,omitempty"`
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+	return bidder, nil
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var err error
+	var adapterRequests []*adapters.RequestData
+
+	reqCopy := *request
+	for _, imp := range request.Imp {
+		reqCopy.Imp = []openrtb2.Imp{imp}
+
+		var bidderExt adapters.ExtImpBidder
+		var kiviadsExt openrtb_ext.ImpExtKiviads
+
+		if err = json.Unmarshal(reqCopy.Imp[0].Ext, &bidderExt); err != nil {
+			return nil, []error{err}
+		}
+		if err = json.Unmarshal(bidderExt.Bidder, &kiviadsExt); err != nil {
+			return nil, []error{err}
+		}
+
+		temp := reqBodyExt{KiviadsBidderExt: reqBodyExtBidder{}}
+
+		if kiviadsExt.PlacementID != "" {
+			temp.KiviadsBidderExt.PlacementID = kiviadsExt.PlacementID
+			temp.KiviadsBidderExt.Type = "publisher"
+		} else if kiviadsExt.EndpointID != "" {
+			temp.KiviadsBidderExt.EndpointID = kiviadsExt.EndpointID
+			temp.KiviadsBidderExt.Type = "network"
+		}
+
+		finalyImpExt, err := json.Marshal(temp)
+		if err != nil {
+			return nil, []error{err}
+		}
+
+		reqCopy.Imp[0].Ext = finalyImpExt
+
+		adapterReq, err := a.makeRequest(&reqCopy)
+		if err != nil {
+			return nil, []error{err}
+		}
+
+		if adapterReq != nil {
+			adapterRequests = append(adapterRequests, adapterReq)
+		}
+	}
+	return adapterRequests, nil
+}
+
+func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, error) {
+	reqJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, err
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	return &adapters.RequestData{
+		Method:  "POST",
+		Uri:     a.endpoint,
+		Body:    reqJSON,
+		Headers: headers,
+	}, err
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if responseData.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if responseData.StatusCode != http.StatusOK {
+		err := &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
+		}
+		return nil, []error{err}
+	}
+
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(responseData.Body, &response); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+	bidResponse.Currency = response.Cur
+	for _, seatBid := range response.SeatBid {
+		for i := range seatBid.Bid {
+			bidType, err := getMediaTypeForImp(seatBid.Bid[i].ImpID, request.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
+			}
+			if imp.Video != nil {
+				return openrtb_ext.BidTypeVideo, nil
+			}
+			if imp.Native != nil {
+				return openrtb_ext.BidTypeNative, nil
+			}
+		}
+	}
+
+	return "", &errortypes.BadInput{
+		Message: fmt.Sprintf("Failed to find impression \"%s\"", impID),
+	}
+}
diff --git a/adapters/mobilefuse/mobilefuse.go b/adapters/mobilefuse/mobilefuse.go
index 5d5cad1f..021d033f 100644
--- a/adapters/mobilefuse/mobilefuse.go
+++ b/adapters/mobilefuse/mobilefuse.go
@@ -182,7 +182,7 @@ func (adapter *MobileFuseAdapter) getValidImps(bidRequest *openrtb2.BidRequest,
 	var validImps []openrtb2.Imp
 
 	for _, imp := range bidRequest.Imp {
-		if imp.Banner != nil || imp.Video != nil {
+		if imp.Banner != nil || imp.Video != nil || imp.Native != nil {
 			imp.TagID = strconv.Itoa(ext.PlacementId)
 			imp.Ext = nil
 			validImps = append(validImps, imp)
@@ -198,9 +198,12 @@ func (adapter *MobileFuseAdapter) getBidType(bid openrtb2.Bid) openrtb_ext.BidTy
 	if bid.Ext != nil {
 		var bidExt BidExt
 		err := json.Unmarshal(bid.Ext, &bidExt)
-
-		if err == nil && bidExt.Mf.MediaType == "video" {
-			return openrtb_ext.BidTypeVideo
+		if err == nil {
+			if bidExt.Mf.MediaType == "video" {
+				return openrtb_ext.BidTypeVideo
+			} else if bidExt.Mf.MediaType == "native" {
+				return openrtb_ext.BidTypeNative
+			}
 		}
 	}
 
diff --git a/adapters/openx/openx.go b/adapters/openx/openx.go
index 2240bd76..269d1c77 100644
--- a/adapters/openx/openx.go
+++ b/adapters/openx/openx.go
@@ -15,11 +15,13 @@ import (
 const hbconfig = "hb_pbs_1.0.0"
 
 type OpenxAdapter struct {
-	endpoint string
+	bidderName string
+	endpoint   string
 }
 
 type openxImpExt struct {
-	CustomParams map[string]interface{} `json:"customParams,omitempty"`
+	CustomParams       map[string]interface{}             `json:"customParams,omitempty"`
+	AuctionEnvironment openrtb_ext.AuctionEnvironmentType `json:"ae,omitempty"`
 }
 
 type openxReqExt struct {
@@ -28,6 +30,10 @@ type openxReqExt struct {
 	BidderConfig string `json:"bc"`
 }
 
+type openxRespExt struct {
+	FledgeAuctionConfigs map[string]json.RawMessage `json:"fledge_auction_configs,omitempty"`
+}
+
 func (a *OpenxAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
 	var errs []error
 	var bannerImps []openrtb2.Imp
@@ -133,18 +139,27 @@ func preprocess(imp *openrtb2.Imp, reqExt *openxReqExt) error {
 	if imp.BidFloor == 0 && openxExt.CustomFloor > 0 {
 		imp.BidFloor = openxExt.CustomFloor
 	}
-	imp.Ext = nil
+
+	impExt := openxImpExt{}
+	addImpExt := false
 
 	if openxExt.CustomParams != nil {
-		impExt := openxImpExt{
-			CustomParams: openxExt.CustomParams,
-		}
+		impExt.CustomParams = openxExt.CustomParams
+		addImpExt = true
+	}
+	if bidderExt.AuctionEnvironment != openrtb_ext.ServerSideAuction {
+		impExt.AuctionEnvironment = bidderExt.AuctionEnvironment
+		addImpExt = true
+	}
+	if addImpExt {
 		var err error
 		if imp.Ext, err = json.Marshal(impExt); err != nil {
 			return &errortypes.BadInput{
 				Message: err.Error(),
 			}
 		}
+	} else {
+		imp.Ext = nil
 	}
 
 	if imp.Video != nil {
@@ -189,6 +204,21 @@ func (a *OpenxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRe
 		bidResponse.Currency = bidResp.Cur
 	}
 
+	if bidResp.Ext != nil {
+		var bidRespExt openxRespExt
+		if err := json.Unmarshal(bidResp.Ext, &bidRespExt); err == nil && bidRespExt.FledgeAuctionConfigs != nil {
+			bidResponse.FledgeAuctionConfigs = make([]*openrtb_ext.FledgeAuctionConfig, 0, len(bidRespExt.FledgeAuctionConfigs))
+			for impId, config := range bidRespExt.FledgeAuctionConfigs {
+				fledgeAuctionConfig := &openrtb_ext.FledgeAuctionConfig{
+					ImpId:  impId,
+					Bidder: a.bidderName,
+					Config: config,
+				}
+				bidResponse.FledgeAuctionConfigs = append(bidResponse.FledgeAuctionConfigs, fledgeAuctionConfig)
+			}
+		}
+	}
+
 	for _, sb := range bidResp.SeatBid {
 		for i := range sb.Bid {
 			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
@@ -220,7 +250,8 @@ func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
 // Builder builds a new instance of the Openx adapter for the given bidder with the given config.
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
 	bidder := &OpenxAdapter{
-		endpoint: config.Endpoint,
+		endpoint:   config.Endpoint,
+		bidderName: string(bidderName),
 	}
 	return bidder, nil
 }
diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go
index 37c47d88..faa43e93 100644
--- a/adapters/rubicon/rubicon.go
+++ b/adapters/rubicon/rubicon.go
@@ -161,7 +161,7 @@ type rubiconSeatBid struct {
 
 type rubiconBid struct {
 	openrtb2.Bid
-	AdmObject json.RawMessage `json:"admobject,omitempty"`
+	AdmNative json.RawMessage `json:"adm_native,omitempty"`
 }
 
 type extPrebid struct {
@@ -1113,7 +1113,7 @@ func resolveAdm(bid rubiconBid) string {
 		return bidAdm
 	}
 
-	admObject := bid.AdmObject
+	admObject := bid.AdmNative
 	admObjectAsBytes, err := json.Marshal(&admObject)
 	if err != nil {
 		return ""
diff --git a/config/bidderinfo.go b/config/bidderinfo.go
index da606a03..65847151 100644
--- a/config/bidderinfo.go
+++ b/config/bidderinfo.go
@@ -47,7 +47,8 @@ type BidderInfo struct {
 	PlatformID string `yaml:"platform_id" mapstructure:"platform_id"`
 	AppSecret  string `yaml:"app_secret" mapstructure:"app_secret"`
 	// EndpointCompression determines, if set, the type of compression the bid request will undergo before being sent to the corresponding bid server
-	EndpointCompression string `yaml:"endpointCompression" mapstructure:"endpointCompression"`
+	EndpointCompression string       `yaml:"endpointCompression" mapstructure:"endpointCompression"`
+	OpenRTB             *OpenRTBInfo `yaml:"openrtb" mapstructure:"openrtb"`
 }
 
 // BidderInfoExperiment specifies non-production ready feature config for a bidder
@@ -87,6 +88,14 @@ type AdapterXAPI struct {
 	Tracker  string `yaml:"tracker" mapstructure:"tracker"`
 }
 
+// OpenRTBInfo specifies the versions/aspects of openRTB that a bidder supports
+// Version is not yet actively supported
+// GPPSupported is not yet actively supported
+type OpenRTBInfo struct {
+	Version      string `yaml:"version" mapstructure:"version"`
+	GPPSupported bool   `yaml:"gpp-supported" mapstructure:"gpp-supported"`
+}
+
 // Syncer specifies the user sync settings for a bidder. This struct is shared by the account config,
 // so it needs to have both yaml and mapstructure mappings.
 type Syncer struct {
diff --git a/config/config.go b/config/config.go
index 6630cdbc..b6bc69b7 100644
--- a/config/config.go
+++ b/config/config.go
@@ -1406,20 +1406,22 @@ func bindDatabaseEnvVars(v *viper.Viper) {
 
 func setBidderDefaults(v *viper.Viper, bidder string) {
 	adapterCfgPrefix := "adapters." + bidder
-	v.BindEnv(adapterCfgPrefix+".disabled", "")
-	v.BindEnv(adapterCfgPrefix+".endpoint", "")
-	v.BindEnv(adapterCfgPrefix+".extra_info", "")
-	v.BindEnv(adapterCfgPrefix+".modifyingVastXmlAllowed", "")
-	v.BindEnv(adapterCfgPrefix+".debug.allow", "")
-	v.BindEnv(adapterCfgPrefix+".gvlVendorID", "")
-	v.BindEnv(adapterCfgPrefix+".usersync_url", "")
-	v.BindEnv(adapterCfgPrefix+".experiment.adsCert.enabled", "")
-	v.BindEnv(adapterCfgPrefix+".platform_id", "")
-	v.BindEnv(adapterCfgPrefix+".app_secret", "")
-	v.BindEnv(adapterCfgPrefix+".xapi.username", "")
-	v.BindEnv(adapterCfgPrefix+".xapi.password", "")
-	v.BindEnv(adapterCfgPrefix+".xapi.tracker", "")
-	v.BindEnv(adapterCfgPrefix+".endpointCompression", "")
+	v.BindEnv(adapterCfgPrefix + ".disabled")
+	v.BindEnv(adapterCfgPrefix + ".endpoint")
+	v.BindEnv(adapterCfgPrefix + ".extra_info")
+	v.BindEnv(adapterCfgPrefix + ".modifyingVastXmlAllowed")
+	v.BindEnv(adapterCfgPrefix + ".debug.allow")
+	v.BindEnv(adapterCfgPrefix + ".gvlVendorID")
+	v.BindEnv(adapterCfgPrefix + ".usersync_url")
+	v.BindEnv(adapterCfgPrefix + ".experiment.adsCert.enabled")
+	v.BindEnv(adapterCfgPrefix + ".platform_id")
+	v.BindEnv(adapterCfgPrefix + ".app_secret")
+	v.BindEnv(adapterCfgPrefix + ".xapi.username")
+	v.BindEnv(adapterCfgPrefix + ".xapi.password")
+	v.BindEnv(adapterCfgPrefix + ".xapi.tracker")
+	v.BindEnv(adapterCfgPrefix + ".endpointCompression")
+	v.BindEnv(adapterCfgPrefix + ".openrtb.version")
+	v.BindEnv(adapterCfgPrefix + ".openrtb.gpp-supported")
 
 	v.BindEnv(adapterCfgPrefix + ".usersync.key")
 	v.BindEnv(adapterCfgPrefix + ".usersync.default")
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 5b85eb67..6e38f8f8 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -325,6 +325,22 @@ func sendAmpResponse(
 		}
 	}
 
+	// Extract global targeting
+	var extResponse openrtb_ext.ExtBidResponse
+	eRErr := json.Unmarshal(response.Ext, &extResponse)
+	if eRErr != nil {
+		ao.Errors = append(ao.Errors, fmt.Errorf("AMP response: failed to unpack OpenRTB response.ext, debug info cannot be forwarded: %v", eRErr))
+	}
+	// Extract global targeting
+	extPrebid := extResponse.Prebid
+	if extPrebid != nil {
+		for key, value := range extPrebid.Targeting {
+			_, exists := targets[key]
+			if !exists {
+				targets[key] = value
+			}
+		}
+	}
 	// Now JSONify the targets for the AMP response.
 	ampResponse := AmpResponse{Targeting: targets}
 	ao, ampResponse.ORTB2.Ext = getExtBidResponse(hookExecutor, response, reqWrapper, account, ao, errs)
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index ea1cd46b..eddc5173 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -516,6 +516,7 @@ func buildVideoResponse(bidresponse *openrtb2.BidResponse, podErrors []PodError)
 				HbPb:       tempRespBidExt.Prebid.Targeting[formatTargetingKey(openrtb_ext.HbpbConstantKey, seatBid.Seat)],
 				HbPbCatDur: tempRespBidExt.Prebid.Targeting[formatTargetingKey(openrtb_ext.HbCategoryDurationKey, seatBid.Seat)],
 				HbCacheID:  tempRespBidExt.Prebid.Targeting[formatTargetingKey(openrtb_ext.HbVastCacheKey, seatBid.Seat)],
+				HbDeal:     tempRespBidExt.Prebid.Targeting[formatTargetingKey(openrtb_ext.HbDealIDConstantKey, seatBid.Seat)],
 			}
 
 			adPod := findAdPod(podId, adPods)
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index f808a404..bdd00819 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -20,6 +20,7 @@ import (
 	"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/adsinteractive"
 	"github.com/prebid/prebid-server/adapters/adtarget"
 	"github.com/prebid/prebid-server/adapters/adtelligent"
 	"github.com/prebid/prebid-server/adapters/adtrgtme"
@@ -51,6 +52,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/blue"
 	"github.com/prebid/prebid-server/adapters/bmtm"
 	"github.com/prebid/prebid-server/adapters/boldwin"
+	"github.com/prebid/prebid-server/adapters/brave"
 	"github.com/prebid/prebid-server/adapters/brightroll"
 	"github.com/prebid/prebid-server/adapters/ccx"
 	"github.com/prebid/prebid-server/adapters/coinzilla"
@@ -92,6 +94,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/kargo"
 	"github.com/prebid/prebid-server/adapters/kayzen"
 	"github.com/prebid/prebid-server/adapters/kidoz"
+	"github.com/prebid/prebid-server/adapters/kiviads"
 	"github.com/prebid/prebid-server/adapters/krushmedia"
 	"github.com/prebid/prebid-server/adapters/kubient"
 	"github.com/prebid/prebid-server/adapters/lockerdome"
@@ -184,6 +187,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderAdpone:            adpone.Builder,
 		openrtb_ext.BidderAdprime:           adprime.Builder,
 		openrtb_ext.BidderAdrino:            adrino.Builder,
+		openrtb_ext.BidderAdsinteractive:    adsinteractive.Builder,
 		openrtb_ext.BidderAdtarget:          adtarget.Builder,
 		openrtb_ext.BidderAdtrgtme:          adtrgtme.Builder,
 		openrtb_ext.BidderAdtelligent:       adtelligent.Builder,
@@ -215,6 +219,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderBlue:              blue.Builder,
 		openrtb_ext.BidderBmtm:              bmtm.Builder,
 		openrtb_ext.BidderBoldwin:           boldwin.Builder,
+		openrtb_ext.BidderBrave:             brave.Builder,
 		openrtb_ext.BidderBrightroll:        brightroll.Builder,
 		openrtb_ext.BidderCcx:               ccx.Builder,
 		openrtb_ext.BidderCoinzilla:         coinzilla.Builder,
@@ -259,6 +264,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderKargo:             kargo.Builder,
 		openrtb_ext.BidderKayzen:            kayzen.Builder,
 		openrtb_ext.BidderKidoz:             kidoz.Builder,
+		openrtb_ext.BidderKiviads:           kiviads.Builder,
 		openrtb_ext.BidderKrushmedia:        krushmedia.Builder,
 		openrtb_ext.BidderKubient:           kubient.Builder,
 		openrtb_ext.BidderLockerDome:        lockerdome.Builder,
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 2c85b024..d477701e 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -73,6 +73,7 @@ type exchange struct {
 	adsCertSigner            adscert.Signer
 	server                   config.Server
 	bidValidationEnforcement config.Validations
+	requestSplitter          requestSplitter
 }
 
 // Container to pass out response ext data from the GetAllBids goroutines back into the main thread
@@ -131,29 +132,41 @@ func NewExchange(adapters map[openrtb_ext.BidderName]AdaptedBidder, cache prebid
 		gdprDefaultValue = gdpr.SignalNo
 	}
 
-	return &exchange{
-		adapterMap:        adapters,
-		bidderInfo:        infos,
+	privacyConfig := config.Privacy{
+		CCPA: cfg.CCPA,
+		GDPR: cfg.GDPR,
+		LMT:  cfg.LMT,
+	}
+	requestSplitter := requestSplitter{
 		bidderToSyncerKey: bidderToSyncerKey,
-		cache:             cache,
-		cacheTime:         time.Duration(cfg.CacheURL.ExpectedTimeMillis) * time.Millisecond,
-		categoriesFetcher: categoriesFetcher,
-		currencyConverter: currencyConverter,
-		externalURL:       cfg.ExternalURL,
+		me:                metricsEngine,
+		privacyConfig:     privacyConfig,
 		gdprPermsBuilder:  gdprPermsBuilder,
 		tcf2ConfigBuilder: tcf2CfgBuilder,
-		me:                metricsEngine,
-		gdprDefaultValue:  gdprDefaultValue,
-		privacyConfig: config.Privacy{
-			CCPA: cfg.CCPA,
-			GDPR: cfg.GDPR,
-			LMT:  cfg.LMT,
-		},
+		hostSChainNode:    cfg.HostSChainNode,
+		bidderInfo:        infos,
+	}
+
+	return &exchange{
+		adapterMap:               adapters,
+		bidderInfo:               infos,
+		bidderToSyncerKey:        bidderToSyncerKey,
+		cache:                    cache,
+		cacheTime:                time.Duration(cfg.CacheURL.ExpectedTimeMillis) * time.Millisecond,
+		categoriesFetcher:        categoriesFetcher,
+		currencyConverter:        currencyConverter,
+		externalURL:              cfg.ExternalURL,
+		gdprPermsBuilder:         gdprPermsBuilder,
+		tcf2ConfigBuilder:        tcf2CfgBuilder,
+		me:                       metricsEngine,
+		gdprDefaultValue:         gdprDefaultValue,
+		privacyConfig:            privacyConfig,
 		bidIDGenerator:           &bidIDGenerator{cfg.GenerateBidID},
 		hostSChainNode:           cfg.HostSChainNode,
 		adsCertSigner:            adsCertSigner,
 		server:                   config.Server{ExternalUrl: cfg.ExternalURL, GvlID: cfg.GDPR.HostVendorID, DataCenter: cfg.DataCenter},
 		bidValidationEnforcement: cfg.Validations,
+		requestSplitter:          requestSplitter,
 	}
 }
 
@@ -269,7 +282,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 	gdprDefaultValue := e.parseGDPRDefaultValue(r.BidRequestWrapper.BidRequest)
 
 	// Slice of BidRequests, each a copy of the original cleaned to only contain bidder data for the named bidder
-	bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(ctx, r, requestExt, e.bidderToSyncerKey, e.me, gdprDefaultValue, e.privacyConfig, e.gdprPermsBuilder, e.tcf2ConfigBuilder, e.hostSChainNode)
+	bidderRequests, privacyLabels, errs := e.requestSplitter.cleanOpenRTBRequests(ctx, r, requestExt, gdprDefaultValue)
 
 	e.me.RecordRequestPrivacy(privacyLabels)
 
diff --git a/exchange/price_granularity.go b/exchange/price_granularity.go
index 242d420f..af3300b2 100644
--- a/exchange/price_granularity.go
+++ b/exchange/price_granularity.go
@@ -13,7 +13,7 @@ func GetPriceBucket(cpm float64, config openrtb_ext.PriceGranularity) string {
 	bucketMax := 0.0
 	increment := 0.0
 	precision := config.Precision
-
+	bucketMin := 0.0
 	for i := 0; i < len(config.Ranges); i++ {
 		if config.Ranges[i].Max > bucketMax {
 			bucketMax = config.Ranges[i].Max
@@ -21,6 +21,7 @@ func GetPriceBucket(cpm float64, config openrtb_ext.PriceGranularity) string {
 		// find what range cpm is in
 		if cpm >= config.Ranges[i].Min && cpm <= config.Ranges[i].Max {
 			increment = config.Ranges[i].Increment
+			bucketMin = config.Ranges[i].Min
 		}
 	}
 
@@ -29,13 +30,13 @@ func GetPriceBucket(cpm float64, config openrtb_ext.PriceGranularity) string {
 		cpmStr = strconv.FormatFloat(bucketMax, 'f', precision, 64)
 	} else if increment > 0 {
 		// If increment exists, get cpm string value
-		cpmStr = getCpmTarget(cpm, increment, precision)
+		cpmStr = getCpmTarget(cpm, bucketMin, increment, precision)
 	}
 
 	return cpmStr
 }
 
-func getCpmTarget(cpm float64, increment float64, precision int) string {
-	roundedCPM := math.Floor(cpm/increment) * increment
+func getCpmTarget(cpm float64, bucketMin float64, increment float64, precision int) string {
+	roundedCPM := math.Floor((cpm-bucketMin)/increment)*increment + bucketMin
 	return strconv.FormatFloat(roundedCPM, 'f', precision, 64)
 }
diff --git a/exchange/utils.go b/exchange/utils.go
index 3453769d..5450707d 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -34,21 +34,25 @@ var channelTypeMap = map[metrics.RequestType]config.ChannelType{
 
 const unknownBidder string = ""
 
+type requestSplitter struct {
+	bidderToSyncerKey map[string]string
+	me                metrics.MetricsEngine
+	privacyConfig     config.Privacy
+	gdprPermsBuilder  gdpr.PermissionsBuilder
+	tcf2ConfigBuilder gdpr.TCF2ConfigBuilder
+	hostSChainNode    *openrtb2.SupplyChainNode
+	bidderInfo        config.BidderInfos
+}
+
 // cleanOpenRTBRequests splits the input request into requests which are sanitized for each bidder. Intended behavior is:
 //
 //  1. BidRequest.Imp[].Ext will only contain the "prebid" field and a "bidder" field which has the params for the intended Bidder.
 //  2. Every BidRequest.Imp[] requested Bids from the Bidder who keys it.
 //  3. BidRequest.User.BuyerUID will be set to that Bidder's ID.
-func cleanOpenRTBRequests(ctx context.Context,
+func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 	auctionReq AuctionRequest,
 	requestExt *openrtb_ext.ExtRequest,
-	bidderToSyncerKey map[string]string,
-	metricsEngine metrics.MetricsEngine,
 	gdprDefaultValue gdpr.Signal,
-	privacyConfig config.Privacy,
-	gdprPermsBuilder gdpr.PermissionsBuilder,
-	tcf2ConfigBuilder gdpr.TCF2ConfigBuilder,
-	hostSChainNode *openrtb2.SupplyChainNode,
 ) (allowedBidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) {
 
 	req := auctionReq.BidRequestWrapper
@@ -73,7 +77,7 @@ func cleanOpenRTBRequests(ctx context.Context,
 	}
 
 	var allBidderRequests []BidderRequest
-	allBidderRequests, errs = getAuctionBidderRequests(auctionReq, requestExt, bidderToSyncerKey, impsByBidder, aliases, hostSChainNode)
+	allBidderRequests, errs = getAuctionBidderRequests(auctionReq, requestExt, rs.bidderToSyncerKey, impsByBidder, aliases, rs.hostSChainNode)
 
 	bidderNameToBidderReq := buildBidResponseRequest(req.BidRequest, bidderImpWithBidResp, aliases, auctionReq.BidderImpReplaceImpID)
 	//this function should be executed after getAuctionBidderRequests
@@ -98,12 +102,12 @@ func cleanOpenRTBRequests(ctx context.Context,
 	}
 	gdprApplies := gdprSignal == gdpr.SignalYes || (gdprSignal == gdpr.SignalAmbiguous && gdprDefaultValue == gdpr.SignalYes)
 
-	ccpaEnforcer, err := extractCCPA(req.BidRequest, privacyConfig, &auctionReq.Account, aliases, channelTypeMap[auctionReq.LegacyLabels.RType], gpp)
+	ccpaEnforcer, err := extractCCPA(req.BidRequest, rs.privacyConfig, &auctionReq.Account, aliases, channelTypeMap[auctionReq.LegacyLabels.RType], gpp)
 	if err != nil {
 		errs = append(errs, err)
 	}
 
-	lmtEnforcer := extractLMT(req.BidRequest, privacyConfig)
+	lmtEnforcer := extractLMT(req.BidRequest, rs.privacyConfig)
 
 	// request level privacy policies
 	privacyEnforcement := privacy.Enforcement{
@@ -116,7 +120,7 @@ func cleanOpenRTBRequests(ctx context.Context,
 	privacyLabels.COPPAEnforced = privacyEnforcement.COPPA
 	privacyLabels.LMTEnforced = lmtEnforcer.ShouldEnforce(unknownBidder)
 
-	tcf2Cfg := tcf2ConfigBuilder(privacyConfig.GDPR.TCF2, auctionReq.Account.GDPR)
+	tcf2Cfg := rs.tcf2ConfigBuilder(rs.privacyConfig.GDPR.TCF2, auctionReq.Account.GDPR)
 
 	var gdprEnforced bool
 	var gdprPerms gdpr.Permissions = &gdpr.AlwaysAllow{}
@@ -139,7 +143,7 @@ func cleanOpenRTBRequests(ctx context.Context,
 			GDPRSignal:  gdprSignal,
 			PublisherID: auctionReq.LegacyLabels.PubID,
 		}
-		gdprPerms = gdprPermsBuilder(tcf2Cfg, gdprRequestInfo)
+		gdprPerms = rs.gdprPermsBuilder(tcf2Cfg, gdprRequestInfo)
 	}
 
 	// bidder level privacy policies
@@ -163,7 +167,7 @@ func cleanOpenRTBRequests(ctx context.Context,
 			}
 
 			if !bidRequestAllowed {
-				metricsEngine.RecordAdapterGDPRRequestBlocked(bidderRequest.BidderCoreName)
+				rs.me.RecordAdapterGDPRRequestBlocked(bidderRequest.BidderCoreName)
 			}
 		}
 
diff --git a/go.mod b/go.mod
index 90f87507..f3e3cdab 100644
--- a/go.mod
+++ b/go.mod
@@ -30,8 +30,8 @@ require (
 	github.com/vrischmann/go-metrics-influxdb v0.1.1
 	github.com/xeipuuv/gojsonschema v1.2.0
 	github.com/yudai/gojsondiff v1.0.0
-	golang.org/x/net v0.0.0-20220909164309-bea034e7d591
-	golang.org/x/text v0.3.7
+	golang.org/x/net v0.5.0
+	golang.org/x/text v0.6.0
 	google.golang.org/grpc v1.46.2
 	gopkg.in/evanphx/json-patch.v4 v4.12.0
 	gopkg.in/yaml.v3 v3.0.1
@@ -67,7 +67,7 @@ require (
 	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
 	github.com/yudai/pp v2.0.1+incompatible // indirect
 	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
-	golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
+	golang.org/x/sys v0.4.0 // indirect
 	google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect
 	google.golang.org/protobuf v1.28.0 // indirect
 	gopkg.in/ini.v1 v1.66.4 // indirect
diff --git a/go.sum b/go.sum
index 30f3017b..2e795639 100644
--- a/go.sum
+++ b/go.sum
@@ -29,18 +29,12 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW
 cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
 cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
 cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
-cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
 cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
 cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
 cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
-cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
-cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
-cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
-cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
 cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
@@ -90,7 +84,6 @@ github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMU
 github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
@@ -118,13 +111,11 @@ github.com/coocood/freecache v1.2.1/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
 github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -142,7 +133,6 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
 github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
 github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
-github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
@@ -221,7 +211,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -249,14 +238,9 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
 github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
-github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
-github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
-github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
-github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
 github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
@@ -266,7 +250,6 @@ github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9
 github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
 github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
 github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
-github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
@@ -295,7 +278,6 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
 github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
 github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
 github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
-github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
 github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
 github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
 github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
@@ -329,11 +311,9 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
-github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
 github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
@@ -423,7 +403,6 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
 github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
 github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
 github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
-github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
 github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
 github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -450,7 +429,6 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqn
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
-github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
 github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -458,7 +436,6 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
 github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
 github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
-github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
 github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
@@ -520,12 +497,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
-go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
 go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
-go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
 go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
-go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU=
-go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -637,13 +610,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
-golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
+golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -661,9 +629,6 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ
 golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -675,7 +640,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -755,18 +719,11 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
+golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -778,8 +735,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
+golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -843,8 +801,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
 google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -878,14 +834,6 @@ google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdr
 google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
 google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
 google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
-google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
-google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
-google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
-google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
-google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
-google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
-google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
-google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -959,20 +907,7 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6
 google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
-google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
 google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I=
 google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
 google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
@@ -1005,7 +940,6 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
 google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
 google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ=
 google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
@@ -1052,7 +986,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1065,4 +998,3 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/openrtb_ext/bid_response_video.go b/openrtb_ext/bid_response_video.go
index 22661547..3d677772 100644
--- a/openrtb_ext/bid_response_video.go
+++ b/openrtb_ext/bid_response_video.go
@@ -17,4 +17,5 @@ type VideoTargeting struct {
 	HbPb       string `json:"hb_pb,omitempty"`
 	HbPbCatDur string `json:"hb_pb_cat_dur,omitempty"`
 	HbCacheID  string `json:"hb_cache_id,omitempty"`
+	HbDeal     string `json:"hb_deal,omitempty"`
 }
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 8f0fce6e..675beafc 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -105,6 +105,7 @@ const (
 	BidderAdpone            BidderName = "adpone"
 	BidderAdprime           BidderName = "adprime"
 	BidderAdrino            BidderName = "adrino"
+	BidderAdsinteractive    BidderName = "adsinteractive"
 	BidderAdtarget          BidderName = "adtarget"
 	BidderAdtrgtme          BidderName = "adtrgtme"
 	BidderAdtelligent       BidderName = "adtelligent"
@@ -136,6 +137,7 @@ const (
 	BidderBlue              BidderName = "blue"
 	BidderBmtm              BidderName = "bmtm"
 	BidderBoldwin           BidderName = "boldwin"
+	BidderBrave             BidderName = "brave"
 	BidderBrightroll        BidderName = "brightroll"
 	BidderCcx               BidderName = "ccx"
 	BidderCoinzilla         BidderName = "coinzilla"
@@ -180,6 +182,7 @@ const (
 	BidderKargo             BidderName = "kargo"
 	BidderKayzen            BidderName = "kayzen"
 	BidderKidoz             BidderName = "kidoz"
+	BidderKiviads           BidderName = "kiviads"
 	BidderKrushmedia        BidderName = "krushmedia"
 	BidderKubient           BidderName = "kubient"
 	BidderLockerDome        BidderName = "lockerdome"
@@ -279,6 +282,7 @@ func CoreBidderNames() []BidderName {
 		BidderAdpone,
 		BidderAdprime,
 		BidderAdrino,
+		BidderAdsinteractive,
 		BidderAdtarget,
 		BidderAdtrgtme,
 		BidderAdtelligent,
@@ -310,6 +314,7 @@ func CoreBidderNames() []BidderName {
 		BidderBlue,
 		BidderBmtm,
 		BidderBoldwin,
+		BidderBrave,
 		BidderBrightroll,
 		BidderCcx,
 		BidderCoinzilla,
@@ -354,6 +359,7 @@ func CoreBidderNames() []BidderName {
 		BidderKargo,
 		BidderKayzen,
 		BidderKidoz,
+		BidderKiviads,
 		BidderKrushmedia,
 		BidderKubient,
 		BidderLockerDome,
diff --git a/openrtb_ext/imp_adsinteractive.go b/openrtb_ext/imp_adsinteractive.go
new file mode 100644
index 00000000..c382b2d6
--- /dev/null
+++ b/openrtb_ext/imp_adsinteractive.go
@@ -0,0 +1,5 @@
+package openrtb_ext
+
+type ImpExtAdsinteractive struct {
+	AdUnit string `json:"adUnit"`
+}
diff --git a/openrtb_ext/imp_brave.go b/openrtb_ext/imp_brave.go
new file mode 100644
index 00000000..2c7a0eaf
--- /dev/null
+++ b/openrtb_ext/imp_brave.go
@@ -0,0 +1,5 @@
+package openrtb_ext
+
+type ExtImpBrave struct {
+	PlacementID string `json:"placementId"`
+}
diff --git a/openrtb_ext/imp_kiviads.go b/openrtb_ext/imp_kiviads.go
new file mode 100644
index 00000000..b5e02a67
--- /dev/null
+++ b/openrtb_ext/imp_kiviads.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ImpExtKiviads struct {
+	PlacementID string `json:"placementId"`
+	EndpointID  string `json:"endpointId"`
+}
diff --git a/openrtb_ext/response.go b/openrtb_ext/response.go
index e920ee58..9c76744f 100644
--- a/openrtb_ext/response.go
+++ b/openrtb_ext/response.go
@@ -39,10 +39,11 @@ type ExtResponseSyncData struct {
 
 // ExtResponsePrebid defines the contract for bidresponse.ext.prebid
 type ExtResponsePrebid struct {
-	AuctionTimestamp int64           `json:"auctiontimestamp,omitempty"`
-	Passthrough      json.RawMessage `json:"passthrough,omitempty"`
-	Modules          json.RawMessage `json:"modules,omitempty"`
-	Fledge           *Fledge         `json:"fledge,omitempty"`
+	AuctionTimestamp int64             `json:"auctiontimestamp,omitempty"`
+	Passthrough      json.RawMessage   `json:"passthrough,omitempty"`
+	Modules          json.RawMessage   `json:"modules,omitempty"`
+	Fledge           *Fledge           `json:"fledge,omitempty"`
+	Targeting        map[string]string `json:"targeting,omitempty"`
 }
 
 // FledgeResponse defines the contract for bidresponse.ext.fledge
diff --git a/static/bidder-info/adsinteractive.yaml b/static/bidder-info/adsinteractive.yaml
new file mode 100644
index 00000000..20a64bf2
--- /dev/null
+++ b/static/bidder-info/adsinteractive.yaml
@@ -0,0 +1,11 @@
+endpoint: "http://bid.adsinteractive.com/prebid"
+maintainer:
+  email: it@adsinteractive.com
+modifyingVastXmlAllowed: false
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+  site:
+    mediaTypes:
+      - banner
\ No newline at end of file
diff --git a/static/bidder-info/appnexus.yaml b/static/bidder-info/appnexus.yaml
index 06afe454..6686e7ac 100644
--- a/static/bidder-info/appnexus.yaml
+++ b/static/bidder-info/appnexus.yaml
@@ -1,7 +1,7 @@
 endpoint: "http://ib.adnxs.com/openrtb2"
 platform_id: "5"
 maintainer:
-  email: "prebid-server@xandr.com"
+  email: "prebid@microsoft.com"
 gvlVendorID: 32
 capabilities:
   app:
diff --git a/static/bidder-info/bliink.yaml b/static/bidder-info/bliink.yaml
index 8afacff1..bcdacc42 100644
--- a/static/bidder-info/bliink.yaml
+++ b/static/bidder-info/bliink.yaml
@@ -1,4 +1,4 @@
-endpoint: "http://engine.bliink.io/bid"
+endpoint: "https://engine.bliink.io/bid"
 maintainer:
   email: "support@bliink.io"
 gvlVendorID: 658
diff --git a/static/bidder-info/brave.yaml b/static/bidder-info/brave.yaml
new file mode 100644
index 00000000..1e4b7ced
--- /dev/null
+++ b/static/bidder-info/brave.yaml
@@ -0,0 +1,15 @@
+endpoint: "http://point.braveglobal.tv/?t=3&partner={{.PublisherID}}"
+maintainer:
+  email: "support@thebrave.io"
+gvlVendorID: 869
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
diff --git a/static/bidder-info/kiviads.yaml b/static/bidder-info/kiviads.yaml
new file mode 100644
index 00000000..6349060c
--- /dev/null
+++ b/static/bidder-info/kiviads.yaml
@@ -0,0 +1,18 @@
+endpoint: "http://lb.kiviads.com/pserver"
+maintainer:
+  email: "prebid@kiviads.com"
+capabilities:
+  site:
+    mediaTypes:
+    - banner
+    - video
+    - native
+  app:
+    mediaTypes:
+    - banner
+    - video
+    - native
+userSync:
+  redirect:
+    url: "https://sync.kiviads.com/pserver?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&ccpa={{.USPrivacy}}&redir={{.RedirectURL}}"
+    userMacro: "[UID]"
diff --git a/static/bidder-info/mobilefuse.yaml b/static/bidder-info/mobilefuse.yaml
index ac0f8929..e1474f77 100644
--- a/static/bidder-info/mobilefuse.yaml
+++ b/static/bidder-info/mobilefuse.yaml
@@ -7,4 +7,5 @@ capabilities:
     mediaTypes:
       - banner
       - video
+      - native
 endpointCompression: "GZIP"
\ No newline at end of file
diff --git a/static/bidder-info/synacormedia.yaml b/static/bidder-info/synacormedia.yaml
index 47d66458..c19d7674 100644
--- a/static/bidder-info/synacormedia.yaml
+++ b/static/bidder-info/synacormedia.yaml
@@ -12,11 +12,4 @@ capabilities:
       - banner
       - video
 userSync:
-  key: "imds"
-  supportCors: true
-  iframe:
-    url: "https://ad-cdn.technoratimedia.com/html/usersync.html?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&cb={{.RedirectURL}}"
-    userMacro: "[USER_ID]"
-  redirect:
-    url: "https://sync.technoratimedia.com/services?srv=cs&gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&cb={{.RedirectURL}}"
-    userMacro: "[USER_ID]"
+  key: "imds"
\ No newline at end of file
diff --git a/static/bidder-params/adsinteractive.json b/static/bidder-params/adsinteractive.json
new file mode 100644
index 00000000..662c7362
--- /dev/null
+++ b/static/bidder-params/adsinteractive.json
@@ -0,0 +1,13 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Adsinteractive Adapter Params",
+  "description": "Adsinteractive Bidder Parameter Schema",
+  "type": "object",
+  "properties": {
+    "adUnit": {
+      "type": "string",
+      "description": "ad unit name"
+    }
+  },
+  "required": ["adUnit"]
+}
diff --git a/static/bidder-params/brave.json b/static/bidder-params/brave.json
new file mode 100644
index 00000000..8e913982
--- /dev/null
+++ b/static/bidder-params/brave.json
@@ -0,0 +1,13 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Brave Adapter Params",
+  "description": "A schema which validates params accepted by the Brave adapter",
+  "type": "object",
+  "properties": {
+    "placementId": {
+      "type": "string",
+      "description": "Unique Placement ID of publisher"
+    }
+  },
+  "required": ["placementId"]
+}
\ No newline at end of file
diff --git a/static/bidder-params/kiviads.json b/static/bidder-params/kiviads.json
new file mode 100644
index 00000000..0ebe7eae
--- /dev/null
+++ b/static/bidder-params/kiviads.json
@@ -0,0 +1,22 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "Kivi Adapter Params",
+    "description": "A schema which validates params accepted by the Kivi adapter",
+    "type": "object",
+    "properties": {
+      "placementId": {
+        "type": "string",
+        "minLength": 1,
+        "description": "Placement ID"
+      },
+      "endpointId": {
+        "type": "string",
+        "minLength": 1,
+        "description": "Endpoint ID"
+      }
+    },
+    "oneOf": [
+      { "required": ["placementId"] },
+      { "required": ["endpointId"] }
+    ]
+  }
\ No newline at end of file


Test patch:
diff --git a/adapters/adnuntius/adnuntius_test.go b/adapters/adnuntius/adnuntius_test.go
index 27e006e1..9c431c2a 100644
--- a/adapters/adnuntius/adnuntius_test.go
+++ b/adapters/adnuntius/adnuntius_test.go
@@ -13,7 +13,10 @@ import (
 
 func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderAdnuntius, config.Adapter{
-		Endpoint: "http://whatever.url"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+		Endpoint:         "http://whatever.url",
+		ExtraAdapterInfo: "http://gdpr.url",
+	},
+		config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
@@ -43,5 +46,4 @@ func AssignDefaultValues(bidder adapters.Bidder) {
 	bidderAdnuntius.time = &FakeTime{
 		time: time.Date(2016, 1, 1, 12, 30, 15, 0, time.UTC),
 	}
-	bidderAdnuntius.extraInfo = "http://gdpr.url"
 }
diff --git a/adapters/adsinteractive/adsinteractive_test.go b/adapters/adsinteractive/adsinteractive_test.go
new file mode 100644
index 00000000..9a1397b7
--- /dev/null
+++ b/adapters/adsinteractive/adsinteractive_test.go
@@ -0,0 +1,23 @@
+package adsinteractive
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+const testsDir = "adsinteractivetest"
+const testsBidderEndpoint = "http://bid.adsinteractive.com/prebid"
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderAdsinteractive, config.Adapter{
+		Endpoint: testsBidderEndpoint}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, testsDir, bidder)
+}
diff --git a/adapters/adsinteractive/adsinteractivetest/exemplary/banner.json b/adapters/adsinteractive/adsinteractivetest/exemplary/banner.json
new file mode 100644
index 00000000..53b96fbe
--- /dev/null
+++ b/adapters/adsinteractive/adsinteractivetest/exemplary/banner.json
@@ -0,0 +1,89 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "https://good.site/url"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "ext": {
+          "bidder": {
+            "adUnit": "fake-adunit-name"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://bid.adsinteractive.com/prebid",
+        "body": {
+          "id": "test-request-id",
+          "site": {
+            "page": "https://good.site/url"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "ext": {
+                "bidder": {
+                  "adUnit": "fake-adunit-name"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "adsinteractive",
+              "bid": [
+                {
+                  "id": "randomid",
+                  "impid": "test-imp-id",
+                  "price": 0.72,
+                  "adid": "12345678",
+                  "adm": "some-test-ad",
+                  "cid": "987",
+                  "crid": "12345678",
+                  "h": 250,
+                  "w": 300
+                }
+              ]
+            }
+          ],
+          "cur": "EUR"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "id": "randomid",
+            "impid": "test-imp-id",
+            "price": 0.72,
+            "adm": "some-test-ad",
+            "adid": "12345678",
+            "cid": "987",
+            "crid": "12345678",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/adsinteractive/adsinteractivetest/supplemental/bad-request-400.json b/adapters/adsinteractive/adsinteractivetest/supplemental/bad-request-400.json
new file mode 100644
index 00000000..695f2fb0
--- /dev/null
+++ b/adapters/adsinteractive/adsinteractivetest/supplemental/bad-request-400.json
@@ -0,0 +1,64 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adUnit": "fake-adunit-name"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      
+      "expectedRequest": {
+        "uri": "http://bid.adsinteractive.com/prebid",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "adUnit": "fake-adunit-name"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 400,
+        "body": {}
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adsinteractive/adsinteractivetest/supplemental/bad-response.json b/adapters/adsinteractive/adsinteractivetest/supplemental/bad-response.json
new file mode 100644
index 00000000..98acce17
--- /dev/null
+++ b/adapters/adsinteractive/adsinteractivetest/supplemental/bad-response.json
@@ -0,0 +1,63 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adUnit": "fake-adunit-name"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://bid.adsinteractive.com/prebid",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "adUnit": "fake-adunit-name"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": ""
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adsinteractive/adsinteractivetest/supplemental/no-content-204.json b/adapters/adsinteractive/adsinteractivetest/supplemental/no-content-204.json
new file mode 100644
index 00000000..9b24dca0
--- /dev/null
+++ b/adapters/adsinteractive/adsinteractivetest/supplemental/no-content-204.json
@@ -0,0 +1,56 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "fake-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://bid.adsinteractive.com/prebid",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "fake-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 204,
+        "body": {}
+      }
+    }
+  ],
+
+  "expectedBidResponses": []
+}
diff --git a/adapters/adsinteractive/adsinteractivetest/supplemental/service-unavailable-503.json b/adapters/adsinteractive/adsinteractivetest/supplemental/service-unavailable-503.json
new file mode 100644
index 00000000..42e259b8
--- /dev/null
+++ b/adapters/adsinteractive/adsinteractivetest/supplemental/service-unavailable-503.json
@@ -0,0 +1,63 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adUnit": "fake-adunit-name"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://bid.adsinteractive.com/prebid",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "adUnit": "fake-adunit-name"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 503,
+        "body": {}
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 503. Run with request.debug = 1 for more info.",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/adsinteractive/params_test.go b/adapters/adsinteractive/params_test.go
new file mode 100644
index 00000000..2561fc86
--- /dev/null
+++ b/adapters/adsinteractive/params_test.go
@@ -0,0 +1,55 @@
+package adsinteractive
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/adsinteractive.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.BidderAdsinteractive, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected adsinteractive params: %s", validParam)
+		}
+	}
+}
+
+// TestInvalidParams makes sure that the Adsinteractive schema rejects all the imp.ext fields we don't support.
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderAdsinteractive, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"adUnit": "Example_ad_unit_1"}`,
+}
+
+var invalidParams = []string{
+	`{"adunit": "Example_ad_unit_1"}`,
+	`{"AdUnit": "Example_ad_unit_1"}`,
+	`{"ad_unit": Example_ad_unit_1}`,
+	``,
+	`null`,
+	`true`,
+	`[]`,
+	`{}`,
+	`nil`,
+	`53`,
+	`9.1`,
+}
diff --git a/adapters/brave/brave_test.go b/adapters/brave/brave_test.go
new file mode 100644
index 00000000..e8d0a474
--- /dev/null
+++ b/adapters/brave/brave_test.go
@@ -0,0 +1,30 @@
+package brave
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderBrave, config.Adapter{
+		Endpoint: "http://point.braveglobal.tv/?t=3&partner={{.PublisherID}}"},
+		config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "bravetest", bidder)
+}
+
+func TestEndpointTemplateMalformed(t *testing.T) {
+	_, buildErr := Builder(openrtb_ext.BidderBrave, config.Adapter{
+		Endpoint: "{{Malformed}}"},
+		config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+	assert.Error(t, buildErr)
+}
diff --git a/adapters/brave/bravetest/exemplary/banner-app.json b/adapters/brave/bravetest/exemplary/banner-app.json
new file mode 100644
index 00000000..71a30978
--- /dev/null
+++ b/adapters/brave/bravetest/exemplary/banner-app.json
@@ -0,0 +1,140 @@
+{
+    "mockBidRequest": {
+      "id": "request-id",
+      "app": {
+        "publisher": {
+          "id": "123456789"
+        },
+        "cat": [
+          "IAB9-1"
+        ],
+        "bundle": "com.app.test",
+        "name": "Test App",
+        "id": "12345678"
+      },
+      "device": {
+        "ua": "useragent",
+        "ip": "100.100.100.100",
+        "language": "en"
+      },
+      "tmax": 1000,
+      "user": {
+        "id": "some-user"
+      },
+      "imp": [
+        {
+          "id": "impression-id",
+          "tagid": "tid",
+          "banner": {
+            "w":320,
+            "h":50
+          },
+          "ext": {
+            "bidder": {
+              "placementId": "f897beb0daba0253d8e59a098eef9311"
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+    {
+        "expectedRequest": {
+          "headers": {
+            "Content-Type": [
+              "application/json;charset=utf-8"
+            ],
+            "Accept": [
+              "application/json"
+            ]
+          },
+          "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+          "body": {
+            "id": "request-id",
+            "device": {
+              "ua": "useragent",
+              "ip": "100.100.100.100",
+              "language": "en"
+            },
+            "imp": [
+              {
+                "id": "impression-id",
+                "banner": {
+                    "w":320,
+                    "h":50
+                },
+                "tagid": "tid"
+              }
+            ],
+            "app": {
+              "publisher": {
+                "id": "123456789"
+              },
+              "cat": [
+                "IAB9-1"
+              ],
+              "bundle": "com.app.test",
+              "name": "Test App",
+              "id": "12345678"
+            },
+            "user": {
+              "id": "some-user"
+            },
+            "tmax": 1000
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "id": "resp-id",
+            "seatbid": [
+              {
+                "bid": [
+                  {
+                    "id": "123456789",
+                    "impid": "impression-id",
+                    "price": 2,
+                    "adm": "adm code",
+                    "adomain": [
+                      "testdomain.com"
+                    ],
+                    "crid": "100",
+                    "w":320,
+                    "h":50
+                  }
+                ],
+              "type": "banner",
+                "seat": "brave"
+              }
+            ],
+            "cur": "USD",
+            "ext": {
+              "responsetimemillis": {
+                "brave": 120
+              },
+              "tmaxrequest": 1000
+            }
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "bids": [{
+          "bid": {
+            "id": "123456789",
+            "impid": "impression-id",
+            "price": 2,
+            "adm": "adm code",
+            "adomain": [
+              "testdomain.com"
+            ],
+            "crid": "100",
+            "w":320,
+            "h":50
+          },
+          "type": "banner"
+        }]
+      }
+    ]
+  }
diff --git a/adapters/brave/bravetest/exemplary/banner-web.json b/adapters/brave/bravetest/exemplary/banner-web.json
new file mode 100644
index 00000000..0dd6dde0
--- /dev/null
+++ b/adapters/brave/bravetest/exemplary/banner-web.json
@@ -0,0 +1,138 @@
+{
+  "mockBidRequest": {
+    "id": "request-id",
+    "site": {
+      "page": "test.com/page",
+      "domain": "test.com",
+      "cat": [
+        "IAB9-1"
+      ],
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "device": {
+      "ua": "useragent",
+      "ip": "100.100.100.100",
+      "language": "en"
+    },
+    "tmax": 1000,
+    "user": {
+      "id": "some-user"
+    },
+    "imp": [
+      {
+        "id": "impression-id",
+        "tagid": "tid",
+        "banner": {
+          "w":320,
+          "h":50
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "f897beb0daba0253d8e59a098eef9311"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+        "body": {
+          "id": "request-id",
+          "device": {
+            "ua": "useragent",
+            "ip": "100.100.100.100",
+            "language": "en"
+          },
+          "imp": [
+            {
+              "id": "impression-id",
+              "banner": {
+                  "w":320,
+                  "h":50
+              },
+              "tagid": "tid"
+            }
+          ],
+          "site": {
+            "page": "test.com/page",
+            "domain": "test.com",
+            "cat": [
+              "IAB9-1"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "id": "some-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "123456789",
+                  "impid": "impression-id",
+                  "price": 2,
+                  "adm": "adm code",
+                  "adomain": [
+                    "testdomain.com"
+                  ],
+                  "crid": "100",
+                  "w":320,
+                  "h":50
+                }
+              ],
+            "type": "banner",
+              "seat": "brave"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+            "responsetimemillis": {
+              "brave": 120
+            },
+            "tmaxrequest": 1000
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [{
+        "bid": {
+          "id": "123456789",
+          "impid": "impression-id",
+          "price": 2,
+          "adm": "adm code",
+          "adomain": [
+            "testdomain.com"
+          ],
+          "crid": "100",
+          "w":320,
+          "h":50
+        },
+        "type": "banner"
+      }]
+    }
+  ]
+}
diff --git a/adapters/brave/bravetest/exemplary/native-app.json b/adapters/brave/bravetest/exemplary/native-app.json
new file mode 100644
index 00000000..05ac5aa8
--- /dev/null
+++ b/adapters/brave/bravetest/exemplary/native-app.json
@@ -0,0 +1,137 @@
+{
+    "mockBidRequest": {
+      "id": "request-id",
+      "app": {
+        "publisher": {
+          "id": "123456789"
+        },
+        "cat": [
+          "IAB9-1"
+        ],
+        "bundle": "com.app.test",
+        "name": "Test App",
+        "id": "12345678"
+      },
+      "device": {
+        "ua": "useragent",
+        "ip": "100.100.100.100",
+        "language": "en"
+      },
+      "tmax": 1000,
+      "user": {
+        "id": "some-user"
+      },
+      "imp": [
+        {
+          "id": "impression-id",
+          "tagid": "tid",
+          "native": {
+            "ver":"1.1",
+            "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+          },
+	        "ext": {
+            "bidder": {
+              "placementId": "f897beb0daba0253d8e59a098eef9311"
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+    {
+        "expectedRequest": {
+            "headers": {
+              "Content-Type": [
+                "application/json;charset=utf-8"
+              ],
+              "Accept": [
+                "application/json"
+              ]
+            },
+            "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+            "body": {
+              "id": "request-id",
+              "device": {
+                "ua": "useragent",
+                "ip": "100.100.100.100",
+                "language": "en"
+              },
+              "imp": [
+                {
+                  "id": "impression-id",
+                  "native": {
+                    "ver":"1.1",
+                    "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+                  },
+                  "tagid": "tid"
+                }
+              ],
+              "app": {
+                "publisher": {
+                  "id": "123456789"
+                },
+                "cat": [
+                  "IAB9-1"
+                ],
+                "bundle": "com.app.test",
+                "name": "Test App",
+                "id": "12345678"
+              },
+              "user": {
+                "id": "some-user"
+              },
+              "tmax": 1000
+            }
+        },
+        "mockResponse": {
+
+          "status": 200,
+          "body": {
+            "id": "resp-id",
+            "seatbid": [
+              {
+                "bid": [
+                  {
+                    "id": "123456789",
+                    "impid": "impression-id",
+                    "price": 2,
+                    "adm": "adm code",
+                    "adomain": [
+                      "testdomain.com"
+                    ],
+                    "crid": "100"
+                  }
+                ],
+                "type": "native",
+                "seat": "brave"
+              }
+            ],
+            "cur": "USD",
+            "ext": {
+              "responsetimemillis": {
+                "brave": 120
+              },
+              "tmaxrequest": 1000
+            }
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "bids": [{
+          "bid": {
+            "id": "123456789",
+            "impid": "impression-id",
+            "price": 2,
+            "adm": "adm code",
+            "adomain": [
+              "testdomain.com"
+            ],
+            "crid": "100"
+          },
+          "type": "native"
+        }]
+      }
+    ]
+  }
diff --git a/adapters/brave/bravetest/exemplary/native-web.json b/adapters/brave/bravetest/exemplary/native-web.json
new file mode 100644
index 00000000..01126e46
--- /dev/null
+++ b/adapters/brave/bravetest/exemplary/native-web.json
@@ -0,0 +1,135 @@
+{
+    "mockBidRequest": {
+      "id": "request-id",
+      "site": {
+        "page": "test.com/page",
+        "domain": "test.com",
+        "cat": [
+          "IAB9-1"
+        ],
+        "publisher": {
+          "id": "123456789"
+        }
+      },
+      "device": {
+        "ua": "useragent",
+        "ip": "100.100.100.100",
+        "language": "en"
+      },
+      "tmax": 1000,
+      "user": {
+        "id": "some-user"
+      },
+      "imp": [
+        {
+          "id": "impression-id",
+          "tagid": "tid",
+          "native": {
+            "ver":"1.1",
+            "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+          },
+	        "ext": {
+            "bidder": {
+              "placementId": "f897beb0daba0253d8e59a098eef9311"
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+    {
+        "expectedRequest": {
+            "headers": {
+              "Content-Type": [
+                "application/json;charset=utf-8"
+              ],
+              "Accept": [
+                "application/json"
+              ]
+            },
+            "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+            "body": {
+              "id": "request-id",
+              "device": {
+                "ua": "useragent",
+                "ip": "100.100.100.100",
+                "language": "en"
+              },
+              "imp": [
+                {
+                  "id": "impression-id",
+                  "native": {
+                    "ver":"1.1",
+                    "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+                  },
+                  "tagid": "tid"
+                }
+              ],
+              "site": {
+                "page": "test.com/page",
+                "domain": "test.com",
+                "cat": [
+                  "IAB9-1"
+                ],
+                "publisher": {
+                  "id": "123456789"
+                }
+              },
+              "user": {
+                "id": "some-user"
+              },
+              "tmax": 1000
+            }
+        },
+        "mockResponse": {
+
+          "status": 200,
+          "body": {
+            "id": "resp-id",
+            "seatbid": [
+              {
+                "bid": [
+                  {
+                    "id": "123456789",
+                    "impid": "impression-id",
+                    "price": 2,
+                    "adm": "adm code",
+                    "adomain": [
+                      "testdomain.com"
+                    ],
+                    "crid": "100"
+                  }
+                ],
+                "type": "native",
+                "seat": "brave"
+              }
+            ],
+            "cur": "USD",
+            "ext": {
+              "responsetimemillis": {
+                "brave": 120
+              },
+              "tmaxrequest": 1000
+            }
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "bids": [{
+          "bid": {
+            "id": "123456789",
+            "impid": "impression-id",
+            "price": 2,
+            "adm": "adm code",
+            "adomain": [
+              "testdomain.com"
+            ],
+            "crid": "100"
+          },
+          "type": "native"
+        }]
+      }
+    ]
+  }
diff --git a/adapters/brave/bravetest/exemplary/video-app.json b/adapters/brave/bravetest/exemplary/video-app.json
new file mode 100644
index 00000000..7f415109
--- /dev/null
+++ b/adapters/brave/bravetest/exemplary/video-app.json
@@ -0,0 +1,150 @@
+{
+  "mockBidRequest": {
+    "id": "request-id",
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB9-1"
+      ],
+      "bundle": "com.app.test",
+      "name": "Test App",
+      "id": "12345678"
+    },
+    "device": {
+      "ua": "useragent",
+      "ip": "100.100.100.100",
+      "language": "en"
+    },
+    "tmax": 1000,
+    "user": {
+      "id": "some-user"
+    },
+    "imp": [
+      {
+        "id": "impression-id",
+        "tagid": "tid",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 30,
+          "maxduration": 150
+        },
+	      "ext": {
+            "bidder": {
+              "placementId": "f897beb0daba0253d8e59a098eef9311"
+            }
+          }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+        "body": {
+          "id": "request-id",
+          "device": {
+            "ua": "useragent",
+            "ip": "100.100.100.100",
+            "language": "en"
+          },
+          "imp": [
+            {
+              "id": "impression-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 30,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              },
+              "tagid": "tid"
+            }
+          ],
+          "app": {
+            "publisher": {
+              "id": "123456789"
+            },
+            "cat": [
+              "IAB9-1"
+            ],
+            "bundle": "com.app.test",
+            "name": "Test App",
+            "id": "12345678"
+          },
+          "user": {
+            "id": "some-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "123456789",
+                  "impid": "impression-id",
+                  "price": 2,
+                  "adm": "adm code",
+                  "adomain": [
+                    "testdomain.com"
+                  ],
+                  "crid": "100",
+                  "w": 1280,
+                  "h": 720
+                }
+              ],
+            "type": "video",
+            "seat": "brave"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+            "responsetimemillis": {
+              "brave": 120
+            },
+            "tmaxrequest": 1000
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [{
+        "bid": {
+          "id": "123456789",
+          "impid": "impression-id",
+          "price": 2,
+          "adm": "adm code",
+          "adomain": [
+            "testdomain.com"
+          ],
+          "crid": "100",
+          "w": 1280,
+          "h": 720
+        },
+        "type": "video"
+      }]
+    }
+  ]
+}
diff --git a/adapters/brave/bravetest/exemplary/video-web.json b/adapters/brave/bravetest/exemplary/video-web.json
new file mode 100644
index 00000000..5da8ff35
--- /dev/null
+++ b/adapters/brave/bravetest/exemplary/video-web.json
@@ -0,0 +1,148 @@
+{
+  "mockBidRequest": {
+    "id": "request-id",
+    "site": {
+      "page": "test.com/page",
+      "domain": "test.com",
+      "cat": [
+        "IAB9-1"
+      ],
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "device": {
+      "ua": "useragent",
+      "ip": "100.100.100.100",
+      "language": "en"
+    },
+    "tmax": 1000,
+    "user": {
+      "id": "some-user"
+    },
+    "imp": [
+      {
+        "id": "impression-id",
+        "tagid": "tid",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 30,
+          "maxduration": 150
+        },
+	      "ext": {
+            "bidder": {
+              "placementId": "f897beb0daba0253d8e59a098eef9311"
+            }
+          }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+        "body": {
+          "id": "request-id",
+          "device": {
+            "ua": "useragent",
+            "ip": "100.100.100.100",
+            "language": "en"
+          },
+          "imp": [
+            {
+              "id": "impression-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 30,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              },
+              "tagid": "tid"
+            }
+          ],
+          "site": {
+            "page": "test.com/page",
+            "domain": "test.com",
+            "cat": [
+              "IAB9-1"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "id": "some-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "123456789",
+                  "impid": "impression-id",
+                  "price": 2,
+                  "adm": "adm code",
+                  "adomain": [
+                    "testdomain.com"
+                  ],
+                  "crid": "100",
+                  "w": 1280,
+                  "h": 720
+                }
+              ],
+            "type": "video",
+            "seat": "brave"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+            "responsetimemillis": {
+              "brave": 120
+            },
+            "tmaxrequest": 1000
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [{
+        "bid": {
+          "id": "123456789",
+          "impid": "impression-id",
+          "price": 2,
+          "adm": "adm code",
+          "adomain": [
+            "testdomain.com"
+          ],
+          "crid": "100",
+          "w": 1280,
+          "h": 720
+        },
+        "type": "video"
+      }]
+    }
+  ]
+}
diff --git a/adapters/brave/bravetest/supplemental/empty-seatbid-array.json b/adapters/brave/bravetest/supplemental/empty-seatbid-array.json
new file mode 100644
index 00000000..c407c822
--- /dev/null
+++ b/adapters/brave/bravetest/supplemental/empty-seatbid-array.json
@@ -0,0 +1,121 @@
+{
+  "mockBidRequest": {
+    "id": "request-id",
+    "site": {
+      "page": "test.com/page",
+      "domain": "test.com",
+      "cat": [
+        "IAB9-1"
+      ],
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "device": {
+      "ua": "useragent",
+      "ip": "100.100.100.100",
+      "language": "en"
+    },
+    "tmax": 1000,
+    "user": {
+      "id": "some-user"
+    },
+    "imp": [
+      {
+        "id": "impression-id",
+        "tagid": "tid",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 30,
+          "maxduration": 150
+        },
+	      "ext": {
+            "bidder": {
+              "placementId": "f897beb0daba0253d8e59a098eef9311"
+            }
+          }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+        "body": {
+          "id": "request-id",
+          "device": {
+            "ua": "useragent",
+            "ip": "100.100.100.100",
+            "language": "en"
+          },
+          "imp": [
+            {
+              "id": "impression-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 30,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              },
+              "tagid": "tid"
+            }
+          ],
+          "site": {
+            "page": "test.com/page",
+            "domain": "test.com",
+            "cat": [
+              "IAB9-1"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "id": "some-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "resp-id",
+          "seatbid": [
+          ],
+          "cur": "USD",
+          "ext": {
+            "responsetimemillis": {
+              "brave": 120
+            },
+            "tmaxrequest": 1000
+          }
+        }
+      }
+    }
+  ],
+    "mockResponse": {
+      "status": 200,
+      "body": "invalid response"
+    },
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Empty SeatBid array",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/brave/bravetest/supplemental/invalid-brave-ext-object.json b/adapters/brave/bravetest/supplemental/invalid-brave-ext-object.json
new file mode 100644
index 00000000..899834e6
--- /dev/null
+++ b/adapters/brave/bravetest/supplemental/invalid-brave-ext-object.json
@@ -0,0 +1,29 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "ext.bidder not provided",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "request-id",
+    "imp": [
+      {
+        "id": "impression-id",
+        "tagid": "tid",
+        "video": {
+          "mimes": ["video/mp4"],
+          "w": 640,
+          "h": 480,
+          "minduration": 30,
+          "maxduration": 150
+        },
+        "ext": "invalid data"
+      }
+    ],
+    "site": {
+      "page": "test.com"
+    }
+  },
+  "httpCalls": []
+}
diff --git a/adapters/brave/bravetest/supplemental/invalid-response.json b/adapters/brave/bravetest/supplemental/invalid-response.json
new file mode 100644
index 00000000..972c2b30
--- /dev/null
+++ b/adapters/brave/bravetest/supplemental/invalid-response.json
@@ -0,0 +1,97 @@
+{
+  "mockBidRequest": {
+    "id": "request-id",
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB9-1"
+      ],
+      "bundle": "com.app.test",
+      "name": "Test App",
+      "id": "12345678"
+    },
+    "device": {
+      "ua": "useragent",
+      "ip": "100.100.100.100",
+      "language": "en"
+    },
+    "tmax": 1000,
+    "user": {
+      "id": "some-user"
+    },
+    "imp": [
+      {
+        "id": "impression-id",
+        "tagid": "tid",
+        "banner": {
+          "w":320,
+          "h":50
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "f897beb0daba0253d8e59a098eef9311"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "headers": {
+        "Content-Type": [
+          "application/json;charset=utf-8"
+        ],
+        "Accept": [
+          "application/json"
+        ]
+      },
+      "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+      "body": {
+        "id": "request-id",
+        "device": {
+          "ua": "useragent",
+          "ip": "100.100.100.100",
+          "language": "en"
+        },
+        "imp": [
+          {
+            "id": "impression-id",
+            "banner": {
+                "w":320,
+                "h":50
+            },
+            "tagid": "tid"
+          }
+        ],
+        "app": {
+          "publisher": {
+            "id": "123456789"
+          },
+          "cat": [
+            "IAB9-1"
+          ],
+          "bundle": "com.app.test",
+          "name": "Test App",
+          "id": "12345678"
+        },
+        "user": {
+          "id": "some-user"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": "invalid response"
+    }
+  }],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bad Server Response",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/brave/bravetest/supplemental/status-code-bad-request.json b/adapters/brave/bravetest/supplemental/status-code-bad-request.json
new file mode 100644
index 00000000..e80273a7
--- /dev/null
+++ b/adapters/brave/bravetest/supplemental/status-code-bad-request.json
@@ -0,0 +1,108 @@
+
+{
+  "mockBidRequest": {
+    "id": "request-id",
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB9-1"
+      ],
+      "bundle": "com.app.test",
+      "name": "Test App",
+      "id": "12345678"
+    },
+    "device": {
+      "ua": "useragent",
+      "ip": "100.100.100.100",
+      "language": "en"
+    },
+    "tmax": 1000,
+    "user": {
+      "id": "some-user"
+    },
+    "imp": [
+      {
+        "id": "impression-id",
+        "tagid": "tid",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 30,
+          "maxduration": 150
+        },
+	      "ext": {
+            "bidder": {
+              "placementId": "f897beb0daba0253d8e59a098eef9311"
+            }
+          }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "headers": {
+        "Content-Type": [
+          "application/json;charset=utf-8"
+        ],
+        "Accept": [
+          "application/json"
+        ]
+      },
+      "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+      "body": {
+        "id": "request-id",
+        "device": {
+          "ua": "useragent",
+          "ip": "100.100.100.100",
+          "language": "en"
+        },
+        "imp": [
+          {
+            "id": "impression-id",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 30,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            },
+            "tagid": "tid"
+          }
+        ],
+        "app": {
+          "publisher": {
+            "id": "123456789"
+          },
+          "cat": [
+            "IAB9-1"
+          ],
+          "bundle": "com.app.test",
+          "name": "Test App",
+          "id": "12345678"
+        },
+        "user": {
+          "id": "some-user"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 400
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/brave/bravetest/supplemental/status-code-no-content.json b/adapters/brave/bravetest/supplemental/status-code-no-content.json
new file mode 100644
index 00000000..eb369bd8
--- /dev/null
+++ b/adapters/brave/bravetest/supplemental/status-code-no-content.json
@@ -0,0 +1,108 @@
+
+{
+  "mockBidRequest": {
+    "id": "request-id",
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB9-1"
+      ],
+      "bundle": "com.app.test",
+      "name": "Test App",
+      "id": "12345678"
+    },
+    "device": {
+      "ua": "useragent",
+      "ip": "100.100.100.100",
+      "language": "en"
+    },
+    "tmax": 1000,
+    "user": {
+      "id": "some-user"
+    },
+    "imp": [
+      {
+        "id": "impression-id",
+        "tagid": "tid",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 30,
+          "maxduration": 150
+        },
+	      "ext": {
+            "bidder": {
+              "placementId": "f897beb0daba0253d8e59a098eef9311"
+            }
+          }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "headers": {
+        "Content-Type": [
+          "application/json;charset=utf-8"
+        ],
+        "Accept": [
+          "application/json"
+        ]
+      },
+      "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+      "body": {
+        "id": "request-id",
+        "device": {
+          "ua": "useragent",
+          "ip": "100.100.100.100",
+          "language": "en"
+        },
+        "imp": [
+          {
+            "id": "impression-id",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 30,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            },
+            "tagid": "tid"
+          }
+        ],
+        "app": {
+          "publisher": {
+            "id": "123456789"
+          },
+          "cat": [
+            "IAB9-1"
+          ],
+          "bundle": "com.app.test",
+          "name": "Test App",
+          "id": "12345678"
+        },
+        "user": {
+          "id": "some-user"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 204
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "No bid",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/brave/bravetest/supplemental/status-code-other-error.json b/adapters/brave/bravetest/supplemental/status-code-other-error.json
new file mode 100644
index 00000000..bd68ee85
--- /dev/null
+++ b/adapters/brave/bravetest/supplemental/status-code-other-error.json
@@ -0,0 +1,108 @@
+
+{
+  "mockBidRequest": {
+    "id": "request-id",
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB9-1"
+      ],
+      "bundle": "com.app.test",
+      "name": "Test App",
+      "id": "12345678"
+    },
+    "device": {
+      "ua": "useragent",
+      "ip": "100.100.100.100",
+      "language": "en"
+    },
+    "tmax": 1000,
+    "user": {
+      "id": "some-user"
+    },
+    "imp": [
+      {
+        "id": "impression-id",
+        "tagid": "tid",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 30,
+          "maxduration": 150
+        },
+	      "ext": {
+            "bidder": {
+              "placementId": "f897beb0daba0253d8e59a098eef9311"
+            }
+          }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "headers": {
+        "Content-Type": [
+          "application/json;charset=utf-8"
+        ],
+        "Accept": [
+          "application/json"
+        ]
+      },
+      "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+      "body": {
+        "id": "request-id",
+        "device": {
+          "ua": "useragent",
+          "ip": "100.100.100.100",
+          "language": "en"
+        },
+        "imp": [
+          {
+            "id": "impression-id",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 30,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            },
+            "tagid": "tid"
+          }
+        ],
+        "app": {
+          "publisher": {
+            "id": "123456789"
+          },
+          "cat": [
+            "IAB9-1"
+          ],
+          "bundle": "com.app.test",
+          "name": "Test App",
+          "id": "12345678"
+        },
+        "user": {
+          "id": "some-user"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 306
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Something went wrong, please contact your Account Manager. Status Code: [ 306 ] ",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/brave/bravetest/supplemental/status-code-service-unavailable.json b/adapters/brave/bravetest/supplemental/status-code-service-unavailable.json
new file mode 100644
index 00000000..f66347d8
--- /dev/null
+++ b/adapters/brave/bravetest/supplemental/status-code-service-unavailable.json
@@ -0,0 +1,107 @@
+{
+  "mockBidRequest": {
+    "id": "request-id",
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB9-1"
+      ],
+      "bundle": "com.app.test",
+      "name": "Test App",
+      "id": "12345678"
+    },
+    "device": {
+      "ua": "useragent",
+      "ip": "100.100.100.100",
+      "language": "en"
+    },
+    "tmax": 1000,
+    "user": {
+      "id": "some-user"
+    },
+    "imp": [
+      {
+        "id": "impression-id",
+        "tagid": "tid",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 30,
+          "maxduration": 150
+        },
+	      "ext": {
+            "bidder": {
+              "placementId": "f897beb0daba0253d8e59a098eef9311"
+            }
+          }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "headers": {
+        "Content-Type": [
+          "application/json;charset=utf-8"
+        ],
+        "Accept": [
+          "application/json"
+        ]
+      },
+      "uri": "http://point.braveglobal.tv/?t=3&partner=f897beb0daba0253d8e59a098eef9311",
+      "body": {
+        "id": "request-id",
+        "device": {
+          "ua": "useragent",
+          "ip": "100.100.100.100",
+          "language": "en"
+        },
+        "imp": [
+          {
+            "id": "impression-id",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 30,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            },
+            "tagid": "tid"
+          }
+        ],
+        "app": {
+          "publisher": {
+            "id": "123456789"
+          },
+          "cat": [
+            "IAB9-1"
+          ],
+          "bundle": "com.app.test",
+          "name": "Test App",
+          "id": "12345678"
+        },
+        "user": {
+          "id": "some-user"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 503
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Service Unavailable. Status Code: [ 503 ] ",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/brave/params_test.go b/adapters/brave/params_test.go
new file mode 100644
index 00000000..1120a13c
--- /dev/null
+++ b/adapters/brave/params_test.go
@@ -0,0 +1,51 @@
+package brave
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderBrave, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected Brave 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.BidderBrave, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{ "placementId": "f897beb0daba0253d8e59a098eef9311" }`,
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{"adCode": "string", "seatCode": 5, "originalPublisherid": "string"}`,
+	`{ "placementid": "f897beb0daba0253d8e59a098eef9311" }`,
+	`{ "PlacementId": "f897beb0daba0253d8e59a098eef9311" }`,
+}
diff --git a/adapters/ix/ix_test.go b/adapters/ix/ix_test.go
index a4940ef3..b962bb73 100644
--- a/adapters/ix/ix_test.go
+++ b/adapters/ix/ix_test.go
@@ -104,6 +104,56 @@ func TestIxMakeBidsWithCategoryDuration(t *testing.T) {
 	}
 }
 
+func TestIxMakeRequestWithGppString(t *testing.T) {
+	bidder := &IxAdapter{}
+	bidder.maxRequests = 2
+
+	testGppString := "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN"
+
+	mockedReq := &openrtb2.BidRequest{
+		Imp: []openrtb2.Imp{{
+			ID: "1_1",
+			Video: &openrtb2.Video{
+				W:           640,
+				H:           360,
+				MIMEs:       []string{"video/mp4"},
+				MaxDuration: 60,
+				Protocols:   []adcom1.MediaCreativeSubtype{2, 3, 5, 6},
+			},
+			Ext: json.RawMessage(
+				`{
+					"prebid": {},
+					"bidder": {
+						"siteID": 123456
+					}
+				}`,
+			)},
+		},
+		Regs: &openrtb2.Regs{
+			GPP: testGppString,
+		},
+	}
+
+	expectedRequestCount := 1
+	expectedErrorCount := 0
+	var reqInfo *adapters.ExtraRequestInfo
+
+	requests, errors := bidder.MakeRequests(mockedReq, reqInfo)
+
+	if len(requests) != expectedRequestCount {
+		t.Errorf("should have 1 request, requests=%v", requests)
+	}
+
+	if len(errors) != expectedErrorCount {
+		t.Errorf("should not have any errors, errors=%v", errors)
+	}
+
+	req := &openrtb2.BidRequest{}
+	json.Unmarshal(requests[0].Body, req)
+
+	assert.Equal(t, req.Regs.GPP, testGppString)
+}
+
 func TestBuildIxDiag(t *testing.T) {
 	testCases := []struct {
 		description     string
diff --git a/adapters/kiviads/kiviads_test.go b/adapters/kiviads/kiviads_test.go
new file mode 100644
index 00000000..41e9bcbc
--- /dev/null
+++ b/adapters/kiviads/kiviads_test.go
@@ -0,0 +1,20 @@
+package kiviads
+
+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.BidderKiviads, config.Adapter{
+		Endpoint: "http://endpoint1.kiviads.com/pserver"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "kiviadstest", bidder)
+}
diff --git a/adapters/kiviads/kiviadstest/exemplary/endpointId.json b/adapters/kiviads/kiviadstest/exemplary/endpointId.json
new file mode 100644
index 00000000..85e66896
--- /dev/null
+++ b/adapters/kiviads/kiviadstest/exemplary/endpointId.json
@@ -0,0 +1,123 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "iPad"
+        },
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "tagid": "test",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "endpointId": "test"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://endpoint1.kiviads.com/pserver",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "tagid": "test",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "endpointId": "test",
+                                    "type": "network"
+                                }
+                            }
+                        }
+                    ],
+                    "app": {
+                        "id": "1",
+                        "bundle": "com.wls.testwlsapplication"
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "iPad"
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://endpoint1.kiviads.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "w": 300,
+                                    "h": 250
+                                }
+                            ],
+                            "seat": "kiviads"
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://endpoint1.kiviads.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "w": 300,
+                        "h": 250
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/kiviads/kiviadstest/exemplary/simple-banner.json b/adapters/kiviads/kiviadstest/exemplary/simple-banner.json
new file mode 100644
index 00000000..b80f0c5f
--- /dev/null
+++ b/adapters/kiviads/kiviadstest/exemplary/simple-banner.json
@@ -0,0 +1,123 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "iPad"
+        },
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "tagid": "test",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://endpoint1.kiviads.com/pserver",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "tagid": "test",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": "test",
+                                    "type": "publisher"
+                                }
+                            }
+                        }
+                    ],
+                    "app": {
+                        "id": "1",
+                        "bundle": "com.wls.testwlsapplication"
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "iPad"
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://endpoint1.kiviads.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "w": 300,
+                                    "h": 250
+                                }
+                            ],
+                            "seat": "kiviads"
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://endpoint1.kiviads.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "w": 300,
+                        "h": 250
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/kiviads/kiviadstest/exemplary/simple-native.json b/adapters/kiviads/kiviadstest/exemplary/simple-native.json
new file mode 100644
index 00000000..043bfeeb
--- /dev/null
+++ b/adapters/kiviads/kiviadstest/exemplary/simple-native.json
@@ -0,0 +1,107 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "iPad"
+        },
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "tagid": "test",
+                "native": {
+                    "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+                    "ver": "1.1"
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://endpoint1.kiviads.com/pserver",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "tagid": "test",
+                            "native": {
+                                "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+                                "ver": "1.1"
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": "test",
+                                    "type": "publisher"
+                                }
+                            }
+                        }
+                    ],
+                    "app": {
+                        "id": "1",
+                        "bundle": "com.wls.testwlsapplication"
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "iPad"
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://endpoint1.kiviads.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "w": 300,
+                                    "h": 250
+                                }
+                            ],
+                            "seat": "kiviads"
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://endpoint1.kiviads.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "w": 300,
+                        "h": 250
+                    },
+                    "type": "native"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/kiviads/kiviadstest/exemplary/simple-video.json b/adapters/kiviads/kiviadstest/exemplary/simple-video.json
new file mode 100644
index 00000000..c14df358
--- /dev/null
+++ b/adapters/kiviads/kiviadstest/exemplary/simple-video.json
@@ -0,0 +1,118 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "iPad"
+        },
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "tagid": "test",
+                "video": {
+                    "mimes": [
+                        "video/mp4"
+                    ],
+                    "protocols": [
+                        2,
+                        5
+                    ],
+                    "w": 1024,
+                    "h": 576
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://endpoint1.kiviads.com/pserver",
+                "body": {
+                    "id": "test-request-id",
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "iPad"
+                    },
+                    "app": {
+                        "id": "1",
+                        "bundle": "com.wls.testwlsapplication"
+                    },
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "tagid": "test",
+                            "video": {
+                                "mimes": [
+                                    "video/mp4"
+                                ],
+                                "protocols": [
+                                    2,
+                                    5
+                                ],
+                                "w": 1024,
+                                "h": 576
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": "test",
+                                    "type": "publisher"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "<VAST version=\"3.0\"><Ad id=\"20001\" sequence=\"1\"><InLine><AdSystem version=\"4.0\"><![CDATA[iabtechlab]]></AdSystem><AdTitle><![CDATA[Inline Simple Ad]]></AdTitle><Impression><![CDATA[]]></Impression><Creatives><Creative id=\"5480\" sequence=\"1\"><Linear><Duration>00:01:00</Duration><MediaFiles><MediaFile id=\"5246\" delivery=\"progressive\" type=\"video/mp4\" bitrate=\"600\" width=\"640\" height=\"360\" minBitrate=\"500\" maxBitrate=\"700\" scalable=\"1\" maintainAspectRatio=\"1\" codec=\"0\"><![CDATA[https://s0.2mdn.net/4253510/google_ddm_animation_480P.mp4]]></MediaFile></MediaFiles><VideoClicks><ClickThrough id=\"blog\"><![CDATA[https://example.com]]></ClickThrough></VideoClicks></Linear></Creative></Creatives></InLine></Ad></VAST>",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid"
+                                }
+                            ],
+                            "seat": "kiviads"
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "<VAST version=\"3.0\"><Ad id=\"20001\" sequence=\"1\"><InLine><AdSystem version=\"4.0\"><![CDATA[iabtechlab]]></AdSystem><AdTitle><![CDATA[Inline Simple Ad]]></AdTitle><Impression><![CDATA[]]></Impression><Creatives><Creative id=\"5480\" sequence=\"1\"><Linear><Duration>00:01:00</Duration><MediaFiles><MediaFile id=\"5246\" delivery=\"progressive\" type=\"video/mp4\" bitrate=\"600\" width=\"640\" height=\"360\" minBitrate=\"500\" maxBitrate=\"700\" scalable=\"1\" maintainAspectRatio=\"1\" codec=\"0\"><![CDATA[https://s0.2mdn.net/4253510/google_ddm_animation_480P.mp4]]></MediaFile></MediaFiles><VideoClicks><ClickThrough id=\"blog\"><![CDATA[https://example.com]]></ClickThrough></VideoClicks></Linear></Creative></Creatives></InLine></Ad></VAST>",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid"
+                    },
+                    "type": "video"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/kiviads/kiviadstest/exemplary/simple-web-banner.json b/adapters/kiviads/kiviadstest/exemplary/simple-web-banner.json
new file mode 100644
index 00000000..7396dd99
--- /dev/null
+++ b/adapters/kiviads/kiviadstest/exemplary/simple-web-banner.json
@@ -0,0 +1,123 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "tagid": "test",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ],
+        "site": {
+            "id": "1",
+            "domain": "test.com"
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "Ubuntu"
+        }
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://endpoint1.kiviads.com/pserver",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "tagid": "test",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": "test",
+                                    "type": "publisher"
+                                }
+                            }
+                        }
+                    ],
+                    "site": {
+                        "id": "1",
+                        "domain": "test.com"
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "Ubuntu"
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "<iframe id=\"adm-banner-1\" width=\"468\" height=\"60\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://endpoint1.kiviads.com/pserver&k=bc2d316f39931a07d9a8dd249bf85fc0\"></iframe>",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "w": 468,
+                                    "h": 60
+                                }
+                            ],
+                            "seat": "kiviads"
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "<iframe id=\"adm-banner-1\" width=\"468\" height=\"60\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://endpoint1.kiviads.com/pserver&k=bc2d316f39931a07d9a8dd249bf85fc0\"></iframe>",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "w": 468,
+                        "h": 60
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/kiviads/kiviadstest/supplemental/bad_media_type.json b/adapters/kiviads/kiviadstest/supplemental/bad_media_type.json
new file mode 100644
index 00000000..9a204eab
--- /dev/null
+++ b/adapters/kiviads/kiviadstest/supplemental/bad_media_type.json
@@ -0,0 +1,81 @@
+{
+	"mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ],
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+        }
+    },
+	"httpCalls": [{
+		"expectedRequest": {
+			"uri": "http://endpoint1.kiviads.com/pserver",
+			"body": {
+                "id": "test-request-id",
+                "imp": [
+                    {
+                        "id": "test-imp-id",
+                        "ext": {
+                            "bidder": {
+                                "placementId": "test",
+                                "type": "publisher"
+                            }
+                        }
+                    }
+                ],
+                "app": {
+                    "id": "1",
+                    "bundle": "com.wls.testwlsapplication"
+                },
+                "device": {
+                    "ip": "123.123.123.123",
+                    "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+                }
+            }
+		},
+		"mockResponse": {
+			"status": 200,
+			"body": {
+                "id": "test-request-id",
+                "seatbid": [
+                    {
+                        "bid": [
+                            {
+                                "id": "test_bid_id",
+                                "impid": "test-imp-id",
+                                "price": 0.27543,
+                                "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://endpoint1.kiviads.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                                "cid": "test_cid",
+                                "crid": "test_crid",
+                                "dealid": "test_dealid",
+                                "w": 300,
+                                "h": 250
+                            }
+                        ],
+                        "seat": "kiviads"
+                    }
+                ],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedMakeBidsErrors": [
+        {
+          "value": "Failed to find impression \"test-imp-id\"",
+          "comparison": "literal"
+        }
+    ]
+}
diff --git a/adapters/kiviads/kiviadstest/supplemental/bad_response.json b/adapters/kiviads/kiviadstest/supplemental/bad_response.json
new file mode 100644
index 00000000..e9671824
--- /dev/null
+++ b/adapters/kiviads/kiviadstest/supplemental/bad_response.json
@@ -0,0 +1,84 @@
+{
+	"mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ],
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+        }
+    },
+	"httpCalls": [{
+		"expectedRequest": {
+			"uri": "http://endpoint1.kiviads.com/pserver",
+			"body": {
+                "id": "test-request-id",
+                "imp": [
+                    {
+                        "id": "test-imp-id",
+                        "banner": {
+                            "format": [
+                                {
+                                    "w": 300,
+                                    "h": 250
+                                },
+                                {
+                                    "w": 300,
+                                    "h": 600
+                                }
+                            ]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": "test",
+                                "type": "publisher"
+                            }
+                        }
+                    }
+                ],
+                "app": {
+                    "id": "1",
+                    "bundle": "com.wls.testwlsapplication"
+                },
+                "device": {
+                    "ip": "123.123.123.123",
+                    "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+                }
+            }
+		},
+		"mockResponse": {
+			"status": 200,
+			"body": ""
+        }
+    }],
+    "expectedMakeBidsErrors": [
+        {
+          "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse",
+          "comparison": "literal"
+        }
+    ]
+}
diff --git a/adapters/kiviads/kiviadstest/supplemental/status-204.json b/adapters/kiviads/kiviadstest/supplemental/status-204.json
new file mode 100644
index 00000000..71229f88
--- /dev/null
+++ b/adapters/kiviads/kiviadstest/supplemental/status-204.json
@@ -0,0 +1,79 @@
+{
+	"mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ],
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+        }
+    },
+	"httpCalls": [{
+		"expectedRequest": {
+			"uri": "http://endpoint1.kiviads.com/pserver",
+			"body": {
+                "id": "test-request-id",
+                "imp": [
+                    {
+                        "id": "test-imp-id",
+                        "banner": {
+                            "format": [
+                                {
+                                    "w": 300,
+                                    "h": 250
+                                },
+                                {
+                                    "w": 300,
+                                    "h": 600
+                                }
+                            ]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": "test",
+                                "type": "publisher"
+                            }
+                        }
+                    }
+                ],
+                "app": {
+                    "id": "1",
+                    "bundle": "com.wls.testwlsapplication"
+                },
+                "device": {
+                    "ip": "123.123.123.123",
+                    "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+                }
+            }
+		},
+		"mockResponse": {
+			"status": 204,
+			"body": {}
+		}
+    }],
+    "expectedBidResponses": []
+}
diff --git a/adapters/kiviads/kiviadstest/supplemental/status-not-200.json b/adapters/kiviads/kiviadstest/supplemental/status-not-200.json
new file mode 100644
index 00000000..16d2db48
--- /dev/null
+++ b/adapters/kiviads/kiviadstest/supplemental/status-not-200.json
@@ -0,0 +1,84 @@
+{
+	"mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ],
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+        }
+    },
+	"httpCalls": [{
+		"expectedRequest": {
+			"uri": "http://endpoint1.kiviads.com/pserver",
+			"body": {
+                "id": "test-request-id",
+                "imp": [
+                    {
+                        "id": "test-imp-id",
+                        "banner": {
+                            "format": [
+                                {
+                                    "w": 300,
+                                    "h": 250
+                                },
+                                {
+                                    "w": 300,
+                                    "h": 600
+                                }
+                            ]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": "test",
+                                "type": "publisher"
+                            }
+                        }
+                    }
+                ],
+                "app": {
+                    "id": "1",
+                    "bundle": "com.wls.testwlsapplication"
+                },
+                "device": {
+                    "ip": "123.123.123.123",
+                    "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+                }
+            }
+		},
+		"mockResponse": {
+			"status": 404,
+			"body": {}
+		}
+    }],
+    "expectedMakeBidsErrors": [
+        {
+          "value": "Unexpected status code: 404. Run with request.debug = 1 for more info.",
+          "comparison": "literal"
+        }
+    ]
+}
diff --git a/adapters/kiviads/params_test.go b/adapters/kiviads/params_test.go
new file mode 100644
index 00000000..177e2f14
--- /dev/null
+++ b/adapters/kiviads/params_test.go
@@ -0,0 +1,47 @@
+package kiviads
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json schema. %v", err)
+	}
+
+	for _, p := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderKiviads, json.RawMessage(p)); err != nil {
+			t.Errorf("Schema rejected valid params: %s", p)
+		}
+	}
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json schema. %v", err)
+	}
+
+	for _, p := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderKiviads, json.RawMessage(p)); err == nil {
+			t.Errorf("Schema allowed invalid params: %s", p)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"placementId": "test"}`,
+	`{"placementId": "1"}`,
+	`{"endpointId": "test"}`,
+	`{"endpointId": "1"}`,
+}
+
+var invalidParams = []string{
+	`{"placementId": 42}`,
+	`{"endpointId": 42}`,
+	`{"placementId": "1", "endpointId": "1"}`,
+}
diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/simple-native.json b/adapters/mobilefuse/mobilefusetest/exemplary/simple-native.json
new file mode 100644
index 00000000..a23a1a39
--- /dev/null
+++ b/adapters/mobilefuse/mobilefusetest/exemplary/simple-native.json
@@ -0,0 +1,87 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "native": {
+                    "ver": "1.2",
+                    "request": "{\"native\":{\"assets\":[{\"id\":0,\"required\":1,\"img\":{\"url\":\"...\"}},{\"id\":1,\"required\":1,\"title\":{\"text\":\"custom title\"}}]}}"
+                },
+                "ext": {
+                    "bidder": {
+                        "placement_id": 123456,
+                        "pub_id": 1234
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://mfx.mobilefuse.com/openrtb?pub_id=1234",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "native": {
+                                "ver": "1.2",
+                                "request": "{\"native\":{\"assets\":[{\"id\":0,\"required\":1,\"img\":{\"url\":\"...\"}},{\"id\":1,\"required\":1,\"title\":{\"text\":\"custom title\"}}]}}"
+                            },
+                            "tagid": "123456"
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "mobilefuse",
+                            "bid": [
+                                {
+                                    "id": "test-bid-id",
+                                    "impid": "1",
+                                    "price": 1.50,
+                                    "adm": "{\"native\":{\"link\":{\"url\":\"http://mfx.mobilefuse.com/openrtb\"},\"assets\":[{\"id\":0,\"required\":1,\"img\":{\"url\":\"...\"}},{\"id\":1,\"required\":1,\"title\":{\"text\":\"custom title\"}}]}}",
+                                    "crid": "test-crid",
+                                    "h": 50,
+                                    "w": 320,
+                                    "ext": {
+                                        "mf": {
+                                            "media_type": "native"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test-bid-id",
+                        "impid": "1",
+                        "price": 1.50,
+                        "adm": "{\"native\":{\"link\":{\"url\":\"http://mfx.mobilefuse.com/openrtb\"},\"assets\":[{\"id\":0,\"required\":1,\"img\":{\"url\":\"...\"}},{\"id\":1,\"required\":1,\"title\":{\"text\":\"custom title\"}}]}}",
+                        "crid": "test-crid",
+                        "h": 50,
+                        "w": 320
+                    },
+                    "type": "native"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/openx/openxtest/exemplary/fledge-no-bids.json b/adapters/openx/openxtest/exemplary/fledge-no-bids.json
new file mode 100644
index 00000000..5c09b9ae
--- /dev/null
+++ b/adapters/openx/openxtest/exemplary/fledge-no-bids.json
@@ -0,0 +1,88 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}]
+        },
+        "ext": {
+          "ae": 1,
+          "bidder": {
+            "unit": "539439964",
+            "delDomain": "se-demo-d.openx.net"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://rtb.openx.net/prebid",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [{"w": 728, "h": 90}]
+              },
+              "tagid": "539439964",
+              "ext": {
+                "ae": 1
+              }
+            }
+          ],
+          "ext": {
+            "bc": "hb_pbs_1.0.0",
+            "delDomain": "se-demo-d.openx.net"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "cur": "USD",
+          "nbr": 1,
+          "ext": {
+            "fledge_auction_configs": {
+              "test-request-id": {
+                "seller": "openx.com",
+                "interestGroupBuyers": ["buyer1.com"],
+                "sellerTimeout": 0,
+                "perBuyerSignals": {
+                  "buyer1.com": [1,"two",3,4, {}]
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [],
+      "fledgeauctionconfigs": [
+        {
+          "impid": "test-request-id",
+          "bidder": "openx",
+          "config": {
+            "seller": "openx.com",
+            "interestGroupBuyers": ["buyer1.com"],
+            "sellerTimeout": 0,
+            "perBuyerSignals": {
+              "buyer1.com": [1,"two",3,4, {}]
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/openx/openxtest/exemplary/fledge.json b/adapters/openx/openxtest/exemplary/fledge.json
new file mode 100644
index 00000000..9c8074b3
--- /dev/null
+++ b/adapters/openx/openxtest/exemplary/fledge.json
@@ -0,0 +1,114 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}]
+        },
+        "ext": {
+          "ae": 1,
+          "bidder": {
+            "unit": "539439964",
+            "delDomain": "se-demo-d.openx.net"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://rtb.openx.net/prebid",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [{"w": 728, "h": 90}]
+              },
+              "tagid": "539439964",
+              "ext": {
+                "ae": 1
+              }
+            }
+          ],
+          "ext": {
+            "bc": "hb_pbs_1.0.0",
+            "delDomain": "se-demo-d.openx.net"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "openx",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "test-imp-id",
+                "price": 0.500000,
+                "adm": "some-test-ad",
+                "crid": "crid_10",
+                "h": 90,
+                "w": 728
+              }]
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+            "fledge_auction_configs": {
+              "test-request-id": {
+                "seller": "openx.com",
+                "interestGroupBuyers": ["buyer1.com"],
+                "sellerTimeout": 0,
+                "perBuyerSignals": {
+                  "buyer1.com": [1,"two",3,4, {}]
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-ad",
+            "crid": "crid_10",
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        }
+      ],
+      "fledgeauctionconfigs": [
+        {
+          "impid": "test-request-id",
+          "bidder": "openx",
+          "config": {
+            "seller": "openx.com",
+            "interestGroupBuyers": ["buyer1.com"],
+            "sellerTimeout": 0,
+            "perBuyerSignals": {
+              "buyer1.com": [1,"two",3,4, {}]
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/rubicon/rubicontest/exemplary/simple-native.json b/adapters/rubicon/rubicontest/exemplary/simple-native.json
index da970486..976d05c1 100644
--- a/adapters/rubicon/rubicontest/exemplary/simple-native.json
+++ b/adapters/rubicon/rubicontest/exemplary/simple-native.json
@@ -335,7 +335,7 @@
                   "cid": "test_cid",
                   "crid": "test_crid",
                   "dealid": "test_dealid",
-                  "admobject": {
+                  "adm_native": {
                     "property1": "value1"
                   },
                   "ext": {
diff --git a/config/bidderinfo_test.go b/config/bidderinfo_test.go
index 462e920f..6005178c 100644
--- a/config/bidderinfo_test.go
+++ b/config/bidderinfo_test.go
@@ -2,10 +2,11 @@ package config
 
 import (
 	"errors"
-	"gopkg.in/yaml.v3"
 	"strings"
 	"testing"
 
+	"gopkg.in/yaml.v3"
+
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/stretchr/testify/assert"
 )
@@ -13,12 +14,12 @@ import (
 const testInfoFilesPathValid = "./test/bidder-info-valid"
 const testSimpleYAML = `
 maintainer:
-  email: "some-email@domain.com"
+  email: some-email@domain.com
 gvlVendorID: 42
 `
 const fullBidderYAMLConfig = `
 maintainer:
-  email: "some-email@domain.com"
+  email: some-email@domain.com
 capabilities:
   app:
     mediaTypes:
@@ -37,7 +38,10 @@ gvlVendorID: 42
 experiment:
   adsCert:
     enabled: true
-endpointCompression: "GZIP"
+endpointCompression: GZIP
+openrtb:
+  version: 2.6
+  gpp-supported: true
 `
 
 func TestLoadBidderInfoFromDisk(t *testing.T) {
@@ -990,6 +994,10 @@ func TestReadFullYamlBidderConfig(t *testing.T) {
 			},
 			Experiment:          BidderInfoExperiment{AdsCert: BidderAdsCert{Enabled: true}},
 			EndpointCompression: "GZIP",
+			OpenRTB: &OpenRTBInfo{
+				Version:      "2.6",
+				GPPSupported: true,
+			},
 		},
 	}
 	assert.Equalf(t, expectedBidderInfo, actualBidderInfo, "Bidder info objects aren't matching")
diff --git a/config/config_test.go b/config/config_test.go
index 43ab9273..985981d7 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -860,6 +860,11 @@ func TestBidderInfoFromEnv(t *testing.T) {
 	} else {
 		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_URL")
 	}
+	if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_OPENRTB_VERSION"); ok {
+		defer os.Setenv("PBS_ADAPTERS_BIDDER1_OPENRTB_VERSION", oldval)
+	} else {
+		defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_OPENRTB_VERSION")
+	}
 
 	// set new
 	os.Setenv("PBS_ADAPTERS_BIDDER1_DISABLED", "true")
@@ -870,6 +875,7 @@ func TestBidderInfoFromEnv(t *testing.T) {
 	os.Setenv("PBS_ADAPTERS_BIDDER1_EXPERIMENT_ADSCERT_ENABLED", "true")
 	os.Setenv("PBS_ADAPTERS_BIDDER1_XAPI_USERNAME", "username_override")
 	os.Setenv("PBS_ADAPTERS_BIDDER1_USERSYNC_REDIRECT_URL", "http://some.url/sync?redirect={{.RedirectURL}}")
+	os.Setenv("PBS_ADAPTERS_BIDDER1_OPENRTB_VERSION", "2.6")
 
 	cfg, _ := newDefaultConfig(t)
 
@@ -882,6 +888,8 @@ func TestBidderInfoFromEnv(t *testing.T) {
 
 	assert.Equal(t, true, cfg.BidderInfos["bidder1"].Experiment.AdsCert.Enabled)
 	assert.Equal(t, "username_override", cfg.BidderInfos["bidder1"].XAPI.Username)
+
+	assert.Equal(t, "2.6", cfg.BidderInfos["bidder1"].OpenRTB.Version)
 }
 
 func TestMigrateConfigPurposeOneTreatment(t *testing.T) {
diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go
index 58c13963..80544874 100644
--- a/endpoints/openrtb2/amp_auction_test.go
+++ b/endpoints/openrtb2/amp_auction_test.go
@@ -1345,6 +1345,7 @@ func (s formatOverrideSpec) execute(t *testing.T) {
 
 type mockAmpExchange struct {
 	lastRequest *openrtb2.BidRequest
+	requestExt  json.RawMessage
 }
 
 var expectedErrorsFromHoldAuction map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage = map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage{
@@ -1369,6 +1370,10 @@ func (m *mockAmpExchange) HoldAuction(ctx context.Context, auctionRequest exchan
 		}},
 		Ext: json.RawMessage(`{ "errors": {"openx":[ { "code": 1, "message": "The request exceeded the timeout allocated" } ] } }`),
 	}
+
+	if m.requestExt != nil {
+		response.Ext = m.requestExt
+	}
 	if len(auctionRequest.StoredAuctionResponses) > 0 {
 		var seatBids []openrtb2.SeatBid
 
@@ -1588,6 +1593,7 @@ func TestBuildAmpObject(t *testing.T) {
 	testCases := []struct {
 		description       string
 		inTagId           string
+		exchange          *mockAmpExchange
 		inStoredRequest   json.RawMessage
 		expectedAmpObject *analytics.AmpObject
 	}{
@@ -1671,6 +1677,61 @@ func TestBuildAmpObject(t *testing.T) {
 				Origin: "",
 			},
 		},
+		{
+			description:     "Global targeting from bid response should be applied for Amp",
+			inTagId:         "test",
+			inStoredRequest: json.RawMessage(`{"id":"some-request-id","site":{"page":"prebid.org"},"imp":[{"id":"some-impression-id","banner":{"format":[{"w":300,"h":250}]},"ext":{"prebid":{"bidder":{"appnexus":{"placementId":12883451}}}}}],"tmax":500}`),
+			exchange:        &mockAmpExchange{requestExt: json.RawMessage(`{ "prebid": {"targeting": { "test_key": "test_value", "hb_appnexus_pb": "9999" } }, "errors": {"openx":[ { "code": 1, "message": "The request exceeded the timeout allocated" } ] } }`)},
+			expectedAmpObject: &analytics.AmpObject{
+				Status: http.StatusOK,
+				Errors: nil,
+				Request: &openrtb2.BidRequest{
+					ID: "some-request-id",
+					Device: &openrtb2.Device{
+						IP: "192.0.2.1",
+					},
+					Site: &openrtb2.Site{
+						Page: "prebid.org",
+						Ext:  json.RawMessage(`{"amp":1}`),
+					},
+					Imp: []openrtb2.Imp{
+						{
+							ID: "some-impression-id",
+							Banner: &openrtb2.Banner{
+								Format: []openrtb2.Format{
+									{
+										W: 300,
+										H: 250,
+									},
+								},
+							},
+							Secure: func(val int8) *int8 { return &val }(1), //(*int8)(1),
+							Ext:    json.RawMessage(`{"prebid":{"bidder":{"appnexus":{"placementId":12883451}}}}`),
+						},
+					},
+					AT:   1,
+					TMax: 500,
+					Ext:  json.RawMessage(`{"prebid":{"cache":{"bids":{"returnCreative":null},"vastxml":null},"channel":{"name":"amp","version":""},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true,"includebrandcategory":null,"includeformat":false,"durationrangesec":null,"preferdeals":false}}}`),
+				},
+				AuctionResponse: &openrtb2.BidResponse{
+					SeatBid: []openrtb2.SeatBid{{
+						Bid: []openrtb2.Bid{{
+							AdM: "<script></script>",
+							Ext: json.RawMessage(`{ "prebid": {"targeting": { "hb_pb": "1.20", "hb_appnexus_pb": "1.20", "hb_cache_id": "some_id"}}}`),
+						}},
+						Seat: "",
+					}},
+					Ext: json.RawMessage(`{ "prebid": {"targeting": { "test_key": "test_value", "hb_appnexus_pb": "9999" } }, "errors": {"openx":[ { "code": 1, "message": "The request exceeded the timeout allocated" } ] } }`),
+				},
+				AmpTargetingValues: map[string]string{
+					"hb_appnexus_pb": "1.20", // Bid level has higher priority than global
+					"hb_cache_id":    "some_id",
+					"hb_pb":          "1.20",
+					"test_key":       "test_value", // New global key added
+				},
+				Origin: "",
+			},
+		},
 	}
 
 	request := httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=test", nil)
@@ -1678,7 +1739,11 @@ func TestBuildAmpObject(t *testing.T) {
 
 	for _, test := range testCases {
 		// Set up test, declare a new mock logger every time
-		actualAmpObject, endpoint := ampObjectTestSetup(t, test.inTagId, test.inStoredRequest, false)
+		exchange := test.exchange
+		if exchange == nil {
+			exchange = &mockAmpExchange{}
+		}
+		actualAmpObject, endpoint := ampObjectTestSetup(t, test.inTagId, test.inStoredRequest, false, exchange)
 		// Run test
 		endpoint(recorder, request, nil)
 
@@ -1744,13 +1809,13 @@ func TestIdGeneration(t *testing.T) {
 
 	for _, test := range testCases {
 		// Set up and run test
-		actualAmpObject, endpoint := ampObjectTestSetup(t, "test", test.givenInStoredRequest, test.givenGenerateRequestID)
+		actualAmpObject, endpoint := ampObjectTestSetup(t, "test", test.givenInStoredRequest, test.givenGenerateRequestID, &mockAmpExchange{})
 		endpoint(recorder, request, nil)
 		assert.Equalf(t, test.expectedID, actualAmpObject.Request.ID, "Bid Request ID is incorrect: %s\n", test.description)
 	}
 }
 
-func ampObjectTestSetup(t *testing.T, inTagId string, inStoredRequest json.RawMessage, generateRequestID bool) (*analytics.AmpObject, httprouter.Handle) {
+func ampObjectTestSetup(t *testing.T, inTagId string, inStoredRequest json.RawMessage, generateRequestID bool, exchange *mockAmpExchange) (*analytics.AmpObject, httprouter.Handle) {
 	actualAmpObject := analytics.AmpObject{}
 	logger := newMockLogger(&actualAmpObject, nil)
 
@@ -1762,7 +1827,7 @@ func ampObjectTestSetup(t *testing.T, inTagId string, inStoredRequest json.RawMe
 
 	endpoint, _ := NewAmpEndpoint(
 		fakeUUIDGenerator{id: "foo", err: nil},
-		&mockAmpExchange{},
+		exchange,
 		newParamsValidator(t),
 		mockAmpFetcher,
 		empty_fetcher.EmptyFetcher{},
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index c9cd0aff..4e8fe2f7 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -60,6 +60,7 @@ func TestVideoEndpointImpressionsNumber(t *testing.T) {
 	assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response")
 
 	assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s", "Incorrect number of Ad Pods in response")
+	assert.Equal(t, resp.AdPods[0].Targeting[0].HbDeal, "ABC_123", "If DealID exists in bid response, hb_deal targeting needs to be added to resp")
 }
 
 func TestVideoEndpointImpressionsDuration(t *testing.T) {
@@ -1372,7 +1373,7 @@ func (m *mockExchangeVideo) HoldAuction(ctx context.Context, r exchange.AuctionR
 	if debugLog != nil && debugLog.Enabled {
 		m.cache.called = true
 	}
-	ext := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"20.00","hb_pb_cat_dur_appnex":"20.00_395_30s","hb_size":"1x1", "hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"},"type":"video","dealpriority":0,"dealtiersatisfied":false},"bidder":{"appnexus":{"brand_id":1,"auction_id":7840037870526938650,"bidder_id":2,"bid_ad_type":1,"creative_info":{"video":{"duration":30,"mimes":["video\/mp4"]}}}}}`)
+	ext := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"20.00","hb_pb_cat_dur_appnex":"20.00_395_30s","hb_size":"1x1", "hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1", "hb_deal_appnexus": "ABC_123"},"type":"video","dealpriority":0,"dealtiersatisfied":false},"bidder":{"appnexus":{"brand_id":1,"auction_id":7840037870526938650,"bidder_id":2,"bid_ad_type":1,"creative_info":{"video":{"duration":30,"mimes":["video\/mp4"]}}}}}`)
 	return &openrtb2.BidResponse{
 		SeatBid: []openrtb2.SeatBid{{
 			Seat: "appnexus",
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 8bf8427c..34749ba6 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -340,7 +340,11 @@ func TestDebugBehaviour(t *testing.T) {
 	}.Builder
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.categoriesFetcher = categoriesFetcher
-
+	e.requestSplitter = requestSplitter{
+		me:                &metricsConf.NilMetricsEngine{},
+		gdprPermsBuilder:  e.gdprPermsBuilder,
+		tcf2ConfigBuilder: e.tcf2ConfigBuilder,
+	}
 	ctx := context.Background()
 
 	// Run tests
@@ -505,6 +509,11 @@ func TestTwoBiddersDebugDisabledAndEnabled(t *testing.T) {
 	}.Builder
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.categoriesFetcher = categoriesFetcher
+	e.requestSplitter = requestSplitter{
+		me:                e.me,
+		gdprPermsBuilder:  e.gdprPermsBuilder,
+		tcf2ConfigBuilder: e.tcf2ConfigBuilder,
+	}
 
 	debugLog := DebugLog{Enabled: true}
 
@@ -668,6 +677,11 @@ func TestOverrideWithCustomCurrency(t *testing.T) {
 	e.currencyConverter = mockCurrencyConverter
 	e.categoriesFetcher = categoriesFetcher
 	e.bidIDGenerator = &mockBidIDGenerator{false, false}
+	e.requestSplitter = requestSplitter{
+		me:                e.me,
+		gdprPermsBuilder:  e.gdprPermsBuilder,
+		tcf2ConfigBuilder: e.tcf2ConfigBuilder,
+	}
 
 	// Define mock incoming bid requeset
 	mockBidRequest := &openrtb2.BidRequest{
@@ -774,6 +788,11 @@ func TestAdapterCurrency(t *testing.T) {
 			openrtb_ext.BidderName("foo"): AdaptBidder(mockBidder, nil, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderName("foo"), nil, ""),
 		},
 	}
+	e.requestSplitter = requestSplitter{
+		me:                e.me,
+		gdprPermsBuilder:  e.gdprPermsBuilder,
+		tcf2ConfigBuilder: e.tcf2ConfigBuilder,
+	}
 
 	// Define Bid Request
 	request := &openrtb2.BidRequest{
@@ -1154,6 +1173,11 @@ func TestReturnCreativeEndToEnd(t *testing.T) {
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.categoriesFetcher = categoriesFetcher
 	e.bidIDGenerator = &mockBidIDGenerator{false, false}
+	e.requestSplitter = requestSplitter{
+		me:                e.me,
+		gdprPermsBuilder:  e.gdprPermsBuilder,
+		tcf2ConfigBuilder: e.tcf2ConfigBuilder,
+	}
 
 	// Define mock incoming bid requeset
 	mockBidRequest := &openrtb2.BidRequest{
@@ -2422,9 +2446,20 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 		hostSChainNode = nil
 	}
 
+	metricsEngine := metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.CoreBidderNames(), nil, nil)
+	requestSplitter := requestSplitter{
+		bidderToSyncerKey: bidderToSyncerKey,
+		me:                metricsEngine,
+		privacyConfig:     privacyConfig,
+		gdprPermsBuilder:  gdprPermsBuilder,
+		tcf2ConfigBuilder: tcf2ConfigBuilder,
+		hostSChainNode:    hostSChainNode,
+		bidderInfo:        bidderInfos,
+	}
+
 	return &exchange{
 		adapterMap:               bidderAdapters,
-		me:                       metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.CoreBidderNames(), nil, nil),
+		me:                       metricsEngine,
 		cache:                    &wellBehavedCache{},
 		cacheTime:                0,
 		currencyConverter:        currency.NewRateConverter(&http.Client{}, "", time.Duration(0)),
@@ -2440,6 +2475,7 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 		hostSChainNode:           hostSChainNode,
 		server:                   config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "Datacenter"},
 		bidValidationEnforcement: hostBidValidation,
+		requestSplitter:          requestSplitter,
 	}
 }
 
@@ -4120,6 +4156,11 @@ func TestAuctionDebugEnabled(t *testing.T) {
 	e.tcf2ConfigBuilder = fakeTCF2ConfigBuilder{
 		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
 	}.Builder
+	e.requestSplitter = requestSplitter{
+		me:                e.me,
+		gdprPermsBuilder:  e.gdprPermsBuilder,
+		tcf2ConfigBuilder: e.tcf2ConfigBuilder,
+	}
 
 	ctx := context.Background()
 
@@ -4632,6 +4673,11 @@ func TestOverrideConfigAlternateBidderCodesWithRequestValues(t *testing.T) {
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.categoriesFetcher = categoriesFetcher
 	e.bidIDGenerator = &mockBidIDGenerator{false, false}
+	e.requestSplitter = requestSplitter{
+		me:                e.me,
+		gdprPermsBuilder:  e.gdprPermsBuilder,
+		tcf2ConfigBuilder: e.tcf2ConfigBuilder,
+	}
 
 	// Define mock incoming bid requeset
 	mockBidRequest := &openrtb2.BidRequest{
@@ -5035,6 +5081,11 @@ func TestModulesCanBeExecutedForMultipleBiddersSimultaneously(t *testing.T) {
 		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
 	}.Builder
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
+	e.requestSplitter = requestSplitter{
+		me:                e.me,
+		gdprPermsBuilder:  e.gdprPermsBuilder,
+		tcf2ConfigBuilder: e.tcf2ConfigBuilder,
+	}
 
 	bidRequest := &openrtb2.BidRequest{
 		ID: "some-request-id",
diff --git a/exchange/price_granularity_test.go b/exchange/price_granularity_test.go
index 13840838..6349713e 100644
--- a/exchange/price_granularity_test.go
+++ b/exchange/price_granularity_test.go
@@ -30,6 +30,22 @@ func TestGetPriceBucketString(t *testing.T) {
 		},
 	}
 
+	custom2 := openrtb_ext.PriceGranularity{
+		Precision: 2,
+		Ranges: []openrtb_ext.GranularityRange{
+			{
+				Min:       0.0,
+				Max:       1.5,
+				Increment: 1.0,
+			},
+			{
+				Min:       1.5,
+				Max:       10.0,
+				Increment: 1.2,
+			},
+		},
+	}
+
 	// Define test cases
 	type aTest struct {
 		granularityId       string
@@ -51,6 +67,7 @@ func TestGetPriceBucketString(t *testing.T) {
 				{"auto", auto, "1.85"},
 				{"dense", dense, "1.87"},
 				{"custom1", custom1, "1.86"},
+				{"custom2", custom2, "1.50"},
 			},
 		},
 		{
@@ -63,6 +80,15 @@ func TestGetPriceBucketString(t *testing.T) {
 				{"auto", auto, "5.70"},
 				{"dense", dense, "5.70"},
 				{"custom1", custom1, "5.70"},
+				{"custom2", custom2, "5.10"},
+			},
+		},
+		{
+			groupDesc: "cpm equal the max for custom granularity",
+			cpm:       10,
+			testCases: []aTest{
+				{"custom1", custom1, "10.00"},
+				{"custom2", custom2, "9.90"},
 			},
 		},
 		{
diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go
index dc91177a..809b58af 100644
--- a/exchange/targeting_test.go
+++ b/exchange/targeting_test.go
@@ -106,6 +106,11 @@ func runTargetingAuction(t *testing.T, mockBids map[openrtb_ext.BidderName][]*op
 		categoriesFetcher: categoriesFetcher,
 		bidIDGenerator:    &mockBidIDGenerator{false, false},
 	}
+	ex.requestSplitter = requestSplitter{
+		me:                ex.me,
+		gdprPermsBuilder:  ex.gdprPermsBuilder,
+		tcf2ConfigBuilder: ex.tcf2ConfigBuilder,
+	}
 
 	imps := buildImps(t, mockBids)
 
@@ -439,6 +444,32 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 		},
 		TruncateTargetAttr: nil,
 	},
+	{
+		Description: "bidder with no dealID should not have deal targeting",
+		TargetData: targetData{
+			priceGranularity:  openrtb_ext.PriceGranularityFromString("med"),
+			includeBidderKeys: true,
+		},
+		Auction: auction{
+			winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*entities.PbsOrtbBid{
+				"ImpId-1": {
+					openrtb_ext.BidderAppnexus: {
+						Bid:     bid123,
+						BidType: openrtb_ext.BidTypeBanner,
+					},
+				},
+			},
+		},
+		ExpectedBidTargetsByBidder: map[string]map[openrtb_ext.BidderName]map[string]string{
+			"ImpId-1": {
+				openrtb_ext.BidderAppnexus: {
+					"hb_bidder_appnexus": "appnexus",
+					"hb_pb_appnexus":     "1.20",
+				},
+			},
+		},
+		TruncateTargetAttr: nil,
+	},
 	{
 		Description: "Truncate Targeting Attribute value is given and is less than const MaxKeyLength",
 		TargetData: targetData{
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index d499ea43..257449bd 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -476,9 +476,6 @@ func TestCleanOpenRTBRequests(t *testing.T) {
 
 	for _, test := range testCases {
 
-		metricsMock := metrics.MetricsEngineMock{}
-		bidderToSyncerKey := map[string]string{}
-
 		gdprPermsBuilder := fakePermissionsBuilder{
 			permissions: &permissionsMock{
 				allowAllBidders: true,
@@ -488,7 +485,16 @@ func TestCleanOpenRTBRequests(t *testing.T) {
 			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
 		}.Builder
 
-		bidderRequests, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, bidderToSyncerKey, &metricsMock, gdpr.SignalNo, privacyConfig, gdprPermsBuilder, tcf2ConfigBuilder, nil)
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metrics.MetricsEngineMock{},
+			privacyConfig:     privacyConfig,
+			gdprPermsBuilder:  gdprPermsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo)
 		if test.hasError {
 			assert.NotNil(t, err, "Error shouldn't be nil")
 		} else {
@@ -543,8 +549,6 @@ func TestCleanOpenRTBRequestsWithFPD(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		metricsMock := metrics.MetricsEngineMock{}
-		bidderToSyncerKey := map[string]string{}
 
 		gdprPermissionsBuilder := fakePermissionsBuilder{
 			permissions: &permissionsMock{
@@ -555,7 +559,17 @@ func TestCleanOpenRTBRequestsWithFPD(t *testing.T) {
 			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
 		}.Builder
 
-		bidderRequests, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, bidderToSyncerKey, &metricsMock, gdpr.SignalNo, config.Privacy{}, gdprPermissionsBuilder, tcf2ConfigBuilder, nil)
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metrics.MetricsEngineMock{},
+			privacyConfig:     config.Privacy{},
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+
+		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo)
 		assert.Empty(t, err, "No errors should be returned")
 		for _, bidderRequest := range bidderRequests {
 			bidderName := bidderRequest.BidderName
@@ -806,8 +820,6 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		metricsMock := metrics.MetricsEngineMock{}
-		bidderToSyncerKey := map[string]string{}
 
 		gdprPermissionsBuilder := fakePermissionsBuilder{
 			permissions: &permissionsMock{
@@ -824,17 +836,17 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) {
 			StoredBidResponses: test.storedBidResponses,
 		}
 
-		actualBidderRequests, _, err := cleanOpenRTBRequests(
-			context.Background(),
-			auctionReq,
-			nil,
-			bidderToSyncerKey,
-			&metricsMock,
-			gdpr.SignalNo,
-			config.Privacy{},
-			gdprPermissionsBuilder,
-			tcf2ConfigBuilder,
-			nil)
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metrics.MetricsEngineMock{},
+			privacyConfig:     config.Privacy{},
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+
+		actualBidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
 		assert.Empty(t, err, "No errors should be returned")
 		assert.Len(t, actualBidderRequests, len(test.expectedBidderRequests), "result len doesn't match for testCase %s", test.description)
 		for _, actualBidderRequest := range actualBidderRequests {
@@ -996,17 +1008,17 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) {
 		}.Builder
 
 		bidderToSyncerKey := map[string]string{}
-		bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(
-			context.Background(),
-			auctionReq,
-			nil,
-			bidderToSyncerKey,
-			&metrics.MetricsEngineMock{},
-			gdpr.SignalNo,
-			privacyConfig,
-			gdprPermissionsBuilder,
-			tcf2ConfigBuilder,
-			nil)
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: bidderToSyncerKey,
+			me:                &metrics.MetricsEngineMock{},
+			privacyConfig:     privacyConfig,
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+
+		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
 		result := bidderRequests[0]
 
 		assert.Nil(t, errs)
@@ -1076,7 +1088,17 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) {
 		bidderToSyncerKey := map[string]string{}
 		metrics := metrics.MetricsEngineMock{}
 
-		_, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, bidderToSyncerKey, &metrics, gdpr.SignalNo, privacyConfig, gdprPermissionsBuilder, tcf2ConfigBuilder, nil)
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: bidderToSyncerKey,
+			me:                &metrics,
+			privacyConfig:     privacyConfig,
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+
+		_, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, gdpr.SignalNo)
 
 		assert.ElementsMatch(t, []error{test.expectError}, errs, test.description)
 	}
@@ -1128,7 +1150,17 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) {
 		bidderToSyncerKey := map[string]string{}
 		metrics := metrics.MetricsEngineMock{}
 
-		bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, bidderToSyncerKey, &metrics, gdpr.SignalNo, config.Privacy{}, gdprPermissionsBuilder, tcf2ConfigBuilder, nil)
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: bidderToSyncerKey,
+			me:                &metrics,
+			privacyConfig:     config.Privacy{},
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+
+		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
 		result := bidderRequests[0]
 
 		assert.Nil(t, errs)
@@ -1221,9 +1253,17 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) {
 			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
 		}.Builder
 
-		bidderToSyncerKey := map[string]string{}
-		metrics := metrics.MetricsEngineMock{}
-		bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, bidderToSyncerKey, &metrics, gdpr.SignalNo, config.Privacy{}, gdprPermissionsBuilder, tcf2ConfigBuilder, nil)
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metrics.MetricsEngineMock{},
+			privacyConfig:     config.Privacy{},
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
 		if test.hasError == true {
 			assert.NotNil(t, errs)
 			assert.Len(t, bidderRequests, 0)
@@ -1287,10 +1327,17 @@ func TestCleanOpenRTBRequestsBidderParams(t *testing.T) {
 			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
 		}.Builder
 
-		bidderToSyncerKey := map[string]string{}
-		metrics := metrics.MetricsEngineMock{}
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metrics.MetricsEngineMock{},
+			privacyConfig:     config.Privacy{},
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
 
-		bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, bidderToSyncerKey, &metrics, gdpr.SignalNo, config.Privacy{}, gdprPermissionsBuilder, tcf2ConfigBuilder, nil)
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
 		if test.hasError == true {
 			assert.NotNil(t, errs)
 			assert.Len(t, bidderRequests, 0)
@@ -1978,9 +2025,17 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) {
 			},
 		}
 
-		bidderToSyncerKey := map[string]string{}
-		metrics := metrics.MetricsEngineMock{}
-		results, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, bidderToSyncerKey, &metrics, gdpr.SignalNo, privacyConfig, gdprPermissionsBuilder, tcf2ConfigBuilder, nil)
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metrics.MetricsEngineMock{},
+			privacyConfig:     privacyConfig,
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+
+		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
 		result := results[0]
 
 		assert.Nil(t, errs)
@@ -2201,24 +2256,22 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 			),
 		}.Builder
 
-		bidderToSyncerKey := map[string]string{}
-
 		gdprDefaultValue := gdpr.SignalYes
 		if test.gdprDefaultValue == "0" {
 			gdprDefaultValue = gdpr.SignalNo
 		}
 
-		results, privacyLabels, errs := cleanOpenRTBRequests(
-			context.Background(),
-			auctionReq,
-			nil,
-			bidderToSyncerKey,
-			&metrics.MetricsEngineMock{},
-			gdprDefaultValue,
-			privacyConfig,
-			gdprPermissionsBuilder,
-			tcf2ConfigBuilder,
-			nil)
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metrics.MetricsEngineMock{},
+			privacyConfig:     privacyConfig,
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+
+		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdprDefaultValue)
 		result := results[0]
 
 		if test.expectError {
@@ -2312,18 +2365,17 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) {
 		metricsMock := metrics.MetricsEngineMock{}
 		metricsMock.Mock.On("RecordAdapterGDPRRequestBlocked", mock.Anything).Return()
 
-		bidderToSyncerKey := map[string]string{}
-		results, _, errs := cleanOpenRTBRequests(
-			context.Background(),
-			auctionReq,
-			nil,
-			bidderToSyncerKey,
-			&metricsMock,
-			gdpr.SignalNo,
-			privacyConfig,
-			gdprPermissionsBuilder,
-			tcf2ConfigBuilder,
-			nil)
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metricsMock,
+			privacyConfig:     privacyConfig,
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+
+		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
 
 		// extract bidder name from each request in the results
 		bidders := []openrtb_ext.BidderName{}
@@ -3045,9 +3097,16 @@ func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) {
 		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
 	}.Builder
 
-	bidderToSyncerKey := map[string]string{}
-	metrics := metrics.MetricsEngineMock{}
-	bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, bidderToSyncerKey, &metrics, gdpr.SignalNo, config.Privacy{}, gdprPermissionsBuilder, tcf2ConfigBuilder, nil)
+	reqSplitter := &requestSplitter{
+		bidderToSyncerKey: map[string]string{},
+		me:                &metrics.MetricsEngineMock{},
+		privacyConfig:     config.Privacy{},
+		gdprPermsBuilder:  gdprPermissionsBuilder,
+		tcf2ConfigBuilder: tcf2ConfigBuilder,
+		hostSChainNode:    nil,
+		bidderInfo:        config.BidderInfos{},
+	}
+	bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
 
 	assert.Nil(t, errs)
 	assert.Len(t, bidderRequests, 2, "Bid request count is not 2")
@@ -3395,10 +3454,18 @@ func TestCleanOpenRTBRequestsFilterBidderRequestExt(t *testing.T) {
 		tcf2ConfigBuilder := fakeTCF2ConfigBuilder{
 			cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
 		}.Builder
-		bidderToSyncerKey := map[string]string{}
-		metrics := metrics.MetricsEngineMock{}
 
-		bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, bidderToSyncerKey, &metrics, gdpr.SignalNo, config.Privacy{}, gdprPermissionsBuilder, tcf2ConfigBuilder, nil)
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metrics.MetricsEngineMock{},
+			privacyConfig:     config.Privacy{},
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			tcf2ConfigBuilder: tcf2ConfigBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
 		assert.Equal(t, test.wantError, len(errs) != 0, test.desc)
 		sort.Slice(bidderRequests, func(i, j int) bool {
 			return bidderRequests[i].BidderCoreName < bidderRequests[j].BidderCoreName