Repository: prebid/prebid-server
Pull request #2534 (2023-01-19 10:29:37)

Openrtb2blocking module part 3: RawBidderResponse Hook implementation
Module PRD: https://docs.google.com/document/d/1TpB_I-Oft0LpZuKvVsaLAozOe1u52tpPGkB_jZfnMXo/edit?usp=sharing


Patch:
diff --git a/README.md b/README.md
index e88a4b26..4ec6792c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[![Build](https://img.shields.io/github/workflow/status/prebid/prebid-server/Validate/master?style=flat-square)](https://github.com/prebid/prebid-server/actions/workflows/validate.yml)
+[![Build](https://img.shields.io/github/actions/workflow/status/prebid/prebid-server/validate.yml?branch=master&style=flat-square)](https://github.com/prebid/prebid-server/actions/workflows/validate.yml)
 [![Go Report Card](https://goreportcard.com/badge/github.com/prebid/prebid-server?style=flat-square)](https://goreportcard.com/report/github.com/prebid/prebid-server)
 ![Go Version](https://img.shields.io/github/go-mod/go-version/prebid/prebid-server?style=flat-square)
 
diff --git a/adapters/bidder.go b/adapters/bidder.go
index 952ed20f..9f8bd7fa 100644
--- a/adapters/bidder.go
+++ b/adapters/bidder.go
@@ -60,8 +60,9 @@ type TimeoutBidder interface {
 // From the bid response, the bidder accepts a list of valid currencies for the bid.
 // The currency is the same across all bids.
 type BidderResponse struct {
-	Currency string
-	Bids     []*TypedBid
+	Currency             string
+	Bids                 []*TypedBid
+	FledgeAuctionConfigs []*openrtb_ext.FledgeAuctionConfig
 }
 
 // NewBidderResponseWithBidsCapacity create a new BidderResponse initialising the bids array capacity and the default currency value
@@ -134,6 +135,8 @@ type ExtImpBidder struct {
 	// Bidder implementations may safely assume that this JSON has been validated by their
 	// static/bidder-params/{bidder}.json file.
 	Bidder json.RawMessage `json:"bidder"`
+
+	AuctionEnvironment openrtb_ext.AuctionEnvironmentType `json:"ae,omitempty"`
 }
 
 func (r *RequestData) SetBasicAuth(username string, password string) {
diff --git a/adapters/consumable/consumable.go b/adapters/consumable/consumable.go
index dc9759c2..b9a91ee1 100644
--- a/adapters/consumable/consumable.go
+++ b/adapters/consumable/consumable.go
@@ -77,6 +77,8 @@ type decision struct {
 	ImpressionUrl *string    `json:"impressionUrl,omitempty"`
 	Width         uint64     `json:"width,omitempty"`  // Consumable extension, not defined by Adzerk
 	Height        uint64     `json:"height,omitempty"` // Consumable extension, not defined by Adzerk
+	Adomain       []string   `json:"adomain,omitempty"`
+	Cats          []string   `json:"cats,omitempty"`
 }
 
 type contents struct {
@@ -286,7 +288,8 @@ func (a *ConsumableAdapter) MakeBids(
 			bid.H = int64(decision.Height)
 			bid.CrID = strconv.FormatInt(decision.AdID, 10)
 			bid.Exp = 30 // TODO: Check this is intention of TTL
-
+			bid.ADomain = decision.Adomain
+			bid.Cat = decision.Cats
 			// not yet ported from prebid.js adapter
 			//bid.requestId = bidId;
 			//bid.currency = 'USD';
diff --git a/adapters/consumable/consumable/supplemental/simple-banner-meta.json b/adapters/consumable/consumable/supplemental/simple-banner-meta.json
new file mode 100644
index 00000000..6c94c835
--- /dev/null
+++ b/adapters/consumable/consumable/supplemental/simple-banner-meta.json
@@ -0,0 +1,128 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 250}]
+        },
+        "ext": {
+          "bidder": {
+            "networkId": 11,
+            "siteId": 32,
+            "unitId": 42
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36",
+      "ip": "123.123.123.123"
+    },
+    "site": {
+      "domain": "www.some.com",
+      "page": "http://www.some.com/page-where-ad-will-be-shown"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://e.serverbid.com/api/v2",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ],
+          "Forwarded": [
+            "for=123.123.123.123"
+          ],
+          "Origin": [
+            "http://www.some.com"
+          ],
+          "Referer": [
+            "http://www.some.com/page-where-ad-will-be-shown"
+          ]
+        },
+        "body": {
+          "placements": [
+            {
+              "adTypes": [2730],
+              "divName": "test-imp-id",
+              "networkId": 11,
+              "siteId": 32,
+              "unitId": 42
+            }
+          ],
+          "schain": {
+            "complete": 0,
+            "nodes": null,
+            "ver": ""
+          },
+          "networkId": 11,
+          "siteId": 32,
+          "unitId": 42,
+          "time": 1451651415,
+          "url": "http://www.some.com/page-where-ad-will-be-shown",
+          "includePricingData": true,
+          "user":{},
+          "enableBotFiltering": true,
+          "parallel": true
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "decisions": {
+            "test-imp-id": {
+              "adId": 1234567890,
+              "pricing": {
+                "clearPrice": 0.5
+              },
+              "width": 728,
+              "height": 250,
+              "impressionUrl": "http://localhost:8080/shown",
+              "contents" : [
+                {
+                  "body": "<blink>Remember this: https://www.google.com/search?q=blink+tag ?</blink>"
+                }
+              ],
+              "adomain": ["consumabletv.com"],
+              "cats": ["IAB1", "IAB2"]
+            }
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-request-id",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "<blink>Remember this: https://www.google.com/search?q=blink+tag ?</blink>",
+            "crid": "1234567890",
+            "exp": 30,
+            "w": 728,
+            "h": 250,
+            "adomain": ["consumabletv.com"],
+            "cat": ["IAB1", "IAB2"]
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/cwire/cwire.go b/adapters/cwire/cwire.go
new file mode 100644
index 00000000..3540b504
--- /dev/null
+++ b/adapters/cwire/cwire.go
@@ -0,0 +1,100 @@
+package cwire
+
+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"
+)
+
+/*
+Bid adapter implements and exports the requirements:
+
+- The adapters.Builder method to create a new instance of the adapter based on
+  the host configuration
+
+- The adapters.Bidder interface consisting of the MakeRequests method to create
+  outgoing requests to your bidding server and the MakeBids method to create bid
+  responses.
+*/
+
+type adapter struct {
+	endpoint string
+}
+
+// Builder builds a new instance of the CWire 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
+}
+
+/*
+This method creates an HTTP requests that should be sent to the CWire OpenRTB endpoint.
+It's only provided with valid impressions for the adapter, it's not called if there is none.
+For optimization purposes bid adapters are forbidden from directly initiating any form of
+network communication and must entirely rely upon the core framework (adapters.RequestData).
+*/
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+
+	resJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{fmt.Errorf("Error while encoding OpenRTB BidRequest: %v", err)}
+	}
+
+	reqs := []*adapters.RequestData{
+		{
+			Method:  "POST",
+			Uri:     a.endpoint,
+			Body:    resJSON,
+			Headers: headers,
+		},
+	}
+
+	return reqs, nil
+}
+
+/*
+This method is called for every Bid Response from CWire's OpenRTB endpoint.
+It maps the responses to core framework's OpenRTB 2.5 Bid Response object model.
+*/
+func (a *adapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.RequestData, httpRes *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if httpRes.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if httpRes.StatusCode != http.StatusOK {
+		return nil, []error{
+			fmt.Errorf("Unexpected status code: %d. Run with request.debug = 1 for more info", httpRes.StatusCode),
+		}
+	}
+
+	var resp openrtb2.BidResponse
+	if err := json.Unmarshal(httpRes.Body, &resp); err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Error while decoding response, err: %s", err),
+		}}
+	}
+
+	bidderResponse := adapters.NewBidderResponse()
+	bidderResponse.Currency = resp.Cur
+	for _, sb := range resp.SeatBid {
+		for i := range sb.Bid {
+			bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
+				Bid:     &sb.Bid[i],
+				BidType: openrtb_ext.BidTypeBanner,
+			})
+		}
+	}
+
+	return bidderResponse, nil
+}
diff --git a/adapters/definemedia/definemedia.go b/adapters/definemedia/definemedia.go
new file mode 100644
index 00000000..21687015
--- /dev/null
+++ b/adapters/definemedia/definemedia.go
@@ -0,0 +1,109 @@
+package definemedia
+
+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 Foo 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
+}
+
+// MakeRequests makes the HTTP requests which should be made to fetch bids.
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var errors []error
+	requestJSON, err := json.Marshal(request)
+	if err != nil {
+		errors = append(errors, err)
+		return nil, errors
+	}
+
+	requestData := &adapters.RequestData{
+		Method: "POST",
+		Uri:    a.endpoint,
+		Body:   requestJSON,
+	}
+
+	return []*adapters.RequestData{requestData}, errors
+
+}
+
+func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if response.StatusCode == http.StatusBadRequest {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
+		}}
+	}
+
+	if response.StatusCode != http.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
+		}}
+	}
+
+	var bidResp openrtb2.BidResponse
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+
+	bidsCapacity := 1
+	if len(bidResp.SeatBid) > 0 {
+		bidsCapacity = len(bidResp.SeatBid[0].Bid)
+	}
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(bidsCapacity)
+	var errors []error
+	for _, sb := range bidResp.SeatBid {
+		for i, bid := range sb.Bid {
+			bidType, err := getMediaTypeForBid(bid)
+			if err != nil {
+				errors = append(errors, err)
+				continue
+			}
+			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+				Bid:     &sb.Bid[i],
+				BidType: bidType,
+			})
+
+		}
+	}
+
+	return bidResponse, errors
+}
+
+func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
+	if bid.Ext != nil {
+		var bidExt openrtb_ext.ExtBid
+		err := json.Unmarshal(bid.Ext, &bidExt)
+		if err == nil && bidExt.Prebid != nil {
+			if (bidExt.Prebid.Type == openrtb_ext.BidTypeBanner) || (bidExt.Prebid.Type == openrtb_ext.BidTypeNative) {
+				return openrtb_ext.ParseBidType(string(bidExt.Prebid.Type))
+			}
+			return "", &errortypes.BadServerResponse{
+				Message: fmt.Sprintf("Invalid mediatype in the impression"),
+			}
+		}
+	}
+
+	return "", &errortypes.BadServerResponse{
+		Message: fmt.Sprintf("Failed to parse impression \"%s\" mediatype", bid.ImpID),
+	}
+}
diff --git a/adapters/synacormedia/synacormedia.go b/adapters/imds/imds.go
similarity index 82%
rename from adapters/synacormedia/synacormedia.go
rename to adapters/imds/imds.go
index 3a882b71..66bc14be 100644
--- a/adapters/synacormedia/synacormedia.go
+++ b/adapters/imds/imds.go
@@ -1,4 +1,4 @@
-package synacormedia
+package imds
 
 import (
 	"encoding/json"
@@ -14,7 +14,7 @@ import (
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
-type SynacorMediaAdapter struct {
+type adapter struct {
 	EndpointTemplate *template.Template
 }
 
@@ -27,20 +27,20 @@ type ReqExt struct {
 	SeatId string `json:"seatId"`
 }
 
-// Builder builds a new instance of the SynacorMedia adapter for the given bidder with the given config.
+// Builder builds a new instance of the Imds adapter for the given bidder with the given config.
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
 	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
 	if err != nil {
 		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
 	}
 
-	bidder := &SynacorMediaAdapter{
+	bidder := &adapter{
 		EndpointTemplate: template,
 	}
 	return bidder, nil
 }
 
-func (a *SynacorMediaAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
 	var errs []error
 	var bidRequests []*adapters.RequestData
 
@@ -53,11 +53,11 @@ func (a *SynacorMediaAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo
 	return bidRequests, errs
 }
 
-func (a *SynacorMediaAdapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) {
+func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) {
 	var errs []error
 	var validImps []openrtb2.Imp
 	var re *ReqExt
-	var firstExtImp *openrtb_ext.ExtImpSynacormedia = nil
+	var firstExtImp *openrtb_ext.ExtImpImds = nil
 
 	for _, imp := range request.Imp {
 		validExtImpObj, err := getExtImpObj(&imp) // getExtImpObj returns {seatId:"", tagId:""}
@@ -126,11 +126,11 @@ func (a *SynacorMediaAdapter) makeRequest(request *openrtb2.BidRequest) (*adapte
 }
 
 // Builds enpoint url based on adapter-specific pub settings from imp.ext
-func (adapter *SynacorMediaAdapter) buildEndpointURL(params *openrtb_ext.ExtImpSynacormedia) (string, error) {
+func (adapter *adapter) buildEndpointURL(params *openrtb_ext.ExtImpImds) (string, error) {
 	return macros.ResolveMacros(adapter.EndpointTemplate, macros.EndpointTemplateParams{Host: params.SeatId})
 }
 
-func getExtImpObj(imp *openrtb2.Imp) (*openrtb_ext.ExtImpSynacormedia, error) {
+func getExtImpObj(imp *openrtb2.Imp) (*openrtb_ext.ExtImpImds, error) {
 	var bidderExt adapters.ExtImpBidder
 	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
 		return nil, &errortypes.BadInput{
@@ -138,18 +138,18 @@ func getExtImpObj(imp *openrtb2.Imp) (*openrtb_ext.ExtImpSynacormedia, error) {
 		}
 	}
 
-	var synacormediaExt openrtb_ext.ExtImpSynacormedia
-	if err := json.Unmarshal(bidderExt.Bidder, &synacormediaExt); err != nil {
+	var imdsExt openrtb_ext.ExtImpImds
+	if err := json.Unmarshal(bidderExt.Bidder, &imdsExt); err != nil {
 		return nil, &errortypes.BadInput{
 			Message: err.Error(),
 		}
 	}
 
-	return &synacormediaExt, nil
+	return &imdsExt, nil
 }
 
 // MakeBids make the bids for the bid response.
-func (a *SynacorMediaAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
 	const errorMessage string = "Unexpected status code: %d. Run with request.debug = 1 for more info"
 	switch {
 	case response.StatusCode == http.StatusNoContent:
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 44005a7d..f8c4aecd 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -1447,6 +1447,8 @@ func isPossibleBidder(bidder string) bool {
 		return false
 	case openrtb_ext.BidderReservedTID:
 		return false
+	case openrtb_ext.BidderReservedAE:
+		return false
 	default:
 		return true
 	}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 71c8e987..f808a404 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -61,9 +61,11 @@ import (
 	"github.com/prebid/prebid-server/adapters/conversant"
 	"github.com/prebid/prebid-server/adapters/cpmstar"
 	"github.com/prebid/prebid-server/adapters/criteo"
+	"github.com/prebid/prebid-server/adapters/cwire"
 	"github.com/prebid/prebid-server/adapters/datablocks"
 	"github.com/prebid/prebid-server/adapters/decenterads"
 	"github.com/prebid/prebid-server/adapters/deepintent"
+	"github.com/prebid/prebid-server/adapters/definemedia"
 	"github.com/prebid/prebid-server/adapters/dianomi"
 	"github.com/prebid/prebid-server/adapters/dmx"
 	evolution "github.com/prebid/prebid-server/adapters/e_volution"
@@ -77,6 +79,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/grid"
 	"github.com/prebid/prebid-server/adapters/gumgum"
 	"github.com/prebid/prebid-server/adapters/huaweiads"
+	"github.com/prebid/prebid-server/adapters/imds"
 	"github.com/prebid/prebid-server/adapters/impactify"
 	"github.com/prebid/prebid-server/adapters/improvedigital"
 	"github.com/prebid/prebid-server/adapters/infytv"
@@ -134,7 +137,6 @@ import (
 	"github.com/prebid/prebid-server/adapters/sspBC"
 	"github.com/prebid/prebid-server/adapters/stroeerCore"
 	"github.com/prebid/prebid-server/adapters/suntContent"
-	"github.com/prebid/prebid-server/adapters/synacormedia"
 	"github.com/prebid/prebid-server/adapters/taboola"
 	"github.com/prebid/prebid-server/adapters/tappx"
 	"github.com/prebid/prebid-server/adapters/telaria"
@@ -223,9 +225,11 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderConversant:        conversant.Builder,
 		openrtb_ext.BidderCpmstar:           cpmstar.Builder,
 		openrtb_ext.BidderCriteo:            criteo.Builder,
+		openrtb_ext.BidderCWire:             cwire.Builder,
 		openrtb_ext.BidderDatablocks:        datablocks.Builder,
 		openrtb_ext.BidderDecenterAds:       decenterads.Builder,
 		openrtb_ext.BidderDeepintent:        deepintent.Builder,
+		openrtb_ext.BidderDefinemedia:       definemedia.Builder,
 		openrtb_ext.BidderDianomi:           dianomi.Builder,
 		openrtb_ext.BidderDmx:               dmx.Builder,
 		openrtb_ext.BidderEmxDigital:        emx_digital.Builder,
@@ -241,6 +245,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderGroupm:            pubmatic.Builder,
 		openrtb_ext.BidderGumGum:            gumgum.Builder,
 		openrtb_ext.BidderHuaweiAds:         huaweiads.Builder,
+		openrtb_ext.BidderImds:              imds.Builder,
 		openrtb_ext.BidderImpactify:         impactify.Builder,
 		openrtb_ext.BidderImprovedigital:    improvedigital.Builder,
 		openrtb_ext.BidderInfyTV:            infytv.Builder,
@@ -304,7 +309,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderStreamkey:         adtelligent.Builder,
 		openrtb_ext.BidderSuntContent:       suntContent.Builder,
 		openrtb_ext.BidderStroeerCore:       stroeerCore.Builder,
-		openrtb_ext.BidderSynacormedia:      synacormedia.Builder,
+		openrtb_ext.BidderSynacormedia:      imds.Builder,
 		openrtb_ext.BidderTaboola:           taboola.Builder,
 		openrtb_ext.BidderTappx:             tappx.Builder,
 		openrtb_ext.BidderTelaria:           telaria.Builder,
diff --git a/exchange/bidder.go b/exchange/bidder.go
index 969b1887..125a1714 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -275,6 +275,15 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde
 					}
 				}
 
+				// FLEDGE auctionconfig responses are sent separate from bids
+				if bidResponse.FledgeAuctionConfigs != nil {
+					if fledgeAuctionConfigs := seatBidMap[bidderRequest.BidderName].FledgeAuctionConfigs; fledgeAuctionConfigs != nil {
+						seatBidMap[bidderRequest.BidderName].FledgeAuctionConfigs = append(fledgeAuctionConfigs, bidResponse.FledgeAuctionConfigs...)
+					} else {
+						seatBidMap[bidderRequest.BidderName].FledgeAuctionConfigs = bidResponse.FledgeAuctionConfigs
+					}
+				}
+
 				if len(bidderRequest.BidderStoredResponses) > 0 {
 					//set imp ids back to response for bids with stored responses
 					for i := 0; i < len(bidResponse.Bids); i++ {
diff --git a/exchange/entities/entities.go b/exchange/entities/entities.go
index f038d6b2..d535af00 100644
--- a/exchange/entities/entities.go
+++ b/exchange/entities/entities.go
@@ -14,6 +14,9 @@ type PbsOrtbSeatBid struct {
 	// Currency is the Currency in which the Bids are made.
 	// Should be a valid Currency ISO code.
 	Currency string
+	// fledgeAuctionConfigs is quasi-opaque data passed back for in-browser interest group auction.
+	// if exists, it should be passed through even if bids[] is empty.
+	FledgeAuctionConfigs []*openrtb_ext.FledgeAuctionConfig
 	// HttpCalls is the list of debugging info. It should only be populated if the request.test == 1.
 	// This will become response.ext.debug.httpcalls.{bidder} on the final Response.
 	HttpCalls []*openrtb_ext.ExtHttpCall
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 9686fc48..2c85b024 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -89,6 +89,7 @@ type bidResponseWrapper struct {
 	adapterSeatBids []*entities.PbsOrtbSeatBid
 	adapterExtra    *seatResponseExtra
 	bidder          openrtb_ext.BidderName
+	adapter         openrtb_ext.BidderName
 }
 
 type BidIDGenerator interface {
@@ -286,13 +287,14 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 
 	var adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid
 	var adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra
+	var fledge *openrtb_ext.Fledge
 	var anyBidsReturned bool
 
 	// List of bidders we have requests for.
 	var liveAdapters []openrtb_ext.BidderName
 
 	if len(r.StoredAuctionResponses) > 0 {
-		adapterBids, liveAdapters, err = buildStoredAuctionResponse(r.StoredAuctionResponses)
+		adapterBids, fledge, liveAdapters, err = buildStoredAuctionResponse(r.StoredAuctionResponses)
 		if err != nil {
 			return nil, err
 		}
@@ -310,7 +312,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 			alternateBidderCodes = *r.Account.AlternateBidderCodes
 		}
 
-		adapterBids, adapterExtra, anyBidsReturned = e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, accountDebugAllow, r.GlobalPrivacyControlHeader, debugLog.DebugOverride, alternateBidderCodes, requestExt.Prebid.Experiment, r.HookExecutor)
+		adapterBids, adapterExtra, fledge, anyBidsReturned = e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, accountDebugAllow, r.GlobalPrivacyControlHeader, debugLog.DebugOverride, alternateBidderCodes, requestExt.Prebid.Experiment, r.HookExecutor)
 	}
 
 	var auc *auction
@@ -357,7 +359,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 				errs = append(errs, dealErrs...)
 			}
 
-			bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, requestExt.Prebid.Passthrough, errs)
+			bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, requestExt.Prebid.Passthrough, fledge, errs)
 			if debugLog.DebugEnabledOrOverridden {
 				if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil {
 					debugLog.Data.Response = string(bidRespExtBytes)
@@ -375,9 +377,9 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 			targData.setTargeting(auc, r.BidRequestWrapper.BidRequest.App != nil, bidCategory, r.Account.TruncateTargetAttribute)
 
 		}
-		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, requestExt.Prebid.Passthrough, errs)
+		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, requestExt.Prebid.Passthrough, fledge, errs)
 	} else {
-		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, requestExt.Prebid.Passthrough, errs)
+		bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, responseDebugAllow, requestExt.Prebid.Passthrough, fledge, errs)
 
 		if debugLog.DebugEnabledOrOverridden {
 
@@ -527,7 +529,9 @@ func (e *exchange) getAllBids(
 	experiment *openrtb_ext.Experiment,
 	hookExecutor hookexecution.StageExecutor) (
 	map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid,
-	map[openrtb_ext.BidderName]*seatResponseExtra, bool) {
+	map[openrtb_ext.BidderName]*seatResponseExtra,
+	*openrtb_ext.Fledge,
+	bool) {
 	// Set up pointers to the bid results
 	adapterBids := make(map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, len(bidderRequests))
 	adapterExtra := make(map[openrtb_ext.BidderName]*seatResponseExtra, len(bidderRequests))
@@ -544,6 +548,7 @@ func (e *exchange) getAllBids(
 			}
 			brw := new(bidResponseWrapper)
 			brw.bidder = bidderRequest.BidderName
+			brw.adapter = bidderRequest.BidderCoreName
 			// Defer basic metrics to insure we capture them after all the values have been set
 			defer func() {
 				e.me.RecordAdapterRequest(bidderRequest.BidderLabels)
@@ -593,18 +598,25 @@ func (e *exchange) getAllBids(
 		}, chBids)
 		go bidderRunner(bidder, conversions)
 	}
+	var fledge *openrtb_ext.Fledge
+
 	// Wait for the bidders to do their thing
 	for i := 0; i < len(bidderRequests); i++ {
 		brw := <-chBids
 
 		//if bidder returned no bids back - remove bidder from further processing
 		for _, seatBid := range brw.adapterSeatBids {
-			if seatBid != nil && len(seatBid.Bids) != 0 {
-				if _, ok := adapterBids[openrtb_ext.BidderName(seatBid.Seat)]; ok {
-					adapterBids[openrtb_ext.BidderName(seatBid.Seat)].Bids = append(adapterBids[openrtb_ext.BidderName(seatBid.Seat)].Bids, seatBid.Bids...)
-				} else {
-					adapterBids[openrtb_ext.BidderName(seatBid.Seat)] = seatBid
+			if seatBid != nil {
+				bidderName := openrtb_ext.BidderName(seatBid.Seat)
+				if len(seatBid.Bids) != 0 {
+					if val, ok := adapterBids[bidderName]; ok {
+						adapterBids[bidderName].Bids = append(val.Bids, seatBid.Bids...)
+					} else {
+						adapterBids[bidderName] = seatBid
+					}
 				}
+				// collect fledgeAuctionConfigs separately from bids, as empty seatBids may be discarded
+				fledge = collectFledgeFromSeatBid(fledge, bidderName, brw.adapter, seatBid)
 			}
 		}
 		//but we need to add all bidders data to adapterExtra to have metrics and other metadata
@@ -615,7 +627,26 @@ func (e *exchange) getAllBids(
 		}
 	}
 
-	return adapterBids, adapterExtra, bidsFound
+	return adapterBids, adapterExtra, fledge, bidsFound
+}
+
+func collectFledgeFromSeatBid(fledge *openrtb_ext.Fledge, bidderName openrtb_ext.BidderName, adapterName openrtb_ext.BidderName, seatBid *entities.PbsOrtbSeatBid) *openrtb_ext.Fledge {
+	if seatBid.FledgeAuctionConfigs != nil {
+		if fledge == nil {
+			fledge = &openrtb_ext.Fledge{
+				AuctionConfigs: make([]*openrtb_ext.FledgeAuctionConfig, 0, len(seatBid.FledgeAuctionConfigs)),
+			}
+		}
+		for _, config := range seatBid.FledgeAuctionConfigs {
+			fledge.AuctionConfigs = append(fledge.AuctionConfigs, &openrtb_ext.FledgeAuctionConfig{
+				Bidder:  bidderName.String(),
+				Adapter: config.Bidder,
+				ImpId:   config.ImpId,
+				Config:  config.Config,
+			})
+		}
+	}
+	return fledge
 }
 
 func (e *exchange) recoverSafely(bidderRequests []BidderRequest,
@@ -974,7 +1005,7 @@ func getPrimaryAdServer(adServerId int) (string, error) {
 }
 
 // Extract all the data from the SeatBids and build the ExtBidResponse
-func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, r AuctionRequest, debugInfo bool, passthrough json.RawMessage, errList []error) *openrtb_ext.ExtBidResponse {
+func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, r AuctionRequest, debugInfo bool, passthrough json.RawMessage, fledge *openrtb_ext.Fledge, errList []error) *openrtb_ext.ExtBidResponse {
 	bidResponseExt := &openrtb_ext.ExtBidResponse{
 		Errors:               make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage, len(adapterBids)),
 		Warnings:             make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage, len(adapterBids)),
@@ -987,18 +1018,20 @@ func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*en
 			ResolvedRequest: r.ResolvedBidRequest,
 		}
 	}
+
+	var auctionTimestamp int64
 	if !r.StartTime.IsZero() {
-		// auctiontimestamp is the only response.ext.prebid attribute we may emit
-		bidResponseExt.Prebid = &openrtb_ext.ExtResponsePrebid{
-			AuctionTimestamp: r.StartTime.UnixNano() / 1e+6,
-		}
+		auctionTimestamp = r.StartTime.UnixMilli()
 	}
 
-	if passthrough != nil {
-		if bidResponseExt.Prebid == nil {
-			bidResponseExt.Prebid = &openrtb_ext.ExtResponsePrebid{}
+	if auctionTimestamp > 0 ||
+		passthrough != nil ||
+		fledge != nil {
+		bidResponseExt.Prebid = &openrtb_ext.ExtResponsePrebid{
+			AuctionTimestamp: auctionTimestamp,
+			Passthrough:      passthrough,
+			Fledge:           fledge,
 		}
-		bidResponseExt.Prebid.Passthrough = passthrough
 	}
 
 	for bidderName, responseExtra := range adapterExtra {
@@ -1234,15 +1267,20 @@ func listBiddersWithRequests(bidderRequests []BidderRequest) []openrtb_ext.Bidde
 	return liveAdapters
 }
 
-func buildStoredAuctionResponse(storedAuctionResponses map[string]json.RawMessage) (map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, []openrtb_ext.BidderName, error) {
+func buildStoredAuctionResponse(storedAuctionResponses map[string]json.RawMessage) (
+	map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid,
+	*openrtb_ext.Fledge,
+	[]openrtb_ext.BidderName,
+	error) {
 
 	adapterBids := make(map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, 0)
+	var fledge *openrtb_ext.Fledge
 	liveAdapters := make([]openrtb_ext.BidderName, 0)
 	for impId, storedResp := range storedAuctionResponses {
 		var seatBids []openrtb2.SeatBid
 
 		if err := json.Unmarshal(storedResp, &seatBids); err != nil {
-			return nil, nil, err
+			return nil, nil, nil, err
 		}
 		for _, seat := range seatBids {
 			var bidsToAdd []*entities.PbsOrtbBid
@@ -1254,20 +1292,51 @@ func buildStoredAuctionResponse(storedAuctionResponses map[string]json.RawMessag
 
 			bidderName := openrtb_ext.BidderName(seat.Seat)
 
+			if seat.Ext != nil {
+				var seatExt openrtb_ext.ExtBidResponse
+				if err := json.Unmarshal(seat.Ext, &seatExt); err != nil {
+					return nil, nil, nil, err
+				}
+				// add in FLEDGE response with impId substituted
+				if seatExt.Prebid != nil &&
+					seatExt.Prebid.Fledge != nil &&
+					seatExt.Prebid.Fledge.AuctionConfigs != nil {
+					auctionConfigs := seatExt.Prebid.Fledge.AuctionConfigs
+					if fledge == nil {
+						fledge = &openrtb_ext.Fledge{
+							AuctionConfigs: make([]*openrtb_ext.FledgeAuctionConfig, 0, len(auctionConfigs)),
+						}
+					}
+					for _, config := range auctionConfigs {
+						newConfig := &openrtb_ext.FledgeAuctionConfig{
+							ImpId:   impId,
+							Bidder:  string(bidderName),
+							Adapter: string(bidderName),
+							Config:  config.Config,
+						}
+						fledge.AuctionConfigs = append(fledge.AuctionConfigs, newConfig)
+					}
+				}
+			}
+
 			if _, ok := adapterBids[bidderName]; ok {
 				adapterBids[bidderName].Bids = append(adapterBids[bidderName].Bids, bidsToAdd...)
 
 			} else {
 				//create new seat bid and add it to live adapters
 				liveAdapters = append(liveAdapters, bidderName)
-				newSeatBid := entities.PbsOrtbSeatBid{bidsToAdd, "", nil, ""}
+				newSeatBid := entities.PbsOrtbSeatBid{
+					Bids:     bidsToAdd,
+					Currency: "",
+					Seat:     "",
+				}
 				adapterBids[bidderName] = &newSeatBid
 
 			}
 		}
 	}
 
-	return adapterBids, liveAdapters, nil
+	return adapterBids, fledge, liveAdapters, nil
 }
 
 func isAdsCertEnabled(experiment *openrtb_ext.Experiment, info config.BidderInfo) bool {
diff --git a/exchange/utils.go b/exchange/utils.go
index 962afe40..3453769d 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -450,17 +450,29 @@ func splitImps(imps []openrtb2.Imp) (map[string][]openrtb2.Imp, error) {
 	return bidderImps, nil
 }
 
+var allowedImpExtFields = map[string]interface{}{
+	openrtb_ext.AuctionEnvironmentKey:       struct{}{},
+	openrtb_ext.FirstPartyDataExtKey:        struct{}{},
+	openrtb_ext.FirstPartyDataContextExtKey: struct{}{},
+	openrtb_ext.GPIDKey:                     struct{}{},
+	openrtb_ext.SKAdNExtKey:                 struct{}{},
+	openrtb_ext.TIDKey:                      struct{}{},
+}
+
+var allowedImpExtPrebidFields = map[string]interface{}{
+	openrtb_ext.IsRewardedInventoryKey: struct{}{},
+	openrtb_ext.OptionsKey:             struct{}{},
+}
+
 func createSanitizedImpExt(impExt, impExtPrebid map[string]json.RawMessage) (map[string]json.RawMessage, error) {
 	sanitizedImpExt := make(map[string]json.RawMessage, 6)
 	sanitizedImpPrebidExt := make(map[string]json.RawMessage, 2)
 
 	// copy allowed imp[].ext.prebid fields
-	if v, exists := impExtPrebid["is_rewarded_inventory"]; exists {
-		sanitizedImpPrebidExt["is_rewarded_inventory"] = v
-	}
-
-	if v, exists := impExtPrebid["options"]; exists {
-		sanitizedImpPrebidExt["options"] = v
+	for k := range allowedImpExtPrebidFields {
+		if v, exists := impExtPrebid[k]; exists {
+			sanitizedImpPrebidExt[k] = v
+		}
 	}
 
 	// marshal sanitized imp[].ext.prebid
@@ -473,38 +485,15 @@ func createSanitizedImpExt(impExt, impExtPrebid map[string]json.RawMessage) (map
 	}
 
 	// copy reserved imp[].ext fields known to not be bidder names
-	if v, exists := impExt[openrtb_ext.FirstPartyDataExtKey]; exists {
-		sanitizedImpExt[openrtb_ext.FirstPartyDataExtKey] = v
-	}
-
-	if v, exists := impExt[openrtb_ext.FirstPartyDataContextExtKey]; exists {
-		sanitizedImpExt[openrtb_ext.FirstPartyDataContextExtKey] = v
-	}
-
-	if v, exists := impExt[openrtb_ext.SKAdNExtKey]; exists {
-		sanitizedImpExt[openrtb_ext.SKAdNExtKey] = v
-	}
-
-	if v, exists := impExt[string(openrtb_ext.GPIDKey)]; exists {
-		sanitizedImpExt[openrtb_ext.GPIDKey] = v
-	}
-
-	if v, exists := impExt[string(openrtb_ext.TIDKey)]; exists {
-		sanitizedImpExt[openrtb_ext.TIDKey] = v
+	for k := range allowedImpExtFields {
+		if v, exists := impExt[k]; exists {
+			sanitizedImpExt[k] = v
+		}
 	}
 
 	return sanitizedImpExt, nil
 }
 
-func isSpecialField(bidder string) bool {
-	return bidder == openrtb_ext.FirstPartyDataContextExtKey ||
-		bidder == openrtb_ext.FirstPartyDataExtKey ||
-		bidder == openrtb_ext.SKAdNExtKey ||
-		bidder == openrtb_ext.GPIDKey ||
-		bidder == openrtb_ext.PrebidExtKey ||
-		bidder == openrtb_ext.TIDKey
-}
-
 // prepareUser changes req.User so that it's ready for the given bidder.
 // This *will* mutate the request, but will *not* mutate any objects nested inside it.
 //
diff --git a/hooks/hookanalytics/analytics.go b/hooks/hookanalytics/analytics.go
index 7907e57d..322a04b9 100644
--- a/hooks/hookanalytics/analytics.go
+++ b/hooks/hookanalytics/analytics.go
@@ -28,7 +28,7 @@ type Result struct {
 }
 
 type AppliedTo struct {
-	Bidders  []string `json:"bidders,omitempty"`
+	Bidder   string   `json:"bidder,omitempty"`
 	BidIds   []string `json:"bidids,omitempty"`
 	ImpIds   []string `json:"impids,omitempty"`
 	Request  bool     `json:"request,omitempty"`
diff --git a/hooks/hookexecution/execution.go b/hooks/hookexecution/execution.go
index 1d9b4a52..20fa76d8 100644
--- a/hooks/hookexecution/execution.go
+++ b/hooks/hookexecution/execution.go
@@ -164,6 +164,9 @@ func handleHookResponses[P any](
 	return groupOutcome, payload, groupModuleCtx, nil
 }
 
+// moduleReplacer changes unwanted symbols to be in compliance with metric naming requirements
+var moduleReplacer = strings.NewReplacer(".", "_", "-", "_")
+
 // handleHookResponse is a strategy function that selects and applies
 // one of the available algorithms to handle hook response.
 func handleHookResponse[P any](
@@ -173,7 +176,7 @@ func handleHookResponse[P any](
 	metricEngine metrics.MetricsEngine,
 ) (P, HookOutcome, *RejectError) {
 	var rejectErr *RejectError
-	labels := metrics.ModuleLabels{Module: hr.HookID.ModuleCode, Stage: ctx.stage, AccountID: ctx.accountId}
+	labels := metrics.ModuleLabels{Module: moduleReplacer.Replace(hr.HookID.ModuleCode), Stage: ctx.stage, AccountID: ctx.accountId}
 	metricEngine.RecordModuleCalled(labels, hr.ExecutionTime)
 
 	hookOutcome := HookOutcome{
diff --git a/hooks/hookexecution/executor.go b/hooks/hookexecution/executor.go
index 15fa7d03..a19e7368 100644
--- a/hooks/hookexecution/executor.go
+++ b/hooks/hookexecution/executor.go
@@ -215,7 +215,8 @@ func (e *hookExecutor) ExecuteRawBidderResponseStage(response *adapters.BidderRe
 	executionCtx := e.newContext(stageName)
 	payload := hookstage.RawBidderResponsePayload{Bids: response.Bids, Bidder: bidder}
 
-	outcome, _, contexts, reject := executeStage(executionCtx, plan, payload, handler, e.metricEngine)
+	outcome, payload, contexts, reject := executeStage(executionCtx, plan, payload, handler, e.metricEngine)
+	response.Bids = payload.Bids
 	outcome.Entity = entity(bidder)
 	outcome.Stage = stageName
 
diff --git a/hooks/hookstage/rawbidderresponse_mutations.go b/hooks/hookstage/rawbidderresponse_mutations.go
new file mode 100644
index 00000000..61c0de10
--- /dev/null
+++ b/hooks/hookstage/rawbidderresponse_mutations.go
@@ -0,0 +1,43 @@
+package hookstage
+
+import (
+	"errors"
+
+	"github.com/prebid/prebid-server/adapters"
+)
+
+func (c *ChangeSet[T]) RawBidderResponse() ChangeSetRawBidderResponse[T] {
+	return ChangeSetRawBidderResponse[T]{changeSet: c}
+}
+
+type ChangeSetRawBidderResponse[T any] struct {
+	changeSet *ChangeSet[T]
+}
+
+func (c ChangeSetRawBidderResponse[T]) Bids() ChangeSetBids[T] {
+	return ChangeSetBids[T]{changeSetRawBidderResponse: c}
+}
+
+func (c ChangeSetRawBidderResponse[T]) castPayload(p T) (RawBidderResponsePayload, error) {
+	if payload, ok := any(p).(RawBidderResponsePayload); ok {
+		return payload, nil
+	}
+	return RawBidderResponsePayload{}, errors.New("failed to cast RawBidderResponsePayload")
+}
+
+type ChangeSetBids[T any] struct {
+	changeSetRawBidderResponse ChangeSetRawBidderResponse[T]
+}
+
+func (c ChangeSetBids[T]) Update(bids []*adapters.TypedBid) {
+	c.changeSetRawBidderResponse.changeSet.AddMutation(func(p T) (T, error) {
+		bidderPayload, err := c.changeSetRawBidderResponse.castPayload(p)
+		if err == nil {
+			bidderPayload.Bids = bids
+		}
+		if payload, ok := any(bidderPayload).(T); ok {
+			return payload, nil
+		}
+		return p, errors.New("failed to cast RawBidderResponsePayload")
+	}, MutationUpdate, "bids")
+}
diff --git a/modules/prebid/ortb2blocking/analytics.go b/modules/prebid/ortb2blocking/analytics.go
new file mode 100644
index 00000000..4026b672
--- /dev/null
+++ b/modules/prebid/ortb2blocking/analytics.go
@@ -0,0 +1,94 @@
+package ortb2blocking
+
+import (
+	"github.com/prebid/prebid-server/hooks/hookanalytics"
+	"github.com/prebid/prebid-server/hooks/hookstage"
+)
+
+const enforceBlockingTag = "enforce_blocking"
+
+const (
+	attributesAnalyticKey = "attributes"
+	badvAnalyticKey       = "adomain"
+	cattaxAnalyticKey     = "bcat"
+	bappAnalyticKey       = "bundle"
+	battrAnalyticKey      = "attr"
+)
+
+// ortb2blocking module has only 1 activity: `enforce_blocking` which will be used in further result processing
+func newEnforceBlockingTags() hookanalytics.Analytics {
+	return hookanalytics.Analytics{
+		Activities: []hookanalytics.Activity{
+			{
+				Name:   enforceBlockingTag,
+				Status: hookanalytics.ActivityStatusSuccess,
+			},
+		},
+	}
+}
+
+func addFailedStatusTag(result *hookstage.HookResult[hookstage.RawBidderResponsePayload]) {
+	result.AnalyticsTags.Activities[0].Status = hookanalytics.ActivityStatusError
+}
+
+func addAllowedAnalyticTag(result *hookstage.HookResult[hookstage.RawBidderResponsePayload], bidder, ImpID string) {
+	newAllowedResult := hookanalytics.Result{
+		Status: hookanalytics.ResultStatusAllow,
+		AppliedTo: hookanalytics.AppliedTo{
+			Bidder: bidder,
+			ImpIds: []string{ImpID},
+		},
+	}
+
+	result.AnalyticsTags.Activities[0].Results = append(result.AnalyticsTags.Activities[0].Results, newAllowedResult)
+}
+
+func addBlockedAnalyticTag(
+	result *hookstage.HookResult[hookstage.RawBidderResponsePayload],
+	bidder, ImpID string,
+	failedAttributes []string,
+	data map[string]interface{},
+) {
+	values := make(map[string]interface{})
+
+	values[attributesAnalyticKey] = failedAttributes
+	for _, attribute := range [5]string{
+		"badv",
+		"bcat",
+		"cattax",
+		"bapp",
+		"battr",
+	} {
+		if _, ok := data[attribute]; ok {
+			analyticKey := getAnalyticKeyForAttribute(attribute)
+			values[analyticKey] = data[attribute]
+		}
+	}
+
+	newBlockedResult := hookanalytics.Result{
+		Status: hookanalytics.ResultStatusBlock,
+		Values: values,
+		AppliedTo: hookanalytics.AppliedTo{
+			Bidder: bidder,
+			ImpIds: []string{ImpID},
+		},
+	}
+
+	result.AnalyticsTags.Activities[0].Results = append(result.AnalyticsTags.Activities[0].Results, newBlockedResult)
+}
+
+// most of the attributes have their own representation for an analytic key
+func getAnalyticKeyForAttribute(attribute string) string {
+	switch attribute {
+	case "badv":
+		return badvAnalyticKey
+	case "cattax":
+		return cattaxAnalyticKey
+	case "bapp":
+		return bappAnalyticKey
+	case "battr":
+		return battrAnalyticKey
+	default:
+		return attribute
+	}
+}
diff --git a/modules/prebid/ortb2blocking/hook_bidderrequest.go b/modules/prebid/ortb2blocking/hook_bidderrequest.go
index 1f99cc8a..8b21e212 100644
--- a/modules/prebid/ortb2blocking/hook_bidderrequest.go
+++ b/modules/prebid/ortb2blocking/hook_bidderrequest.go
@@ -345,6 +345,10 @@ func getIds(override Override) ([]int, error) {
 	return override.Ids, nil
 }
 
+func getIsActive(override Override) (bool, error) {
+	return override.IsActive, nil
+}
+
 type mediaTypes map[string]struct{}
 
 func (m mediaTypes) String() string {
diff --git a/modules/prebid/ortb2blocking/hook_raw_bidder_response.go b/modules/prebid/ortb2blocking/hook_raw_bidder_response.go
new file mode 100644
index 00000000..e5ebbb6f
--- /dev/null
+++ b/modules/prebid/ortb2blocking/hook_raw_bidder_response.go
@@ -0,0 +1,382 @@
+package ortb2blocking
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	"github.com/prebid/openrtb/v17/adcom1"
+	"github.com/prebid/openrtb/v17/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/hooks/hookexecution"
+	"github.com/prebid/prebid-server/hooks/hookstage"
+)
+
+func handleRawBidderResponseHook(
+	cfg config,
+	payload hookstage.RawBidderResponsePayload,
+	moduleCtx hookstage.ModuleContext,
+) (result hookstage.HookResult[hookstage.RawBidderResponsePayload], err error) {
+	bidder := payload.Bidder
+	blockAttrsVal, ok := moduleCtx[bidder]
+	if !ok {
+		// if there are no blocking attributes for this bidder just pass empty blockingAttributes for further processing
+		// other values from config must still be checked
+		blockAttrsVal = blockingAttributes{}
+	}
+
+	blockAttrs, ok := blockAttrsVal.(blockingAttributes)
+	if !ok {
+		return result, hookexecution.NewFailure("could not cast blocking attributes for bidder `%s`, module context has incorrect data", payload.Bidder)
+	}
+
+	result.AnalyticsTags = newEnforceBlockingTags()
+
+	// allowedBids will store all bids that have passed the attribute check
+	allowedBids := make([]*adapters.TypedBid, 0)
+	for _, bid := range payload.Bids {
+
+		failedChecksData := make(map[string]interface{})
+		bidMediaTypes := mediaTypesFromBid(bid)
+		dealID := bid.Bid.DealID
+
+		failedChecksData, err = shouldBeBlockedDueToBadv(&result, cfg, blockAttrs, bid.Bid, bidMediaTypes, bidder, dealID, failedChecksData)
+		if err != nil {
+			addFailedStatusTag(&result)
+			return result, hookexecution.NewFailure("failed to process badv block checking: %s", err)
+		}
+
+		failedChecksData, enforceBlocks, err := shouldBeBlockedDueToBcat(&result, cfg, blockAttrs, bid.Bid, bidMediaTypes, bidder, dealID, failedChecksData)
+		if err != nil {
+			addFailedStatusTag(&result)
+			return result, hookexecution.NewFailure("failed to process bcat block checking: %s", err)
+		}
+
+		failedChecksData = shouldBeBlockedDueToCattax(blockAttrs, bid.Bid, enforceBlocks, failedChecksData)
+
+		failedChecksData, err = shouldBeBlockedDueToBapp(&result, cfg, blockAttrs, bid.Bid, bidMediaTypes, bidder, dealID, failedChecksData)
+		if err != nil {
+			addFailedStatusTag(&result)
+			return result, hookexecution.NewFailure("failed to process bapp block checking: %s", err)
+		}
+
+		failedChecksData, err = shouldBeBlockedDueToBattr(&result, cfg, blockAttrs, bid.Bid, bidMediaTypes, bidder, dealID, failedChecksData)
+		if err != nil {
+			addFailedStatusTag(&result)
+			return result, hookexecution.NewFailure("failed to process battr block checking: %s", err)
+		}
+
+		if len(failedChecksData) == 0 {
+			addAllowedAnalyticTag(&result, bidder, bid.Bid.ImpID)
+			allowedBids = append(allowedBids, bid)
+		} else {
+			failedAttributes := getFailedAttributes(failedChecksData)
+			addBlockedAnalyticTag(&result, bidder, bid.Bid.ImpID, failedAttributes, failedChecksData)
+			addDebugMessage(&result, bid.Bid, bidder, failedAttributes)
+		}
+	}
+
+	changeSet := hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{}
+	if len(payload.Bids) != len(allowedBids) {
+		changeSet.RawBidderResponse().Bids().Update(allowedBids)
+		result.ChangeSet = changeSet
+	}
+
+	return result, err
+}
+
+func mediaTypesFromBid(bid *adapters.TypedBid) mediaTypes {
+	return mediaTypes{string(bid.BidType): struct{}{}}
+}
+
+func shouldBeBlockedDueToBadv(
+	result *hookstage.HookResult[hookstage.RawBidderResponsePayload],
+	cfg config,
+	blockAttr blockingAttributes,
+	bid *openrtb2.Bid,
+	bidMediaTypes mediaTypes,
+	bidder string,
+	dealID string,
+	failedChecksData map[string]interface{},
+) (map[string]interface{}, error) {
+	badv := cfg.Attributes.Badv
+
+	enforceBlocks, message, err := firstOrDefaultOverride(bidder, bidMediaTypes, getIsActive, badv.ActionOverrides.EnforceBlocks, badv.EnforceBlocks)
+	result.Warnings = mergeStrings(result.Warnings, message)
+	if err != nil {
+		return failedChecksData, fmt.Errorf("failed to get override for badv.enforce_blocks: %s", err)
+	}
+	if !enforceBlocks {
+		return failedChecksData, nil
+	}
+
+	blockUnknown, message, err := firstOrDefaultOverride(bidder, bidMediaTypes, getIsActive, badv.ActionOverrides.BlockUnknownAdomain, badv.BlockUnknownAdomain)
+	result.Warnings = mergeStrings(result.Warnings, message)
+	if err != nil {
+		return failedChecksData, fmt.Errorf("failed to get override for badv.block_unknown_adomain: %s", err)
+	}
+
+	var dealExceptions []string
+	if dealID != "" {
+		dealExceptions, err = findDealExceptionsOverridesOrDefault(dealID, getNames, badv.ActionOverrides.AllowedAdomainForDeals, badv.AllowedAdomainForDeals)
+		if err != nil {
+			return failedChecksData, fmt.Errorf("failed to get override for badv.allowed_adomain_for_deals: %s", err)
+		}
+	}
+
+	shouldBlock, blockedAttributes := blockAttribute(bid.ADomain, blockAttr.bAdv, dealExceptions, blockUnknown)
+	if shouldBlock {
+		failedChecksData["badv"] = blockedAttributes
+	}
+
+	return failedChecksData, nil
+}
+
+func shouldBeBlockedDueToBcat(
+	result *hookstage.HookResult[hookstage.RawBidderResponsePayload],
+	cfg config,
+	blockAttr blockingAttributes,
+	bid *openrtb2.Bid,
+	bidMediaTypes mediaTypes,
+	bidder, dealID string,
+	failedChecksData map[string]interface{},
+) (map[string]interface{}, bool, error) {
+	bcat := cfg.Attributes.Bcat
+
+	enforceBlocks, message, err := firstOrDefaultOverride(bidder, bidMediaTypes, getIsActive, bcat.ActionOverrides.EnforceBlocks, bcat.EnforceBlocks)
+	result.Warnings = mergeStrings(result.Warnings, message)
+	if err != nil {
+		return failedChecksData, false, fmt.Errorf("failed to get override for bcat.enforce_blocks: %s", err)
+	}
+	if !enforceBlocks {
+		return failedChecksData, false, nil
+	}
+
+	blockUnknown, message, err := firstOrDefaultOverride(bidder, bidMediaTypes, getIsActive, bcat.ActionOverrides.BlockUnknownAdvCat, bcat.BlockUnknownAdvCat)
+	result.Warnings = mergeStrings(result.Warnings, message)
+	if err != nil {
+		return failedChecksData, enforceBlocks, fmt.Errorf("failed to get override for bcat.block_unknown_adv_cat: %s", err)
+	}
+
+	var dealExceptions []string
+	if dealID != "" {
+		dealExceptions, err = findDealExceptionsOverridesOrDefault(dealID, getNames, bcat.ActionOverrides.AllowedAdvCatForDeals, bcat.AllowedAdvCatForDeals)
+		if err != nil {
+			return failedChecksData, enforceBlocks, fmt.Errorf("failed to get override for bcat.allowed_adv_cat_for_deals: %s", err)
+		}
+	}
+
+	shouldBlock, blockedAttributes := blockAttribute(bid.Cat, blockAttr.bCat, dealExceptions, blockUnknown)
+	if shouldBlock {
+		failedChecksData["bcat"] = blockedAttributes
+	}
+
+	return failedChecksData, enforceBlocks, nil
+}
+
+func shouldBeBlockedDueToCattax(
+	blockAttr blockingAttributes,
+	bid *openrtb2.Bid,
+	enforceBlocks bool,
+	failedChecksData map[string]interface{},
+) map[string]interface{} {
+	if !enforceBlocks {
+		return failedChecksData
+	}
+
+	cattax := bid.CatTax
+	if cattax == 0 {
+		return failedChecksData
+	}
+
+	// if blocking cattax was not specified use a default value
+	if blockAttr.catTax == 0 {
+		blockAttr.catTax = adcom1.CatTaxIABContent10
+	}
+
+	// cattax check has a reverse logic, the blockAttr.cattax should have one allowed value
+	if cattax != blockAttr.catTax {
+		failedChecksData["cattax"] = []adcom1.CategoryTaxonomy{cattax}
+	}
+
+	return failedChecksData
+}
+
+func shouldBeBlockedDueToBapp(
+	result *hookstage.HookResult[hookstage.RawBidderResponsePayload],
+	cfg config,
+	blockAttr blockingAttributes,
+	bid *openrtb2.Bid,
+	bidMediaTypes mediaTypes,
+	bidder, dealID string,
+	failedChecksData map[string]interface{},
+) (map[string]interface{}, error) {
+	bapp := cfg.Attributes.Bapp
+
+	enforceBlocks, message, err := firstOrDefaultOverride(bidder, bidMediaTypes, getIsActive, bapp.ActionOverrides.EnforceBlocks, bapp.EnforceBlocks)
+	result.Warnings = mergeStrings(result.Warnings, message)
+	if err != nil {
+		return failedChecksData, fmt.Errorf("failed to get override for bapp.enforce_blocks: %s", err)
+	}
+	if !enforceBlocks {
+		return failedChecksData, nil
+	}
+
+	var dealExceptions []string
+	if dealID != "" {
+		dealExceptions, err = findDealExceptionsOverridesOrDefault(dealID, getNames, bapp.ActionOverrides.AllowedAppForDeals, bapp.AllowedAppForDeals)
+		if err != nil {
+			return failedChecksData, fmt.Errorf("failed to get override for bapp.allowed_app_for_deals: %s", err)
+		}
+	}
+
+	bidBapp := []string{bid.Bundle}
+
+	shouldBlock, blockedAttributes := blockAttribute(bidBapp, blockAttr.bApp, dealExceptions, false)
+	if shouldBlock {
+		failedChecksData["bapp"] = blockedAttributes
+	}
+
+	return failedChecksData, nil
+}
+
+func shouldBeBlockedDueToBattr(
+	result *hookstage.HookResult[hookstage.RawBidderResponsePayload],
+	cfg config,
+	blockAttr blockingAttributes,
+	bid *openrtb2.Bid,
+	bidMediaTypes mediaTypes,
+	bidder, dealID string,
+	failedChecksData map[string]interface{},
+) (map[string]interface{}, error) {
+	battr := cfg.Attributes.Battr
+
+	enforceBlocks, message, err := firstOrDefaultOverride(bidder, bidMediaTypes, getIsActive, battr.ActionOverrides.EnforceBlocks, battr.EnforceBlocks)
+	result.Warnings = mergeStrings(result.Warnings, message)
+	if err != nil {
+		return failedChecksData, fmt.Errorf("failed to get override for battr.enforce_blocks: %s", err)
+	}
+	if !enforceBlocks {
+		return failedChecksData, nil
+	}
+
+	var dealExceptions []int
+	if dealID != "" {
+		dealExceptions, err = findDealExceptionsOverridesOrDefault(dealID, getIds, battr.ActionOverrides.AllowedBannerAttrForDeals, battr.AllowedBannerAttrForDeals)
+		if err != nil {
+			return failedChecksData, fmt.Errorf("failed to get override for battr.allowed_banner_attr_for_deals: %s", err)
+		}
+	}
+
+	if blockAttr.bAttr == nil || len(blockAttr.bAttr) == 0 {
+		return failedChecksData, nil
+	}
+	blockedBattr := blockAttr.bAttr[bid.ImpID]
+	bidAttr := toInt(bid.Attr)
+
+	shouldBlock, blockedAttributes := blockAttribute(bidAttr, blockedBattr, dealExceptions, false)
+	if shouldBlock {
+		failedChecksData["battr"] = blockedAttributes
+	}
+
+	return failedChecksData, nil
+}
+
+func blockAttribute[Attribute comparable](attributes, blockedAttributes, dealExceptions []Attribute, blockUnknown bool) (bool, []Attribute) {
+	if len(attributes) == 0 {
+		if blockUnknown {
+			return true, nil
+		}
+		return false, nil
+	}
+
+	if len(blockedAttributes) == 0 {
+		return false, nil
+	}
+
+	blockedElements := make([]Attribute, 0)
+	shouldBlock := true
+	for _, attribute := range attributes {
+		for _, blockedAttribute := range blockedAttributes {
+			if attribute == blockedAttribute {
+				for _, allowedAttribute := range dealExceptions {
+					if attribute == allowedAttribute {
+						shouldBlock = false
+						break
+					}
+				}
+				if shouldBlock {
+					blockedElements = append(blockedElements, attribute)
+				}
+			}
+			shouldBlock = true
+		}
+	}
+
+	if len(blockedElements) == 0 {
+		return false, nil
+	}
+
+	return true, blockedElements
+}
+
+func findDealExceptionsOverridesOrDefault[T any](
+	dealID string,
+	overrideGetter overrideGetterFn[[]T],
+	actionOverrides []ActionOverride,
+	defaultOverride []T,
+) (overrides []T, err error) {
+	for _, action := range actionOverrides {
+		if action.Conditions.DealIds == nil {
+			return overrides, errors.New("conditions field in account configuration must contain deal_ids")
+		}
+
+		for _, id := range action.Conditions.DealIds {
+			if id == dealID {
+				actionOverride, err := overrideGetter(action.Override)
+				if err != nil {
+					return overrides, err
+				}
+				overrides = append(overrides, actionOverride...)
+			}
+		}
+	}
+
+	if len(overrides) == 0 {
+		return defaultOverride, nil
+	}
+
+	overrides = append(overrides, defaultOverride...)
+
+	return overrides, nil
+}
+
+func addDebugMessage(
+	result *hookstage.HookResult[hookstage.RawBidderResponsePayload],
+	bid *openrtb2.Bid,
+	bidder string,
+	failedAttributes []string,
+) {
+	result.DebugMessages = append(
+		result.DebugMessages,
+		fmt.Sprintf("Bid %s from bidder %s has been rejected, failed checks: %s", bid.ID, bidder, strings.Join(failedAttributes, ", ")),
+	)
+}
+
+// returns a slice with the names of failed attributes
+func getFailedAttributes(data map[string]interface{}) []string {
+	var builder []string
+	for _, attribute := range [5]string{
+		"badv",
+		"bcat",
+		"cattax",
+		"bapp",
+		"battr",
+	} {
+		if _, ok := data[attribute]; ok {
+			builder = append(builder, attribute)
+		}
+	}
+
+	return builder
+}
diff --git a/modules/prebid/ortb2blocking/module.go b/modules/prebid/ortb2blocking/module.go
index 8f8c2968..1f83a9f6 100644
--- a/modules/prebid/ortb2blocking/module.go
+++ b/modules/prebid/ortb2blocking/module.go
@@ -35,6 +35,25 @@ func (m Module) HandleBidderRequestHook(
 	return handleBidderRequestHook(cfg, payload)
 }
 
+// HandleRawBidderResponseHook rejects bids for a specific bidder if they fail the attribute check.
+func (m Module) HandleRawBidderResponseHook(
+	_ context.Context,
+	miCtx hookstage.ModuleInvocationContext,
+	payload hookstage.RawBidderResponsePayload,
+) (hookstage.HookResult[hookstage.RawBidderResponsePayload], error) {
+	result := hookstage.HookResult[hookstage.RawBidderResponsePayload]{}
+	var cfg config
+	if len(miCtx.AccountConfig) != 0 {
+		ncfg, err := newConfig(miCtx.AccountConfig)
+		if err != nil {
+			return result, err
+		}
+		cfg = ncfg
+	}
+
+	return handleRawBidderResponseHook(cfg, payload, miCtx.ModuleContext)
+}
+
 type blockingAttributes struct {
 	bAdv   []string
 	bApp   []string
diff --git a/modules/prebid/ortb2blocking/utils.go b/modules/prebid/ortb2blocking/utils.go
index affd5a47..eb148b02 100644
--- a/modules/prebid/ortb2blocking/utils.go
+++ b/modules/prebid/ortb2blocking/utils.go
@@ -1,6 +1,11 @@
 package ortb2blocking
 
-import "strings"
+import (
+	"strings"
+
+	"github.com/prebid/openrtb/v17/adcom1"
+	"github.com/prebid/openrtb/v17/openrtb2"
+)
 
 func mergeStrings(messages []string, newMessages ...string) []string {
 	for _, msg := range newMessages {
@@ -20,3 +25,15 @@ func hasMatches(list []string, s string) bool {
 	}
 	return false
 }
+
+type numeric interface {
+	openrtb2.BannerAdType | adcom1.CreativeAttribute
+}
+
+func toInt[T numeric](values []T) []int {
+	ints := make([]int, len(values))
+	for i := range values {
+		ints[i] = int(values[i])
+	}
+	return ints
+}
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index fe128f2e..8f0fce6e 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -36,6 +36,7 @@ const (
 	BidderReservedPrebid  BidderName = "prebid"  // Reserved for Prebid Server configuration.
 	BidderReservedSKAdN   BidderName = "skadn"   // Reserved for Apple's SKAdNetwork OpenRTB extension.
 	BidderReservedTID     BidderName = "tid"     // Reserved for Per-Impression Transactions IDs for Multi-Impression Bid Requests.
+	BidderReservedAE      BidderName = "ae"      // Reserved for FLEDGE Auction Environment
 )
 
 // IsBidderNameReserved returns true if the specified name is a case insensitive match for a reserved bidder name.
@@ -72,6 +73,10 @@ func IsBidderNameReserved(name string) bool {
 		return true
 	}
 
+	if strings.EqualFold(name, string(BidderReservedAE)) {
+		return true
+	}
+
 	return false
 }
 
@@ -141,9 +146,11 @@ const (
 	BidderConversant        BidderName = "conversant"
 	BidderCpmstar           BidderName = "cpmstar"
 	BidderCriteo            BidderName = "criteo"
+	BidderCWire             BidderName = "cwire"
 	BidderDatablocks        BidderName = "datablocks"
 	BidderDecenterAds       BidderName = "decenterads"
 	BidderDeepintent        BidderName = "deepintent"
+	BidderDefinemedia       BidderName = "definemedia"
 	BidderDianomi           BidderName = "dianomi"
 	BidderDmx               BidderName = "dmx"
 	BidderEmxDigital        BidderName = "emx_digital"
@@ -159,6 +166,7 @@ const (
 	BidderGroupm            BidderName = "groupm"
 	BidderGumGum            BidderName = "gumgum"
 	BidderHuaweiAds         BidderName = "huaweiads"
+	BidderImds              BidderName = "imds"
 	BidderImpactify         BidderName = "impactify"
 	BidderImprovedigital    BidderName = "improvedigital"
 	BidderInfyTV            BidderName = "infytv"
@@ -312,9 +320,11 @@ func CoreBidderNames() []BidderName {
 		BidderConversant,
 		BidderCpmstar,
 		BidderCriteo,
+		BidderCWire,
 		BidderDatablocks,
 		BidderDecenterAds,
 		BidderDeepintent,
+		BidderDefinemedia,
 		BidderDianomi,
 		BidderDmx,
 		BidderEmxDigital,
@@ -330,6 +340,7 @@ func CoreBidderNames() []BidderName {
 		BidderGroupm,
 		BidderGumGum,
 		BidderHuaweiAds,
+		BidderImds,
 		BidderImpactify,
 		BidderImprovedigital,
 		BidderInfyTV,
diff --git a/openrtb_ext/imp.go b/openrtb_ext/imp.go
index 9e62a97f..e0fdd2d7 100644
--- a/openrtb_ext/imp.go
+++ b/openrtb_ext/imp.go
@@ -4,6 +4,24 @@ import (
 	"encoding/json"
 )
 
+// AuctionEnvironmentType is a Google Privacy Sandbox flag indicating where the auction may take place
+type AuctionEnvironmentType int8
+
+const (
+	// 0 Standard server-side auction
+	ServerSideAuction AuctionEnvironmentType = 0
+	// 1 On-device interest group auction (FLEDGE)
+	OnDeviceIGAuctionFledge AuctionEnvironmentType = 1
+	// 2 Server-side with interest group simulation
+	ServerSideWithIGSimulation AuctionEnvironmentType = 2
+)
+
+// IsRewardedInventoryKey is the json key for ExtImpPrebid.IsRewardedInventory
+const IsRewardedInventoryKey = "is_rewarded_inventory"
+
+// OptionsKey is the json key for ExtImpPrebid.Options
+const OptionsKey = "options"
+
 // ExtImpPrebid defines the contract for bidrequest.imp[i].ext.prebid
 type ExtImpPrebid struct {
 	// StoredRequest specifies which stored impression to use, if any.
diff --git a/openrtb_ext/imp_cwire.go b/openrtb_ext/imp_cwire.go
new file mode 100644
index 00000000..a0a171cc
--- /dev/null
+++ b/openrtb_ext/imp_cwire.go
@@ -0,0 +1,10 @@
+package openrtb_ext
+
+// ImpExtCwire defines the contract for MakeRequests `request.imp[i].ext.bidder`
+type ImpExtCWire struct {
+	PlacementID int      `json:"placementId,omitempty"`
+	PageID      int      `json:"pageId,omitempty"`
+	CwCreative  string   `json:"cwcreative,omitempty"`
+	CwDebug     bool     `json:"cwdebug,omitempty"`
+	CwFeatures  []string `json:"cwfeatures,omitempty"`
+}
diff --git a/openrtb_ext/imp_definemedia.go b/openrtb_ext/imp_definemedia.go
new file mode 100644
index 00000000..aa94bf5d
--- /dev/null
+++ b/openrtb_ext/imp_definemedia.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ImpExtDefinemedia struct {
+	MandantID int64 `json:"mandantId"`
+	AdslotID  int64 `json:"adslotId"`
+}
diff --git a/openrtb_ext/imp_imds.go b/openrtb_ext/imp_imds.go
new file mode 100644
index 00000000..97f32e90
--- /dev/null
+++ b/openrtb_ext/imp_imds.go
@@ -0,0 +1,7 @@
+package openrtb_ext
+
+// ExtImpImds defines the contract for bidrequest.imp[i].ext.prebid.bidder.imds
+type ExtImpImds struct {
+	SeatId string `json:"seatId"`
+	TagId  string `json:"tagId"`
+}
diff --git a/openrtb_ext/imp_synacormedia.go b/openrtb_ext/imp_synacormedia.go
deleted file mode 100644
index 2c62a6d3..00000000
--- a/openrtb_ext/imp_synacormedia.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package openrtb_ext
-
-// ExtImpSynacormedia defines the contract for bidrequest.imp[i].ext.prebid.bidder.synacormedia
-type ExtImpSynacormedia struct {
-	SeatId string `json:"seatId"`
-	TagId  string `json:"tagId"`
-}
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index 22f6b127..6559e632 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -22,6 +22,9 @@ const GPIDKey = "gpid"
 // TIDKey reserved for Per-Impression Transactions IDs for Multi-Impression Bid Requests.
 const TIDKey = "tid"
 
+// AuctionEnvironmentKey is the json key under imp[].ext for ExtImp.AuctionEnvironment
+const AuctionEnvironmentKey = string(BidderReservedAE)
+
 // NativeExchangeSpecificLowerBound defines the lower threshold of exchange specific types for native ads. There is no upper bound.
 const NativeExchangeSpecificLowerBound = 500
 
diff --git a/openrtb_ext/response.go b/openrtb_ext/response.go
index 68097504..e920ee58 100644
--- a/openrtb_ext/response.go
+++ b/openrtb_ext/response.go
@@ -42,6 +42,20 @@ 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"`
+}
+
+// FledgeResponse defines the contract for bidresponse.ext.fledge
+type Fledge struct {
+	AuctionConfigs []*FledgeAuctionConfig `json:"auctionconfigs,omitempty"`
+}
+
+// FledgeAuctionConfig defines the container for bidresponse.ext.fledge.auctionconfigs[]
+type FledgeAuctionConfig struct {
+	ImpId   string          `json:"impid"`
+	Bidder  string          `json:"bidder,omitempty"`
+	Adapter string          `json:"adapter,omitempty"`
+	Config  json.RawMessage `json:"config"`
 }
 
 // ExtUserSync defines the contract for bidresponse.ext.usersync.{bidder}.syncs[i]
diff --git a/static/bidder-info/cwire.yaml b/static/bidder-info/cwire.yaml
new file mode 100644
index 00000000..95e51d53
--- /dev/null
+++ b/static/bidder-info/cwire.yaml
@@ -0,0 +1,10 @@
+endpoint: "https://ortb.cwi.re/v1/bid"
+maintainer:
+  email: "devs@cwire.com"
+endpointCompression: gzip
+gvlVendorID: 1081
+modifyingVastXmlAllowed: false
+capabilities:
+  site:
+    mediaTypes:
+      - banner
diff --git a/static/bidder-info/definemedia.yaml b/static/bidder-info/definemedia.yaml
new file mode 100644
index 00000000..d79c3835
--- /dev/null
+++ b/static/bidder-info/definemedia.yaml
@@ -0,0 +1,10 @@
+endpoint: "https://rtb.conative.network/openrtb2/auction"
+maintainer:
+  email: "d.joest@definemedia.de"
+  gvlVendorID: 440  # GDPR vendor list ID
+capabilities:
+
+  site:
+    mediaTypes:
+      - banner
+      - native
diff --git a/static/bidder-info/imds.yaml b/static/bidder-info/imds.yaml
new file mode 100644
index 00000000..3f27c766
--- /dev/null
+++ b/static/bidder-info/imds.yaml
@@ -0,0 +1,20 @@
+endpoint: "http://{{.Host}}.technoratimedia.com/openrtb/bids/{{.Host}}"
+maintainer:
+  email: "eng-demand@imds.tv"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+  site:
+    mediaTypes:
+      - banner
+      - video
+userSync:
+  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]"
diff --git a/static/bidder-info/synacormedia.yaml b/static/bidder-info/synacormedia.yaml
index 30f5308c..47d66458 100644
--- a/static/bidder-info/synacormedia.yaml
+++ b/static/bidder-info/synacormedia.yaml
@@ -1,3 +1,4 @@
+# DEPRECATED: Use imds bidder instead
 endpoint: "http://{{.Host}}.technoratimedia.com/openrtb/bids/{{.Host}}"
 maintainer:
   email: "eng-demand@imds.tv"
@@ -11,6 +12,11 @@ capabilities:
       - banner
       - video
 userSync:
+  key: "imds"
+  supportCors: true
   iframe:
-    url: "https://ad-cdn.technoratimedia.com/html/usersync.html?cb={{.RedirectURL}}"
-    userMacro: "[USER_ID]"
\ No newline at end of file
+    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]"
diff --git a/static/bidder-params/cwire.json b/static/bidder-params/cwire.json
new file mode 100644
index 00000000..8d21e1a9
--- /dev/null
+++ b/static/bidder-params/cwire.json
@@ -0,0 +1,32 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "CWire Adapter Params",
+  "description": "A schema which validates params accepted by the CWire adapter",
+  "type": "object",
+  "properties": {
+    "placementId": {
+      "type": "integer",
+      "description": "An ID which identifies this placement of the impression"
+    },
+    "pageId": {
+      "type": "integer",
+      "description": "An ID which identifies the site selling the impression"
+    },
+    "cwcreative": {
+      "type": "string",
+      "description": "An CWire ID of the creative that we want to show"
+    },
+    "cwdebug": {
+      "type": "boolean",
+      "description": "Enable CWire debug mode"
+    },
+    "cwfeatures": {
+      "type": "array",
+      "items": {
+        "type": "string"
+      },
+      "description": "A string array of CWire features"
+    }
+  },
+  "required": []
+}
diff --git a/static/bidder-params/definemedia.json b/static/bidder-params/definemedia.json
new file mode 100644
index 00000000..bf5566b4
--- /dev/null
+++ b/static/bidder-params/definemedia.json
@@ -0,0 +1,19 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "Define Media Adapter Params",
+    "description": "A schema which validates params accepted by the DM adapter",
+    "type": "object",
+  
+    "properties": {
+      "mandantId": {
+        "type": "integer",
+        "description": "The DEFINE-MEDIA mandant id. This is a unique identifier for your account. Please contact your account manager for more information."
+      },
+
+      "adslotId":{
+        "type": "integer",
+        "description": "The adslot id. This is a unique identifier for your adslot and may change on subparts on a website. Please contact your account manager for more information."
+      }
+    },
+    "required": ["mandantId"]
+  }
\ No newline at end of file
diff --git a/static/bidder-params/imds.json b/static/bidder-params/imds.json
new file mode 100644
index 00000000..e5b05bcb
--- /dev/null
+++ b/static/bidder-params/imds.json
@@ -0,0 +1,19 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "iMedia Digital Services (IMDS) Adapter Params",
+  "description": "A schema which validates params accepted by the iMedia Digital Services (IMDS) adapter",
+
+  "type": "object",
+  "properties": {
+    "seatId": {
+      "type": "string",
+      "description": "The seat id."
+    },
+    "tagId": {
+      "type": "string",
+      "description": "The tag id."
+    }
+  },
+
+  "required": ["seatId"]
+}
diff --git a/static/bidder-params/synacormedia.json b/static/bidder-params/synacormedia.json
index 8c74ada2..21eb06cc 100644
--- a/static/bidder-params/synacormedia.json
+++ b/static/bidder-params/synacormedia.json
@@ -1,7 +1,7 @@
 {
   "$schema": "http://json-schema.org/draft-04/schema#",
   "title": "Synacormedia Adapter Params",
-  "description": "A schema which validates params accepted by the Synacormedia adapter",
+  "description": "DEPRECATED: Use imds bidder instead.  A schema which validates params accepted by the Synacormedia adapter",
 
   "type": "object",
   "properties": {


Test patch:
diff --git a/adapters/adapterstest/test_json.go b/adapters/adapterstest/test_json.go
index 6eab9a48..7381fc68 100644
--- a/adapters/adapterstest/test_json.go
+++ b/adapters/adapterstest/test_json.go
@@ -198,8 +198,9 @@ func (resp *httpResponse) ToResponseData(t *testing.T) *adapters.ResponseData {
 }
 
 type expectedBidResponse struct {
-	Bids     []expectedBid `json:"bids"`
-	Currency string        `json:"currency"`
+	Bids                 []expectedBid   `json:"bids"`
+	Currency             string          `json:"currency"`
+	FledgeAuctionConfigs json.RawMessage `json:"fledgeauctionconfigs,omitempty"`
 }
 
 type expectedBid struct {
@@ -255,11 +256,11 @@ func assertErrorList(t *testing.T, description string, actual []error, expected
 	}
 }
 
-func assertMakeBidsOutput(t *testing.T, filename string, bidderResponse *adapters.BidderResponse, expected []expectedBid) {
+func assertMakeBidsOutput(t *testing.T, filename string, bidderResponse *adapters.BidderResponse, expected expectedBidResponse) {
 	t.Helper()
 
-	if (bidderResponse == nil || len(bidderResponse.Bids) == 0) != (len(expected) == 0) {
-		if len(expected) == 0 {
+	if (bidderResponse == nil || len(bidderResponse.Bids) == 0) != (len(expected.Bids) == 0) {
+		if len(expected.Bids) == 0 {
 			t.Fatalf("%s: expectedBidResponses indicated a nil response, but mockResponses supplied a non-nil response", filename)
 		}
 
@@ -271,11 +272,19 @@ func assertMakeBidsOutput(t *testing.T, filename string, bidderResponse *adapter
 		bidderResponse = new(adapters.BidderResponse)
 	}
 
-	if len(bidderResponse.Bids) != len(expected) {
-		t.Fatalf("%s: MakeBids returned wrong bid count. Expected %d, got %d", filename, len(expected), len(bidderResponse.Bids))
+	if len(bidderResponse.Bids) != len(expected.Bids) {
+		t.Fatalf("%s: MakeBids returned wrong bid count. Expected %d, got %d", filename, len(expected.Bids), len(bidderResponse.Bids))
 	}
 	for i := 0; i < len(bidderResponse.Bids); i++ {
-		diffBids(t, fmt.Sprintf("%s:  typedBid[%d]", filename, i), bidderResponse.Bids[i], &(expected[i]))
+		diffBids(t, fmt.Sprintf("%s:  typedBid[%d]", filename, i), bidderResponse.Bids[i], &(expected.Bids[i]))
+	}
+	if expected.FledgeAuctionConfigs != nil {
+		assert.NotNilf(t, bidderResponse.FledgeAuctionConfigs, "%s: expected fledgeauctionconfigs in bidderResponse", filename)
+		fledgeAuctionConfigsJson, err := json.Marshal(bidderResponse.FledgeAuctionConfigs)
+		assert.NoErrorf(t, err, "%s: failed to marshal actual FledgeAuctionConfig response into JSON.", filename)
+		assert.JSONEqf(t, string(expected.FledgeAuctionConfigs), string(fledgeAuctionConfigsJson), "%s: incorrect fledgeauctionconfig", filename)
+	} else {
+		assert.Nilf(t, bidderResponse.FledgeAuctionConfigs, "%s: unexpected fledgeauctionconfigs in bidderResponse", filename)
 	}
 }
 
@@ -435,6 +444,6 @@ func testMakeBidsImpl(t *testing.T, filename string, spec *testSpec, bidder adap
 
 	// Assert MakeBids implementation BidResponses with expected JSON-defined spec.BidResponses[i].Bids
 	for i := 0; i < len(spec.BidResponses); i++ {
-		assertMakeBidsOutput(t, filename, bidResponses[i], spec.BidResponses[i].Bids)
+		assertMakeBidsOutput(t, filename, bidResponses[i], spec.BidResponses[i])
 	}
 }
diff --git a/adapters/cwire/cwire_test.go b/adapters/cwire/cwire_test.go
new file mode 100644
index 00000000..b51b4e1b
--- /dev/null
+++ b/adapters/cwire/cwire_test.go
@@ -0,0 +1,29 @@
+package cwire
+
+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.BidderCWire,
+		config.Adapter{
+			Endpoint: "https://cwi.re/prebid/adapter-endpoint",
+		},
+		config.Server{
+			ExternalUrl: "http://hosturl.com",
+			GvlID:       1,
+			DataCenter:  "2",
+		},
+	)
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "cwiretest", bidder)
+}
diff --git a/adapters/cwire/cwiretest/exemplary/banner-imp-ext.json b/adapters/cwire/cwiretest/exemplary/banner-imp-ext.json
new file mode 100644
index 00000000..af9860d6
--- /dev/null
+++ b/adapters/cwire/cwiretest/exemplary/banner-imp-ext.json
@@ -0,0 +1,137 @@
+{
+  "mockBidRequest": {
+    "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+    "cur": [
+      "CHF"
+    ],
+    "imp": [
+      {
+        "id": "102",
+        "banner": {
+          "h": 250,
+          "w": 300,
+          "pos": 0
+        },
+        "ext": {
+          "bidder": {
+            "cwcreative": "9999",
+            "placementId": 15
+          }
+        }
+      }
+    ],
+    "site": {
+      "page": "http://www.foobar.com/1234.html"
+    },
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
+    },
+    "user": {
+      "id": "55816b39711f9b5acf3b90e313ed29e51665623f",
+      "geo": {
+        "country": "ch",
+        "region": "basel-stadt",
+        "city": "basel"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://cwi.re/prebid/adapter-endpoint",
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "body": {
+          "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+          "cur": [
+            "CHF"
+          ],
+          "imp": [
+            {
+              "id": "102",
+              "banner": {
+                "h": 250,
+                "w": 300,
+                "pos": 0
+              },
+              "ext": {
+                "bidder": {
+                  "cwcreative": "9999",
+                  "placementId": 15
+                }
+              }
+            }
+          ],
+          "site": {
+            "page": "http://www.foobar.com/1234.html"
+          },
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
+          },
+          "user": {
+            "id": "55816b39711f9b5acf3b90e313ed29e51665623f",
+            "geo": {
+              "country": "ch",
+              "region": "basel-stadt",
+              "city": "basel"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "headers": {},
+        "body": {
+          "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+          "cur": "CHF",
+          "seatbid": [
+            {
+              "seat": "cwire",
+              "group": 0,
+              "bid": [
+                {
+                  "id": "333",
+                  "impid": "102",
+                  "price": 8.00,
+                  "crid": "9999",
+                  "adm": "<h3>Example Ad</h3>",
+                  "w": 1,
+                  "h": 1,
+                  "nurl": "https://embed.cwi.re/delivery/prebid-server/win/333",
+                  "mtype": 1
+                }
+              ]
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "CHF",
+      "bids": [
+        {
+          "bid": {
+            "id": "333",
+            "impid": "102",
+            "price": 8.00,
+            "crid": "9999",
+            "adm": "<h3>Example Ad</h3>",
+            "w": 1,
+            "h": 1,
+            "nurl": "https://embed.cwi.re/delivery/prebid-server/win/333",
+            "mtype": 1
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/cwire/cwiretest/exemplary/banner.json b/adapters/cwire/cwiretest/exemplary/banner.json
new file mode 100644
index 00000000..ca67cea8
--- /dev/null
+++ b/adapters/cwire/cwiretest/exemplary/banner.json
@@ -0,0 +1,125 @@
+{
+  "mockBidRequest": {
+    "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+    "cur": [
+      "CHF"
+    ],
+    "imp": [
+      {
+        "id": "102",
+        "banner": {
+          "h": 250,
+          "w": 300,
+          "pos": 0
+        }
+      }
+    ],
+    "site": {
+      "page": "http://www.foobar.com/1234.html"
+    },
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
+    },
+    "user": {
+      "id": "55816b39711f9b5acf3b90e313ed29e51665623f",
+      "geo": {
+        "country": "ch",
+        "region": "basel-stadt",
+        "city": "basel"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://cwi.re/prebid/adapter-endpoint",
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "body": {
+          "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+          "cur": [
+            "CHF"
+          ],
+          "imp": [
+            {
+              "id": "102",
+              "banner": {
+                "h": 250,
+                "w": 300,
+                "pos": 0
+              }
+            }
+          ],
+          "site": {
+            "page": "http://www.foobar.com/1234.html"
+          },
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
+          },
+          "user": {
+            "id": "55816b39711f9b5acf3b90e313ed29e51665623f",
+            "geo": {
+              "country": "ch",
+              "region": "basel-stadt",
+              "city": "basel"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "headers": {},
+        "body": {
+          "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+          "cur": "CHF",
+          "seatbid": [
+            {
+              "seat": "cwire",
+              "group": 0,
+              "bid": [
+                {
+                  "id": "333",
+                  "impid": "102",
+                  "price": 8.00,
+                  "crid": "4321",
+                  "adm": "<h3>Example Ad</h3>",
+                  "w": 1,
+                  "h": 1,
+                  "nurl": "https://embed.cwi.re/delivery/prebid-server/win/333",
+                  "mtype": 1
+                }
+              ]
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "CHF",
+      "bids": [
+        {
+          "bid": {
+            "id": "333",
+            "impid": "102",
+            "price": 8.00,
+            "crid": "4321",
+            "adm": "<h3>Example Ad</h3>",
+            "w": 1,
+            "h": 1,
+            "nurl": "https://embed.cwi.re/delivery/prebid-server/win/333",
+            "mtype": 1
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/cwire/cwiretest/supplemental/204.json b/adapters/cwire/cwiretest/supplemental/204.json
new file mode 100644
index 00000000..c57b7134
--- /dev/null
+++ b/adapters/cwire/cwiretest/supplemental/204.json
@@ -0,0 +1,81 @@
+{
+  "mockBidRequest": {
+    "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+    "cur": [
+      "CHF"
+    ],
+    "imp": [
+      {
+        "id": "102",
+        "banner": {
+          "h": 250,
+          "w": 300,
+          "pos": 0
+        }
+      }
+    ],
+    "site": {
+      "page": "http://www.foobar.com/1234.html"
+    },
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
+    },
+    "user": {
+      "id": "55816b39711f9b5acf3b90e313ed29e51665623f",
+      "geo": {
+        "country": "ch",
+        "region": "basel-stadt",
+        "city": "basel"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://cwi.re/prebid/adapter-endpoint",
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "body": {
+          "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+          "cur": [
+            "CHF"
+          ],
+          "imp": [
+            {
+              "id": "102",
+              "banner": {
+                "h": 250,
+                "w": 300,
+                "pos": 0
+              }
+            }
+          ],
+          "site": {
+            "page": "http://www.foobar.com/1234.html"
+          },
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
+          },
+          "user": {
+            "id": "55816b39711f9b5acf3b90e313ed29e51665623f",
+            "geo": {
+              "country": "ch",
+              "region": "basel-stadt",
+              "city": "basel"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 204
+      }
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/cwire/cwiretest/supplemental/500.json b/adapters/cwire/cwiretest/supplemental/500.json
new file mode 100644
index 00000000..f0128f38
--- /dev/null
+++ b/adapters/cwire/cwiretest/supplemental/500.json
@@ -0,0 +1,86 @@
+{
+  "mockBidRequest": {
+    "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+    "cur": [
+      "CHF"
+    ],
+    "imp": [
+      {
+        "id": "102",
+        "banner": {
+          "h": 250,
+          "w": 300,
+          "pos": 0
+        }
+      }
+    ],
+    "site": {
+      "page": "http://www.foobar.com/1234.html"
+    },
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
+    },
+    "user": {
+      "id": "55816b39711f9b5acf3b90e313ed29e51665623f",
+      "geo": {
+        "country": "ch",
+        "region": "basel-stadt",
+        "city": "basel"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://cwi.re/prebid/adapter-endpoint",
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "body": {
+          "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+          "cur": [
+            "CHF"
+          ],
+          "imp": [
+            {
+              "id": "102",
+              "banner": {
+                "h": 250,
+                "w": 300,
+                "pos": 0
+              }
+            }
+          ],
+          "site": {
+            "page": "http://www.foobar.com/1234.html"
+          },
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
+          },
+          "user": {
+            "id": "55816b39711f9b5acf3b90e313ed29e51665623f",
+            "geo": {
+              "country": "ch",
+              "region": "basel-stadt",
+              "city": "basel"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 500
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 500. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/cwire/cwiretest/supplemental/badresponse.json b/adapters/cwire/cwiretest/supplemental/badresponse.json
new file mode 100644
index 00000000..eb81ea33
--- /dev/null
+++ b/adapters/cwire/cwiretest/supplemental/badresponse.json
@@ -0,0 +1,89 @@
+{
+  "mockBidRequest": {
+    "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+    "cur": [
+      "CHF"
+    ],
+    "imp": [
+      {
+        "id": "102",
+        "banner": {
+          "h": 250,
+          "w": 300,
+          "pos": 0
+        }
+      }
+    ],
+    "site": {
+      "page": "http://www.foobar.com/1234.html"
+    },
+    "device": {
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
+    },
+    "user": {
+      "id": "55816b39711f9b5acf3b90e313ed29e51665623f",
+      "geo": {
+        "country": "ch",
+        "region": "basel-stadt",
+        "city": "basel"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://cwi.re/prebid/adapter-endpoint",
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ]
+        },
+        "body": {
+          "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
+          "cur": [
+            "CHF"
+          ],
+          "imp": [
+            {
+              "id": "102",
+              "banner": {
+                "h": 250,
+                "w": 300,
+                "pos": 0
+              }
+            }
+          ],
+          "site": {
+            "page": "http://www.foobar.com/1234.html"
+          },
+          "device": {
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0"
+          },
+          "user": {
+            "id": "55816b39711f9b5acf3b90e313ed29e51665623f",
+            "geo": {
+              "country": "ch",
+              "region": "basel-stadt",
+              "city": "basel"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": 3
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Error while decoding response, err: .*",
+      "comparison": "regex"
+    }
+  ]
+}
diff --git a/adapters/cwire/params_test.go b/adapters/cwire/params_test.go
new file mode 100644
index 00000000..99689977
--- /dev/null
+++ b/adapters/cwire/params_test.go
@@ -0,0 +1,73 @@
+package cwire
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/cwire.json
+// These also validate the format of the external API: request.imp[i].ext.bidder
+// TestValidParams makes sure that the cwire schema accepts all imp.ext fields which we intend to support.
+
+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.BidderCWire, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected cwire params: %s", validParam)
+		}
+	}
+}
+
+// TestInvalidParams makes sure that the cwire 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.BidderCWire, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"cwcreative":"3746","cwdebug":true,"cwfeatures":["feat1","feat2"]}`,
+	`{"cwcreative":"3746","cwdebug":true}`,
+	`{"cwcreative":"3746"}`,
+	`{"cwdebug":true,"cwfeatures":["feat1","feat2"]}`,
+	`{"cwdebug":true}`,
+	`{"cwfeatures":["feat1","feat2"]}`,
+	`{"cwfeatures":["feat1"]}`,
+	`{"cwfeatures":[]}`,
+	`{"pageId":321,"cwcreative":"3746"}`,
+	`{"pageId":321}`,
+	`{"placementId":123,"cwcreative":"3746"}`,
+	`{"placementId":123,"cwdebug":true,"cwfeatures":["feat1","feat2"]}`,
+	`{"placementId":123,"pageId":321,"cwcreative":"3746","cwdebug":true,"cwfeatures":["feat1","feat2"]}`,
+	`{"placementId":123,"pageId":321,"cwcreative":"3746"}`,
+	`{"placementId":123,"pageId":321}`,
+	`{"placementId":123}`,
+	`{}`,
+}
+
+var invalidParams = []string{
+	`4.2`,
+	`5`,
+	`[]`,
+	``,
+	`null`,
+	`true`,
+	`{"cwcreative":1234}`,
+	`{"placementId":"abc"}`,
+	`{"cwdebug":"TRUE"}`,
+	`{"cwdebug":"FALSE"}`,
+	`{"cwfeatures":[1,2,3]}`,
+}
diff --git a/adapters/definemedia/definemedia_test.go b/adapters/definemedia/definemedia_test.go
new file mode 100644
index 00000000..3ed0cb93
--- /dev/null
+++ b/adapters/definemedia/definemedia_test.go
@@ -0,0 +1,21 @@
+package definemedia
+
+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.BidderDefinemedia, config.Adapter{
+		Endpoint: "https://rtb.conative.network/openrtb2/auction"},
+		config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "definemediatest", bidder)
+}
diff --git a/adapters/definemedia/definemediatest/exemplary/sample-conative-banner.json b/adapters/definemedia/definemediatest/exemplary/sample-conative-banner.json
new file mode 100644
index 00000000..a7d7e411
--- /dev/null
+++ b/adapters/definemedia/definemediatest/exemplary/sample-conative-banner.json
@@ -0,0 +1,270 @@
+{
+  "mockBidRequest": {
+    "imp": [
+      {
+        "ext": {
+          "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9",
+          "prebid": {
+            "bidder": {
+              "definemedia": {
+                "mandantId": 12
+              }
+            }
+          }
+        },
+        "id": "div-gpt-ad-1460505748561-0",
+        "banner": {
+          "topframe": 1,
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        }
+      }
+    ],
+    "site": {
+      "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+      "domain": "localhost:8080",
+      "publisher": {
+        "domain": "localhost:8080",
+        "id": "1"
+      }
+    },
+    "device": {
+      "w": 1098,
+      "h": 1169,
+      "dnt": 0,
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+      "language": "de",
+      "sua": {
+        "source": 2,
+        "platform": {
+          "brand": "macOS",
+          "version": [
+            "12",
+            "3",
+            "1"
+          ]
+        },
+        "browsers": [
+          {
+            "brand": "Not?A_Brand",
+            "version": [
+              "8",
+              "0",
+              "0",
+              "0"
+            ]
+          },
+          {
+            "brand": "Chromium",
+            "version": [
+              "108",
+              "0",
+              "5359",
+              "124"
+            ]
+          },
+          {
+            "brand": "Google Chrome",
+            "version": [
+              "108",
+              "0",
+              "5359",
+              "124"
+            ]
+          }
+        ],
+        "mobile": 0,
+        "model": "",
+        "bitness": "64",
+        "architecture": "x86"
+      }
+    },
+    "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+    "test": 0,
+    "source": {
+      "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+    },
+    "ext": {
+      "prebid": {
+        "auctiontimestamp": 1671449004622,
+        "targeting": {
+          "includewinners": true,
+          "includebidderkeys": false
+        },
+        "debug": true,
+        "channel": {
+          "name": "pbjs",
+          "version": "v7.28.0"
+        }
+      }
+    },
+    "tmax": 1000
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://rtb.conative.network/openrtb2/auction",
+        "body": {
+          "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+          "imp": [
+            {
+              "id": "div-gpt-ad-1460505748561-0",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  }
+                ],
+                "topframe": 1
+              },
+              "ext": {
+                "prebid": {
+                  "bidder": {
+                    "definemedia": {
+                      "mandantId": 12
+                    }
+                  }
+                },
+                "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9"
+              }
+            }
+          ],
+          "site": {
+            "domain": "localhost:8080",
+            "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+            "publisher": {
+              "id": "1",
+              "domain": "localhost:8080"
+            }
+          },
+          "device": {
+            "dnt": 0,
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+            "sua": {
+              "browsers": [
+                {
+                  "brand": "Not?A_Brand",
+                  "version": [
+                    "8",
+                    "0",
+                    "0",
+                    "0"
+                  ]
+                },
+                {
+                  "brand": "Chromium",
+                  "version": [
+                    "108",
+                    "0",
+                    "5359",
+                    "124"
+                  ]
+                },
+                {
+                  "brand": "Google Chrome",
+                  "version": [
+                    "108",
+                    "0",
+                    "5359",
+                    "124"
+                  ]
+                }
+              ],
+              "platform": {
+                "brand": "macOS",
+                "version": [
+                  "12",
+                  "3",
+                  "1"
+                ]
+              },
+              "mobile": 0,
+              "architecture": "x86",
+              "bitness": "64",
+              "source": 2
+            },
+            "h": 1169,
+            "w": 1098,
+            "language": "de"
+          },
+          "tmax": 1000,
+          "source": {
+            "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+          },
+          "ext": {
+            "prebid": {
+              "auctiontimestamp": 1.671449004622e+12,
+              "channel": {
+                "name": "pbjs",
+                "version": "v7.28.0"
+              },
+              "debug": true,
+              "targeting": {
+                "includebidderkeys": false,
+                "includewinners": true
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "46189656-7e2e-477d-b7f2-e05de224bb89",
+                  "impid": "div-gpt-ad-1460505748561-0",
+                  "price": 100,
+                  "adm": "{banner html}",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "crid": "test-creative-id",
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "46189656-7e2e-477d-b7f2-e05de224bb89",
+            "impid": "div-gpt-ad-1460505748561-0",
+            "price": 100,
+            "adm": "{banner html}",
+            "crid": "test-creative-id",
+            "adomain": [
+              "test.com"
+            ],
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/definemedia/definemediatest/exemplary/sample-conative-native.json b/adapters/definemedia/definemediatest/exemplary/sample-conative-native.json
new file mode 100644
index 00000000..4fe56a4c
--- /dev/null
+++ b/adapters/definemedia/definemediatest/exemplary/sample-conative-native.json
@@ -0,0 +1,257 @@
+{
+  "mockBidRequest": {
+    "imp": [
+      {
+        "ext": {
+          "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9",
+          "prebid": {
+            "bidder": {
+              "definemedia": {
+                "mandantId": 12
+              }
+            }
+          }
+        },
+        "id": "div-gpt-ad-1460505748561-0",
+        "native": {}
+      }
+    ],
+    "site": {
+      "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+      "domain": "localhost:8080",
+      "publisher": {
+        "domain": "localhost:8080",
+        "id": "1"
+      }
+    },
+    "device": {
+      "w": 1098,
+      "h": 1169,
+      "dnt": 0,
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+      "language": "de",
+      "sua": {
+        "source": 2,
+        "platform": {
+          "brand": "macOS",
+          "version": [
+            "12",
+            "3",
+            "1"
+          ]
+        },
+        "browsers": [
+          {
+            "brand": "Not?A_Brand",
+            "version": [
+              "8",
+              "0",
+              "0",
+              "0"
+            ]
+          },
+          {
+            "brand": "Chromium",
+            "version": [
+              "108",
+              "0",
+              "5359",
+              "124"
+            ]
+          },
+          {
+            "brand": "Google Chrome",
+            "version": [
+              "108",
+              "0",
+              "5359",
+              "124"
+            ]
+          }
+        ],
+        "mobile": 0,
+        "model": "",
+        "bitness": "64",
+        "architecture": "x86"
+      }
+    },
+    "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+    "test": 0,
+    "source": {
+      "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+    },
+    "ext": {
+      "prebid": {
+        "auctiontimestamp": 1671449004622,
+        "targeting": {
+          "includewinners": true,
+          "includebidderkeys": false
+        },
+        "debug": true,
+        "channel": {
+          "name": "pbjs",
+          "version": "v7.28.0"
+        }
+      }
+    },
+    "tmax": 1000
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://rtb.conative.network/openrtb2/auction",
+        "body": {
+          "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+          "imp": [
+            {
+              "id": "div-gpt-ad-1460505748561-0",
+              "native": {
+                "request": ""
+              },
+              "ext": {
+                "prebid": {
+                  "bidder": {
+                    "definemedia": {
+                      "mandantId": 12
+                    }
+                  }
+                },
+                "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9"
+              }
+            }
+          ],
+          "site": {
+            "domain": "localhost:8080",
+            "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+            "publisher": {
+              "id": "1",
+              "domain": "localhost:8080"
+            }
+          },
+          "device": {
+            "dnt": 0,
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+            "sua": {
+              "browsers": [
+                {
+                  "brand": "Not?A_Brand",
+                  "version": [
+                    "8",
+                    "0",
+                    "0",
+                    "0"
+                  ]
+                },
+                {
+                  "brand": "Chromium",
+                  "version": [
+                    "108",
+                    "0",
+                    "5359",
+                    "124"
+                  ]
+                },
+                {
+                  "brand": "Google Chrome",
+                  "version": [
+                    "108",
+                    "0",
+                    "5359",
+                    "124"
+                  ]
+                }
+              ],
+              "platform": {
+                "brand": "macOS",
+                "version": [
+                  "12",
+                  "3",
+                  "1"
+                ]
+              },
+              "mobile": 0,
+              "architecture": "x86",
+              "bitness": "64",
+              "source": 2
+            },
+            "h": 1169,
+            "w": 1098,
+            "language": "de"
+          },
+          "tmax": 1000,
+          "source": {
+            "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+          },
+          "ext": {
+            "prebid": {
+              "auctiontimestamp": 1.671449004622e+12,
+              "channel": {
+                "name": "pbjs",
+                "version": "v7.28.0"
+              },
+              "debug": true,
+              "targeting": {
+                "includebidderkeys": false,
+                "includewinners": true
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "46189656-7e2e-477d-b7f2-e05de224bb89",
+                  "impid": "div-gpt-ad-1460505748561-0",
+                  "price": 100,
+                  "adm": "{\n \"ver\": \"1.1\",\n \"imptrackers\": [\"http://imptracker.com\"],\n \"jstracker\": \"\u003cscript\u003etrack()\u003c/script\u003e\",\n \"link\": {\n  \"url\": \"http://i.am.a/URL\"\n },\n \"assets\": [\n  {\n   \"id\": 123,\n   \"required\": 1,\n   \"title\": {\n    \"text\": \"Learn about this awesome thing\"\n   }\n  },\n  {\n   \"id\": 124,\n   \"required\": 1,\n   \"img\": {\n    \"url\": \"http://www.myads.com/thumbnail1.png\"\n   }\n  },\n  {\n   \"id\": 128,\n   \"required\": 1,\n   \"img\": {\n    \"url\": \"http://www.myads.com/largethumb1.png\"\n   }\n  },\n  {\n   \"id\": 126,\n   \"required\": 1,\n   \"data\": {\n    \"value\": \"My Brand\"\n   }\n  },\n  {\n   \"id\": 127,\n   \"required\": 1,\n   \"data\": {\n    \"value\": \"Learn all about this awesome story of someone using my product.\"\n   }\n  },\n  {\n   \"id\": 4,\n   \"video\": {\n    \"vasttag\": \"\u003cVAST version=\\\"2.0\\\"\u003e\u003c/VAST\u003e\"\n   }\n  },\n  {\n   \"id\": 5,\n   \"link\": {\n    \"url\": \"http://landing.com\",\n    \"clicktrackers\": [\"http://tracker.com\"],\n    \"fallback\": \"http://fallback.com\"\n   }\n  }\n ]\n}",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "crid": "test-creative-id",
+                  "ext": {
+                    "prebid": {
+                      "type": "native"
+                    }
+                  }
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "46189656-7e2e-477d-b7f2-e05de224bb89",
+            "impid": "div-gpt-ad-1460505748561-0",
+            "price": 100,
+
+            "adm": "{\n \"ver\": \"1.1\",\n \"imptrackers\": [\"http://imptracker.com\"],\n \"jstracker\": \"\u003cscript\u003etrack()\u003c/script\u003e\",\n \"link\": {\n  \"url\": \"http://i.am.a/URL\"\n },\n \"assets\": [\n  {\n   \"id\": 123,\n   \"required\": 1,\n   \"title\": {\n    \"text\": \"Learn about this awesome thing\"\n   }\n  },\n  {\n   \"id\": 124,\n   \"required\": 1,\n   \"img\": {\n    \"url\": \"http://www.myads.com/thumbnail1.png\"\n   }\n  },\n  {\n   \"id\": 128,\n   \"required\": 1,\n   \"img\": {\n    \"url\": \"http://www.myads.com/largethumb1.png\"\n   }\n  },\n  {\n   \"id\": 126,\n   \"required\": 1,\n   \"data\": {\n    \"value\": \"My Brand\"\n   }\n  },\n  {\n   \"id\": 127,\n   \"required\": 1,\n   \"data\": {\n    \"value\": \"Learn all about this awesome story of someone using my product.\"\n   }\n  },\n  {\n   \"id\": 4,\n   \"video\": {\n    \"vasttag\": \"\u003cVAST version=\\\"2.0\\\"\u003e\u003c/VAST\u003e\"\n   }\n  },\n  {\n   \"id\": 5,\n   \"link\": {\n    \"url\": \"http://landing.com\",\n    \"clicktrackers\": [\"http://tracker.com\"],\n    \"fallback\": \"http://fallback.com\"\n   }\n  }\n ]\n}",
+            "crid": "test-creative-id",
+            "adomain": [
+              "test.com"
+            ],
+            "ext": {
+              "prebid": {
+                "type": "native"
+              }
+            }
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/definemedia/definemediatest/supplemental/nobid-response.json b/adapters/definemedia/definemediatest/supplemental/nobid-response.json
new file mode 100644
index 00000000..2140ee9b
--- /dev/null
+++ b/adapters/definemedia/definemediatest/supplemental/nobid-response.json
@@ -0,0 +1,222 @@
+{
+    "mockBidRequest": {
+      "imp": [
+        {
+          "ext": {
+            "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9",
+            "prebid": {
+              "bidder": {
+                "definemedia": {}
+              }
+            }
+          },
+          "id": "div-gpt-ad-1460505748561-0",
+          "banner": {
+            "topframe": 1,
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          }
+        }
+      ],
+      "site": {
+        "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+        "domain": "localhost:8080",
+        "publisher": {
+          "domain": "localhost:8080",
+          "id": "1"
+        }
+      },
+      "device": {
+        "w": 1098,
+        "h": 1169,
+        "dnt": 0,
+        "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+        "language": "de",
+        "sua": {
+          "source": 2,
+          "platform": {
+            "brand": "macOS",
+            "version": [
+              "12",
+              "3",
+              "1"
+            ]
+          },
+          "browsers": [
+            {
+              "brand": "Not?A_Brand",
+              "version": [
+                "8",
+                "0",
+                "0",
+                "0"
+              ]
+            },
+            {
+              "brand": "Chromium",
+              "version": [
+                "108",
+                "0",
+                "5359",
+                "124"
+              ]
+            },
+            {
+              "brand": "Google Chrome",
+              "version": [
+                "108",
+                "0",
+                "5359",
+                "124"
+              ]
+            }
+          ],
+          "mobile": 0,
+          "model": "",
+          "bitness": "64",
+          "architecture": "x86"
+        }
+      },
+      "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+      "test": 0,
+      "source": {
+        "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+      },
+      "ext": {
+        "prebid": {
+          "auctiontimestamp": 1671449004622,
+          "targeting": {
+            "includewinners": true,
+            "includebidderkeys": false
+          },
+          "debug": true,
+          "channel": {
+            "name": "pbjs",
+            "version": "v7.28.0"
+          }
+        }
+      },
+      "tmax": 1000
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://rtb.conative.network/openrtb2/auction",
+          "body": {
+            "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+            "imp": [
+              {
+                "id": "div-gpt-ad-1460505748561-0",
+                "banner": {
+                  "format": [
+                    {
+                      "w": 300,
+                      "h": 250
+                    }
+                  ],
+                  "topframe": 1
+                },
+                "ext": {
+                  "prebid": {
+                    "bidder": {
+                      "definemedia": {}
+                    }
+                  },
+                  "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9"
+                }
+              }
+            ],
+            "site": {
+              "domain": "localhost:8080",
+              "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+              "publisher": {
+                "id": "1",
+                "domain": "localhost:8080"
+              }
+            },
+            "device": {
+              "dnt": 0,
+              "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+              "sua": {
+                "browsers": [
+                  {
+                    "brand": "Not?A_Brand",
+                    "version": [
+                      "8",
+                      "0",
+                      "0",
+                      "0"
+                    ]
+                  },
+                  {
+                    "brand": "Chromium",
+                    "version": [
+                      "108",
+                      "0",
+                      "5359",
+                      "124"
+                    ]
+                  },
+                  {
+                    "brand": "Google Chrome",
+                    "version": [
+                      "108",
+                      "0",
+                      "5359",
+                      "124"
+                    ]
+                  }
+                ],
+                "platform": {
+                  "brand": "macOS",
+                  "version": [
+                    "12",
+                    "3",
+                    "1"
+                  ]
+                },
+                "mobile": 0,
+                "architecture": "x86",
+                "bitness": "64",
+                "source": 2
+              },
+              "h": 1169,
+              "w": 1098,
+              "language": "de"
+            },
+            "tmax": 1000,
+            "source": {
+              "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+            },
+            "ext": {
+              "prebid": {
+                "auctiontimestamp": 1.671449004622e+12,
+                "channel": {
+                  "name": "pbjs",
+                  "version": "v7.28.0"
+                },
+                "debug": true,
+                "targeting": {
+                  "includebidderkeys": false,
+                  "includewinners": true
+                }
+              }
+            }
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "id": "test-request-id",
+            "seatbid": null,
+            "cur": null
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": []
+  }
\ No newline at end of file
diff --git a/adapters/definemedia/definemediatest/supplemental/nocontent-response.json b/adapters/definemedia/definemediatest/supplemental/nocontent-response.json
new file mode 100644
index 00000000..147d2185
--- /dev/null
+++ b/adapters/definemedia/definemediatest/supplemental/nocontent-response.json
@@ -0,0 +1,219 @@
+{
+    "mockBidRequest": {
+      "imp": [
+        {
+          "ext": {
+            "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9",
+            "prebid": {
+              "bidder": {
+                "definemedia": {}
+              }
+            }
+          },
+          "id": "div-gpt-ad-1460505748561-0",
+          "banner": {
+            "topframe": 1,
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          }
+        }
+      ],
+      "site": {
+        "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+        "domain": "localhost:8080",
+        "publisher": {
+          "domain": "localhost:8080",
+          "id": "1"
+        }
+      },
+      "device": {
+        "w": 1098,
+        "h": 1169,
+        "dnt": 0,
+        "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+        "language": "de",
+        "sua": {
+          "source": 2,
+          "platform": {
+            "brand": "macOS",
+            "version": [
+              "12",
+              "3",
+              "1"
+            ]
+          },
+          "browsers": [
+            {
+              "brand": "Not?A_Brand",
+              "version": [
+                "8",
+                "0",
+                "0",
+                "0"
+              ]
+            },
+            {
+              "brand": "Chromium",
+              "version": [
+                "108",
+                "0",
+                "5359",
+                "124"
+              ]
+            },
+            {
+              "brand": "Google Chrome",
+              "version": [
+                "108",
+                "0",
+                "5359",
+                "124"
+              ]
+            }
+          ],
+          "mobile": 0,
+          "model": "",
+          "bitness": "64",
+          "architecture": "x86"
+        }
+      },
+      "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+      "test": 0,
+      "source": {
+        "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+      },
+      "ext": {
+        "prebid": {
+          "auctiontimestamp": 1671449004622,
+          "targeting": {
+            "includewinners": true,
+            "includebidderkeys": false
+          },
+          "debug": true,
+          "channel": {
+            "name": "pbjs",
+            "version": "v7.28.0"
+          }
+        }
+      },
+      "tmax": 1000
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://rtb.conative.network/openrtb2/auction",
+          "body": {
+            "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+            "imp": [
+              {
+                "id": "div-gpt-ad-1460505748561-0",
+                "banner": {
+                  "format": [
+                    {
+                      "w": 300,
+                      "h": 250
+                    }
+                  ],
+                  "topframe": 1
+                },
+                "ext": {
+                  "prebid": {
+                    "bidder": {
+                      "definemedia": {}
+                    }
+                  },
+                  "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9"
+                }
+              }
+            ],
+            "site": {
+              "domain": "localhost:8080",
+              "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+              "publisher": {
+                "id": "1",
+                "domain": "localhost:8080"
+              }
+            },
+            "device": {
+              "dnt": 0,
+              "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+              "sua": {
+                "browsers": [
+                  {
+                    "brand": "Not?A_Brand",
+                    "version": [
+                      "8",
+                      "0",
+                      "0",
+                      "0"
+                    ]
+                  },
+                  {
+                    "brand": "Chromium",
+                    "version": [
+                      "108",
+                      "0",
+                      "5359",
+                      "124"
+                    ]
+                  },
+                  {
+                    "brand": "Google Chrome",
+                    "version": [
+                      "108",
+                      "0",
+                      "5359",
+                      "124"
+                    ]
+                  }
+                ],
+                "platform": {
+                  "brand": "macOS",
+                  "version": [
+                    "12",
+                    "3",
+                    "1"
+                  ]
+                },
+                "mobile": 0,
+                "architecture": "x86",
+                "bitness": "64",
+                "source": 2
+              },
+              "h": 1169,
+              "w": 1098,
+              "language": "de"
+            },
+            "tmax": 1000,
+            "source": {
+              "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+            },
+            "ext": {
+              "prebid": {
+                "auctiontimestamp": 1.671449004622e+12,
+                "channel": {
+                  "name": "pbjs",
+                  "version": "v7.28.0"
+                },
+                "debug": true,
+                "targeting": {
+                  "includebidderkeys": false,
+                  "includewinners": true
+                }
+              }
+            }
+          }
+        },
+        "mockResponse": {
+          "status": 204,
+          "body": {}
+        }
+      }
+    ],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": []
+  }
\ No newline at end of file
diff --git a/adapters/definemedia/definemediatest/supplemental/status_400.json b/adapters/definemedia/definemediatest/supplemental/status_400.json
new file mode 100644
index 00000000..ec772bf8
--- /dev/null
+++ b/adapters/definemedia/definemediatest/supplemental/status_400.json
@@ -0,0 +1,224 @@
+{
+    "mockBidRequest": {
+      "imp": [
+        {
+          "ext": {
+            "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9",
+            "prebid": {
+              "bidder": {
+                "definemedia": {}
+              }
+            }
+          },
+          "id": "div-gpt-ad-1460505748561-0",
+          "banner": {
+            "topframe": 1,
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          }
+        }
+      ],
+      "site": {
+        "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+        "domain": "localhost:8080",
+        "publisher": {
+          "domain": "localhost:8080",
+          "id": "1"
+        }
+      },
+      "device": {
+        "w": 1098,
+        "h": 1169,
+        "dnt": 0,
+        "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+        "language": "de",
+        "sua": {
+          "source": 2,
+          "platform": {
+            "brand": "macOS",
+            "version": [
+              "12",
+              "3",
+              "1"
+            ]
+          },
+          "browsers": [
+            {
+              "brand": "Not?A_Brand",
+              "version": [
+                "8",
+                "0",
+                "0",
+                "0"
+              ]
+            },
+            {
+              "brand": "Chromium",
+              "version": [
+                "108",
+                "0",
+                "5359",
+                "124"
+              ]
+            },
+            {
+              "brand": "Google Chrome",
+              "version": [
+                "108",
+                "0",
+                "5359",
+                "124"
+              ]
+            }
+          ],
+          "mobile": 0,
+          "model": "",
+          "bitness": "64",
+          "architecture": "x86"
+        }
+      },
+      "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+      "test": 0,
+      "source": {
+        "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+      },
+      "ext": {
+        "prebid": {
+          "auctiontimestamp": 1671449004622,
+          "targeting": {
+            "includewinners": true,
+            "includebidderkeys": false
+          },
+          "debug": true,
+          "channel": {
+            "name": "pbjs",
+            "version": "v7.28.0"
+          }
+        }
+      },
+      "tmax": 1000
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://rtb.conative.network/openrtb2/auction",
+          "body": {
+            "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+            "imp": [
+              {
+                "id": "div-gpt-ad-1460505748561-0",
+                "banner": {
+                  "format": [
+                    {
+                      "w": 300,
+                      "h": 250
+                    }
+                  ],
+                  "topframe": 1
+                },
+                "ext": {
+                  "prebid": {
+                    "bidder": {
+                      "definemedia": {}
+                    }
+                  },
+                  "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9"
+                }
+              }
+            ],
+            "site": {
+              "domain": "localhost:8080",
+              "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+              "publisher": {
+                "id": "1",
+                "domain": "localhost:8080"
+              }
+            },
+            "device": {
+              "dnt": 0,
+              "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+              "sua": {
+                "browsers": [
+                  {
+                    "brand": "Not?A_Brand",
+                    "version": [
+                      "8",
+                      "0",
+                      "0",
+                      "0"
+                    ]
+                  },
+                  {
+                    "brand": "Chromium",
+                    "version": [
+                      "108",
+                      "0",
+                      "5359",
+                      "124"
+                    ]
+                  },
+                  {
+                    "brand": "Google Chrome",
+                    "version": [
+                      "108",
+                      "0",
+                      "5359",
+                      "124"
+                    ]
+                  }
+                ],
+                "platform": {
+                  "brand": "macOS",
+                  "version": [
+                    "12",
+                    "3",
+                    "1"
+                  ]
+                },
+                "mobile": 0,
+                "architecture": "x86",
+                "bitness": "64",
+                "source": 2
+              },
+              "h": 1169,
+              "w": 1098,
+              "language": "de"
+            },
+            "tmax": 1000,
+            "source": {
+              "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+            },
+            "ext": {
+              "prebid": {
+                "auctiontimestamp": 1.671449004622e+12,
+                "channel": {
+                  "name": "pbjs",
+                  "version": "v7.28.0"
+                },
+                "debug": true,
+                "targeting": {
+                  "includebidderkeys": false,
+                  "includewinners": true
+                }
+              }
+            }
+          }
+        },
+        "mockResponse": {
+          "status": 400,
+          "body": {}
+        }
+      }
+    ],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [
+        {
+          "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+          "comparison": "literal"
+        }
+      ]
+  }
\ No newline at end of file
diff --git a/adapters/definemedia/definemediatest/supplemental/status_418.json b/adapters/definemedia/definemediatest/supplemental/status_418.json
new file mode 100644
index 00000000..6e82f904
--- /dev/null
+++ b/adapters/definemedia/definemediatest/supplemental/status_418.json
@@ -0,0 +1,224 @@
+{
+    "mockBidRequest": {
+      "imp": [
+        {
+          "ext": {
+            "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9",
+            "prebid": {
+              "bidder": {
+                "definemedia": {}
+              }
+            }
+          },
+          "id": "div-gpt-ad-1460505748561-0",
+          "banner": {
+            "topframe": 1,
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          }
+        }
+      ],
+      "site": {
+        "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+        "domain": "localhost:8080",
+        "publisher": {
+          "domain": "localhost:8080",
+          "id": "1"
+        }
+      },
+      "device": {
+        "w": 1098,
+        "h": 1169,
+        "dnt": 0,
+        "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+        "language": "de",
+        "sua": {
+          "source": 2,
+          "platform": {
+            "brand": "macOS",
+            "version": [
+              "12",
+              "3",
+              "1"
+            ]
+          },
+          "browsers": [
+            {
+              "brand": "Not?A_Brand",
+              "version": [
+                "8",
+                "0",
+                "0",
+                "0"
+              ]
+            },
+            {
+              "brand": "Chromium",
+              "version": [
+                "108",
+                "0",
+                "5359",
+                "124"
+              ]
+            },
+            {
+              "brand": "Google Chrome",
+              "version": [
+                "108",
+                "0",
+                "5359",
+                "124"
+              ]
+            }
+          ],
+          "mobile": 0,
+          "model": "",
+          "bitness": "64",
+          "architecture": "x86"
+        }
+      },
+      "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+      "test": 0,
+      "source": {
+        "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+      },
+      "ext": {
+        "prebid": {
+          "auctiontimestamp": 1671449004622,
+          "targeting": {
+            "includewinners": true,
+            "includebidderkeys": false
+          },
+          "debug": true,
+          "channel": {
+            "name": "pbjs",
+            "version": "v7.28.0"
+          }
+        }
+      },
+      "tmax": 1000
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://rtb.conative.network/openrtb2/auction",
+          "body": {
+            "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+            "imp": [
+              {
+                "id": "div-gpt-ad-1460505748561-0",
+                "banner": {
+                  "format": [
+                    {
+                      "w": 300,
+                      "h": 250
+                    }
+                  ],
+                  "topframe": 1
+                },
+                "ext": {
+                  "prebid": {
+                    "bidder": {
+                      "definemedia": {}
+                    }
+                  },
+                  "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9"
+                }
+              }
+            ],
+            "site": {
+              "domain": "localhost:8080",
+              "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+              "publisher": {
+                "id": "1",
+                "domain": "localhost:8080"
+              }
+            },
+            "device": {
+              "dnt": 0,
+              "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+              "sua": {
+                "browsers": [
+                  {
+                    "brand": "Not?A_Brand",
+                    "version": [
+                      "8",
+                      "0",
+                      "0",
+                      "0"
+                    ]
+                  },
+                  {
+                    "brand": "Chromium",
+                    "version": [
+                      "108",
+                      "0",
+                      "5359",
+                      "124"
+                    ]
+                  },
+                  {
+                    "brand": "Google Chrome",
+                    "version": [
+                      "108",
+                      "0",
+                      "5359",
+                      "124"
+                    ]
+                  }
+                ],
+                "platform": {
+                  "brand": "macOS",
+                  "version": [
+                    "12",
+                    "3",
+                    "1"
+                  ]
+                },
+                "mobile": 0,
+                "architecture": "x86",
+                "bitness": "64",
+                "source": 2
+              },
+              "h": 1169,
+              "w": 1098,
+              "language": "de"
+            },
+            "tmax": 1000,
+            "source": {
+              "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+            },
+            "ext": {
+              "prebid": {
+                "auctiontimestamp": 1.671449004622e+12,
+                "channel": {
+                  "name": "pbjs",
+                  "version": "v7.28.0"
+                },
+                "debug": true,
+                "targeting": {
+                  "includebidderkeys": false,
+                  "includewinners": true
+                }
+              }
+            }
+          }
+        },
+        "mockResponse": {
+          "status": 418,
+          "body": {}
+        }
+      }
+    ],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [
+        {
+          "value": "Unexpected status code: 418. Run with request.debug = 1 for more info",
+          "comparison": "literal"
+        }
+      ]
+  }
\ No newline at end of file
diff --git a/adapters/definemedia/definemediatest/supplemental/unmarshal-error.json b/adapters/definemedia/definemediatest/supplemental/unmarshal-error.json
new file mode 100644
index 00000000..396d6821
--- /dev/null
+++ b/adapters/definemedia/definemediatest/supplemental/unmarshal-error.json
@@ -0,0 +1,224 @@
+{
+    "mockBidRequest": {
+      "imp": [
+        {
+          "ext": {
+            "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9",
+            "prebid": {
+              "bidder": {
+                "definemedia": {}
+              }
+            }
+          },
+          "id": "div-gpt-ad-1460505748561-0",
+          "banner": {
+            "topframe": 1,
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          }
+        }
+      ],
+      "site": {
+        "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+        "domain": "localhost:8080",
+        "publisher": {
+          "domain": "localhost:8080",
+          "id": "1"
+        }
+      },
+      "device": {
+        "w": 1098,
+        "h": 1169,
+        "dnt": 0,
+        "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+        "language": "de",
+        "sua": {
+          "source": 2,
+          "platform": {
+            "brand": "macOS",
+            "version": [
+              "12",
+              "3",
+              "1"
+            ]
+          },
+          "browsers": [
+            {
+              "brand": "Not?A_Brand",
+              "version": [
+                "8",
+                "0",
+                "0",
+                "0"
+              ]
+            },
+            {
+              "brand": "Chromium",
+              "version": [
+                "108",
+                "0",
+                "5359",
+                "124"
+              ]
+            },
+            {
+              "brand": "Google Chrome",
+              "version": [
+                "108",
+                "0",
+                "5359",
+                "124"
+              ]
+            }
+          ],
+          "mobile": 0,
+          "model": "",
+          "bitness": "64",
+          "architecture": "x86"
+        }
+      },
+      "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+      "test": 0,
+      "source": {
+        "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+      },
+      "ext": {
+        "prebid": {
+          "auctiontimestamp": 1671449004622,
+          "targeting": {
+            "includewinners": true,
+            "includebidderkeys": false
+          },
+          "debug": true,
+          "channel": {
+            "name": "pbjs",
+            "version": "v7.28.0"
+          }
+        }
+      },
+      "tmax": 1000
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://rtb.conative.network/openrtb2/auction",
+          "body": {
+            "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+            "imp": [
+              {
+                "id": "div-gpt-ad-1460505748561-0",
+                "banner": {
+                  "format": [
+                    {
+                      "w": 300,
+                      "h": 250
+                    }
+                  ],
+                  "topframe": 1
+                },
+                "ext": {
+                  "prebid": {
+                    "bidder": {
+                      "definemedia": {}
+                    }
+                  },
+                  "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9"
+                }
+              }
+            ],
+            "site": {
+              "domain": "localhost:8080",
+              "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+              "publisher": {
+                "id": "1",
+                "domain": "localhost:8080"
+              }
+            },
+            "device": {
+              "dnt": 0,
+              "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+              "sua": {
+                "browsers": [
+                  {
+                    "brand": "Not?A_Brand",
+                    "version": [
+                      "8",
+                      "0",
+                      "0",
+                      "0"
+                    ]
+                  },
+                  {
+                    "brand": "Chromium",
+                    "version": [
+                      "108",
+                      "0",
+                      "5359",
+                      "124"
+                    ]
+                  },
+                  {
+                    "brand": "Google Chrome",
+                    "version": [
+                      "108",
+                      "0",
+                      "5359",
+                      "124"
+                    ]
+                  }
+                ],
+                "platform": {
+                  "brand": "macOS",
+                  "version": [
+                    "12",
+                    "3",
+                    "1"
+                  ]
+                },
+                "mobile": 0,
+                "architecture": "x86",
+                "bitness": "64",
+                "source": 2
+              },
+              "h": 1169,
+              "w": 1098,
+              "language": "de"
+            },
+            "tmax": 1000,
+            "source": {
+              "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+            },
+            "ext": {
+              "prebid": {
+                "auctiontimestamp": 1.671449004622e+12,
+                "channel": {
+                  "name": "pbjs",
+                  "version": "v7.28.0"
+                },
+                "debug": true,
+                "targeting": {
+                  "includebidderkeys": false,
+                  "includewinners": true
+                }
+              }
+            }
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": "fail for unmarshall"
+        }
+      }
+    ],
+    "expectedBidResponses": [],
+    "expectedMakeBidsErrors": [
+        {
+            "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse",
+            "comparison": "literal"
+        }
+    ]
+  }
\ No newline at end of file
diff --git a/adapters/definemedia/definemediatest/supplemental/unsupported-type.json b/adapters/definemedia/definemediatest/supplemental/unsupported-type.json
new file mode 100644
index 00000000..0c1add16
--- /dev/null
+++ b/adapters/definemedia/definemediatest/supplemental/unsupported-type.json
@@ -0,0 +1,250 @@
+{
+  "mockBidRequest": {
+    "imp": [
+      {
+        "ext": {
+          "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9",
+          "prebid": {
+            "bidder": {
+              "definemedia": {}
+            }
+          }
+        },
+        "id": "div-gpt-ad-1460505748561-0",
+        "video": {
+          "w": 300,
+          "h": 250,
+          "maxduration": 60,
+          "minduration": 1,
+          "api": [1, 2, 5, 6, 7],
+          "mimes": ["video/mp4"],
+          "placement": 4,
+          "protocols": [2]
+        }
+      }
+    ],
+    "site": {
+      "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+      "domain": "localhost:8080",
+      "publisher": {
+        "domain": "localhost:8080",
+        "id": "1"
+      }
+    },
+    "device": {
+      "w": 1098,
+      "h": 1169,
+      "dnt": 0,
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+      "language": "de",
+      "sua": {
+        "source": 2,
+        "platform": {
+          "brand": "macOS",
+          "version": [
+            "12",
+            "3",
+            "1"
+          ]
+        },
+        "browsers": [
+          {
+            "brand": "Not?A_Brand",
+            "version": [
+              "8",
+              "0",
+              "0",
+              "0"
+            ]
+          },
+          {
+            "brand": "Chromium",
+            "version": [
+              "108",
+              "0",
+              "5359",
+              "124"
+            ]
+          },
+          {
+            "brand": "Google Chrome",
+            "version": [
+              "108",
+              "0",
+              "5359",
+              "124"
+            ]
+          }
+        ],
+        "mobile": 0,
+        "model": "",
+        "bitness": "64",
+        "architecture": "x86"
+      }
+    },
+    "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+    "test": 0,
+    "source": {
+      "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+    },
+    "ext": {
+      "prebid": {
+        "auctiontimestamp": 1671449004622,
+        "targeting": {
+          "includewinners": true,
+          "includebidderkeys": false
+        },
+        "debug": true,
+        "channel": {
+          "name": "pbjs",
+          "version": "v7.28.0"
+        }
+      }
+    },
+    "tmax": 1000
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://rtb.conative.network/openrtb2/auction",
+        "body": {
+          "id": "ceb63773-4280-45f2-a684-94cf6a3b8fcf",
+          "imp": [
+            {
+              "id": "div-gpt-ad-1460505748561-0",
+              "video": {
+                "w": 300,
+                "h": 250,
+                "maxduration": 60,
+                "minduration": 1,
+                "api": [1, 2, 5, 6, 7],
+                "mimes": ["video/mp4"],
+                "placement": 4,
+                "protocols": [2]
+              },
+              "ext": {
+                "prebid": {
+                  "bidder": {
+                    "definemedia": {}
+                  }
+                },
+                "tid": "397962d3-47a1-4634-9cb7-78597b01d9a9"
+              }
+            }
+          ],
+          "site": {
+            "domain": "localhost:8080",
+            "page": "http://localhost:8080/prebidServer_example.html?pbjs_debug=true",
+            "publisher": {
+              "id": "1",
+              "domain": "localhost:8080"
+            }
+          },
+          "device": {
+            "dnt": 0,
+            "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
+            "sua": {
+              "browsers": [
+                {
+                  "brand": "Not?A_Brand",
+                  "version": [
+                    "8",
+                    "0",
+                    "0",
+                    "0"
+                  ]
+                },
+                {
+                  "brand": "Chromium",
+                  "version": [
+                    "108",
+                    "0",
+                    "5359",
+                    "124"
+                  ]
+                },
+                {
+                  "brand": "Google Chrome",
+                  "version": [
+                    "108",
+                    "0",
+                    "5359",
+                    "124"
+                  ]
+                }
+              ],
+              "platform": {
+                "brand": "macOS",
+                "version": [
+                  "12",
+                  "3",
+                  "1"
+                ]
+              },
+              "mobile": 0,
+              "architecture": "x86",
+              "bitness": "64",
+              "source": 2
+            },
+            "h": 1169,
+            "w": 1098,
+            "language": "de"
+          },
+          "tmax": 1000,
+          "source": {
+            "tid": "ceb63773-4280-45f2-a684-94cf6a3b8fcf"
+          },
+          "ext": {
+            "prebid": {
+              "auctiontimestamp": 1.671449004622e+12,
+              "channel": {
+                "name": "pbjs",
+                "version": "v7.28.0"
+              },
+              "debug": true,
+              "targeting": {
+                "includebidderkeys": false,
+                "includewinners": true
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "46189656-7e2e-477d-b7f2-e05de224bb89",
+                  "impid": "div-gpt-ad-1460505748561-0",
+                  "price": 100,
+                  "adm": "{banner html}",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "crid": "test-creative-id",
+                  "ext": {
+                    "prebid": {
+                      "type": "video"
+                    }
+                  }
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+      {
+        "value": "Invalid mediatype in the impression",
+        "comparison": "literal"
+      }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/definemedia/params_test.go b/adapters/definemedia/params_test.go
new file mode 100644
index 00000000..63ef5272
--- /dev/null
+++ b/adapters/definemedia/params_test.go
@@ -0,0 +1,48 @@
+package definemedia
+
+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.BidderDefinemedia, 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.BidderDefinemedia, json.RawMessage(p)); err == nil {
+			t.Errorf("Schema allowed invalid params: %s", p)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"mandantId":123}`,
+	`{"mandantId":123, "adslotId":456}`,
+}
+
+var invalidParams = []string{
+	`{"mandantId": "42"}`,
+	`{"MandantId": "42"}`,
+	`{"mandantId":123, "adslotId":"456"}`,
+	`{"adslotId":456}`,
+	`{"adslotId":"456"}`,
+	`{}`,
+}
diff --git a/adapters/synacormedia/synacormedia_test.go b/adapters/imds/imds_test.go
similarity index 74%
rename from adapters/synacormedia/synacormedia_test.go
rename to adapters/imds/imds_test.go
index d4e5e70f..3a8c33ed 100644
--- a/adapters/synacormedia/synacormedia_test.go
+++ b/adapters/imds/imds_test.go
@@ -1,4 +1,4 @@
-package synacormedia
+package imds
 
 import (
 	"testing"
@@ -10,18 +10,18 @@ import (
 )
 
 func TestJsonSamples(t *testing.T) {
-	bidder, buildErr := Builder(openrtb_ext.BidderSynacormedia, config.Adapter{
+	bidder, buildErr := Builder(openrtb_ext.BidderImds, config.Adapter{
 		Endpoint: "http://{{.Host}}.technoratimedia.com/openrtb/bids/{{.Host}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
 	}
 
-	adapterstest.RunJSONBidderTest(t, "synacormediatest", bidder)
+	adapterstest.RunJSONBidderTest(t, "imdstest", bidder)
 }
 
 func TestEndpointTemplateMalformed(t *testing.T) {
-	_, buildErr := Builder(openrtb_ext.BidderSynacormedia, config.Adapter{
+	_, buildErr := Builder(openrtb_ext.BidderImds, config.Adapter{
 		Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
 
 	assert.Error(t, buildErr)
diff --git a/adapters/synacormedia/synacormediatest/exemplary/simple-banner.json b/adapters/imds/imdstest/exemplary/simple-banner.json
similarity index 97%
rename from adapters/synacormedia/synacormediatest/exemplary/simple-banner.json
rename to adapters/imds/imdstest/exemplary/simple-banner.json
index 944e6e54..5b15f9e0 100644
--- a/adapters/synacormedia/synacormediatest/exemplary/simple-banner.json
+++ b/adapters/imds/imdstest/exemplary/simple-banner.json
@@ -73,12 +73,12 @@
                   }
                 }
               ],
-              "seat": "synacormedia"
+              "seat": "imds"
             }
           ],
           "ext": {
             "responsetimemillis": {
-              "synacormedia": 339
+              "imds": 339
             }
           }
         }
diff --git a/adapters/synacormedia/synacormediatest/exemplary/simple-video.json b/adapters/imds/imdstest/exemplary/simple-video.json
similarity index 97%
rename from adapters/synacormedia/synacormediatest/exemplary/simple-video.json
rename to adapters/imds/imdstest/exemplary/simple-video.json
index 2cddd522..9aaea18c 100644
--- a/adapters/synacormedia/synacormediatest/exemplary/simple-video.json
+++ b/adapters/imds/imdstest/exemplary/simple-video.json
@@ -87,12 +87,12 @@
                   }
                 }
               ],
-              "seat": "synacormedia"
+              "seat": "imds"
             }
           ],
           "ext": {
             "responsetimemillis": {
-              "synacormedia": 339
+              "imds": 339
             }
           }
         }
diff --git a/adapters/synacormedia/synacormediatest/supplemental/audio_response.json b/adapters/imds/imdstest/supplemental/audio_response.json
similarity index 100%
rename from adapters/synacormedia/synacormediatest/supplemental/audio_response.json
rename to adapters/imds/imdstest/supplemental/audio_response.json
diff --git a/adapters/synacormedia/synacormediatest/supplemental/bad_response.json b/adapters/imds/imdstest/supplemental/bad_response.json
similarity index 100%
rename from adapters/synacormedia/synacormediatest/supplemental/bad_response.json
rename to adapters/imds/imdstest/supplemental/bad_response.json
diff --git a/adapters/synacormedia/synacormediatest/supplemental/bad_seat_id.json b/adapters/imds/imdstest/supplemental/bad_seat_id.json
similarity index 91%
rename from adapters/synacormedia/synacormediatest/supplemental/bad_seat_id.json
rename to adapters/imds/imdstest/supplemental/bad_seat_id.json
index 00dd2c23..18630c59 100644
--- a/adapters/synacormedia/synacormediatest/supplemental/bad_seat_id.json
+++ b/adapters/imds/imdstest/supplemental/bad_seat_id.json
@@ -24,7 +24,7 @@
 
   "expectedMakeRequestsErrors": [
     {
-      "value": "json: cannot unmarshal number into Go struct field ExtImpSynacormedia.seatId of type string",
+      "value": "json: cannot unmarshal number into Go struct field ExtImpImds.seatId of type string",
       "comparison": "literal"
     }
   ]
diff --git a/adapters/synacormedia/synacormediatest/supplemental/missing_seat_id.json b/adapters/imds/imdstest/supplemental/missing_seat_id.json
similarity index 100%
rename from adapters/synacormedia/synacormediatest/supplemental/missing_seat_id.json
rename to adapters/imds/imdstest/supplemental/missing_seat_id.json
diff --git a/adapters/synacormedia/synacormediatest/supplemental/missing_tag_id.json b/adapters/imds/imdstest/supplemental/missing_tag_id.json
similarity index 100%
rename from adapters/synacormedia/synacormediatest/supplemental/missing_tag_id.json
rename to adapters/imds/imdstest/supplemental/missing_tag_id.json
diff --git a/adapters/synacormedia/synacormediatest/supplemental/native_response.json b/adapters/imds/imdstest/supplemental/native_response.json
similarity index 100%
rename from adapters/synacormedia/synacormediatest/supplemental/native_response.json
rename to adapters/imds/imdstest/supplemental/native_response.json
diff --git a/adapters/synacormedia/synacormediatest/supplemental/one_bad_ext.json b/adapters/imds/imdstest/supplemental/one_bad_ext.json
similarity index 97%
rename from adapters/synacormedia/synacormediatest/supplemental/one_bad_ext.json
rename to adapters/imds/imdstest/supplemental/one_bad_ext.json
index 5749aadb..f5e9fed8 100644
--- a/adapters/synacormedia/synacormediatest/supplemental/one_bad_ext.json
+++ b/adapters/imds/imdstest/supplemental/one_bad_ext.json
@@ -90,12 +90,12 @@
                   }
                 }
               ],
-              "seat": "synacormedia"
+              "seat": "imds"
             }
           ],
           "ext": {
             "responsetimemillis": {
-              "synacormedia": 339
+              "imds": 339
             }
           }
         }
diff --git a/adapters/synacormedia/synacormediatest/supplemental/status_204.json b/adapters/imds/imdstest/supplemental/status_204.json
similarity index 100%
rename from adapters/synacormedia/synacormediatest/supplemental/status_204.json
rename to adapters/imds/imdstest/supplemental/status_204.json
diff --git a/adapters/synacormedia/synacormediatest/supplemental/status_400.json b/adapters/imds/imdstest/supplemental/status_400.json
similarity index 100%
rename from adapters/synacormedia/synacormediatest/supplemental/status_400.json
rename to adapters/imds/imdstest/supplemental/status_400.json
diff --git a/adapters/synacormedia/synacormediatest/supplemental/status_500.json b/adapters/imds/imdstest/supplemental/status_500.json
similarity index 100%
rename from adapters/synacormedia/synacormediatest/supplemental/status_500.json
rename to adapters/imds/imdstest/supplemental/status_500.json
diff --git a/adapters/synacormedia/params_test.go b/adapters/imds/params_test.go
similarity index 57%
rename from adapters/synacormedia/params_test.go
rename to adapters/imds/params_test.go
index edb9a2a4..e2052b4c 100644
--- a/adapters/synacormedia/params_test.go
+++ b/adapters/imds/params_test.go
@@ -1,4 +1,4 @@
-package synacormedia
+package imds
 
 import (
 	"encoding/json"
@@ -7,11 +7,11 @@ import (
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
-// This file actually intends to test static/bidder-params/synacormedia.json
+// This file actually intends to test static/bidder-params/imds.json
 //
-// These also validate the format of the external API: request.imp[i].ext.prebid.bidder.synacormedia
+// These also validate the format of the external API: request.imp[i].ext.prebid.bidder.imds
 
-// TestValidParams makes sure that the synacormedia schema accepts all imp.ext fields which we intend to support.
+// TestValidParams makes sure that the imds schema accepts all imp.ext fields which we intend to support.
 func TestValidParams(t *testing.T) {
 	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
 	if err != nil {
@@ -19,13 +19,13 @@ func TestValidParams(t *testing.T) {
 	}
 
 	for _, validParam := range validParams {
-		if err := validator.Validate(openrtb_ext.BidderSynacormedia, json.RawMessage(validParam)); err != nil {
-			t.Errorf("Schema rejected synacormedia params: %s", validParam)
+		if err := validator.Validate(openrtb_ext.BidderImds, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected imds params: %s", validParam)
 		}
 	}
 }
 
-// TestInvalidParams makes sure that the synacormedia schema rejects all the imp.ext fields we don't support.
+// TestInvalidParams makes sure that the imds 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 {
@@ -33,7 +33,7 @@ func TestInvalidParams(t *testing.T) {
 	}
 
 	for _, invalidParam := range invalidParams {
-		if err := validator.Validate(openrtb_ext.BidderSynacormedia, json.RawMessage(invalidParam)); err == nil {
+		if err := validator.Validate(openrtb_ext.BidderImds, json.RawMessage(invalidParam)); err == nil {
 			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
 		}
 	}
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index dbe88169..32057c50 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -1668,6 +1668,117 @@ func TestRequestBidsStoredBidResponses(t *testing.T) {
 
 }
 
+// TestFledge verifies that fledge responses from bidders are collected.
+func TestFledge(t *testing.T) {
+	respStatus := 200
+	respBody := "{\"bid\":false}"
+	server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody))
+	defer server.Close()
+
+	fledgeAuctionConfig1 := &openrtb_ext.FledgeAuctionConfig{
+		ImpId:  "imp-id-1",
+		Config: json.RawMessage("[1,2,3]"),
+		Bidder: "openx",
+	}
+	fledgeAuctionConfig2 := &openrtb_ext.FledgeAuctionConfig{
+		ImpId:  "imp-id-2",
+		Config: json.RawMessage("[3,2,1]"),
+		Bidder: "openx",
+	}
+
+	testCases := []struct {
+		mockBidderResponse []*adapters.BidderResponse
+		expectedFledge     []*openrtb_ext.FledgeAuctionConfig
+		description        string
+	}{
+		{
+			mockBidderResponse: []*adapters.BidderResponse{
+				{
+					Bids:                 []*adapters.TypedBid{},
+					FledgeAuctionConfigs: []*openrtb_ext.FledgeAuctionConfig{fledgeAuctionConfig1, fledgeAuctionConfig2},
+				},
+				nil,
+			},
+			expectedFledge: []*openrtb_ext.FledgeAuctionConfig{fledgeAuctionConfig1, fledgeAuctionConfig2},
+			description:    "Collects FLEDGE auction configs from single bidder response",
+		},
+		{
+			mockBidderResponse: []*adapters.BidderResponse{
+				{
+					Bids:                 []*adapters.TypedBid{},
+					FledgeAuctionConfigs: []*openrtb_ext.FledgeAuctionConfig{fledgeAuctionConfig2},
+				},
+				{
+					Bids:                 []*adapters.TypedBid{},
+					FledgeAuctionConfigs: []*openrtb_ext.FledgeAuctionConfig{fledgeAuctionConfig1},
+				},
+			},
+			expectedFledge: []*openrtb_ext.FledgeAuctionConfig{fledgeAuctionConfig1, fledgeAuctionConfig2},
+			description:    "Collects FLEDGE auction configs from multiple bidder response",
+		},
+	}
+
+	for _, tc := range testCases {
+		bidderImpl := &goodMultiHTTPCallsBidder{
+			httpRequest: []*adapters.RequestData{
+				{
+					Method:  "POST",
+					Uri:     server.URL,
+					Body:    []byte("{\"key\":\"val1\"}"),
+					Headers: http.Header{},
+				},
+				{
+					Method:  "POST",
+					Uri:     server.URL,
+					Body:    []byte("{\"key\":\"val2\"}"),
+					Headers: http.Header{},
+				},
+			},
+			bidResponses: tc.mockBidderResponse,
+		}
+		bidder := AdaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderOpenx, nil, "")
+		currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
+
+		bidderReq := BidderRequest{
+			BidRequest: &openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{
+					{
+						ID:     "imp-id-1",
+						Banner: &openrtb2.Banner{},
+						Ext:    json.RawMessage(`{"ae": 1}`),
+					},
+					{
+						ID:     "imp-id-2",
+						Banner: &openrtb2.Banner{},
+						Ext:    json.RawMessage(`{"ae": 1}`),
+					},
+				},
+			},
+			BidderName: "openx",
+		}
+		seatBids, _ := bidder.requestBid(
+			context.Background(),
+			bidderReq,
+			currencyConverter.Rates(),
+			&adapters.ExtraRequestInfo{},
+			&adscert.NilSigner{},
+			bidRequestOptions{
+				accountDebugAllowed: true,
+				headerDebugAllowed:  true,
+				addCallSignHeader:   false,
+				bidAdjustments:      map[string]float64{"test": 1.0},
+			},
+			openrtb_ext.ExtAlternateBidderCodes{},
+			&hookexecution.EmptyHookExecutor{},
+		)
+		assert.Len(t, seatBids, 1)
+		assert.NotNil(t, seatBids[0].FledgeAuctionConfigs)
+		assert.Len(t, seatBids[0].FledgeAuctionConfigs, len(tc.expectedFledge))
+
+		assert.ElementsMatch(t, seatBids[0].FledgeAuctionConfigs, tc.expectedFledge)
+	}
+}
+
 func TestErrorReporting(t *testing.T) {
 	bidder := AdaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderAppnexus, nil, "")
 	currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index e53ba7b4..8bf8427c 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -2299,6 +2299,10 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 
 	}
 
+	if spec.FledgeEnabled {
+		assert.JSONEq(t, string(spec.Response.Ext), string(bid.Ext), "ext mismatch")
+	}
+
 	if spec.HostConfigBidValidation.BannerCreativeMaxSize == config.ValidationEnforce || spec.HostConfigBidValidation.SecureMarkup == config.ValidationEnforce {
 		actualBidRespExt := &openrtb_ext.ExtBidResponse{}
 		expectedBidRespExt := &openrtb_ext.ExtBidResponse{}
@@ -3926,6 +3930,7 @@ func TestBuildStoredAuctionResponses(t *testing.T) {
 	}
 	type testResults struct {
 		adapterBids  map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid
+		fledge       *openrtb_ext.Fledge
 		liveAdapters []openrtb_ext.BidderName
 	}
 
@@ -4056,13 +4061,40 @@ func TestBuildStoredAuctionResponses(t *testing.T) {
 				liveAdapters: []openrtb_ext.BidderName{openrtb_ext.BidderName("appnexus"), openrtb_ext.BidderName("rubicon")},
 			},
 		},
+		{
+			desc: "Fledge in stored response bid",
+			in: testIn{
+				StoredAuctionResponses: map[string]json.RawMessage{
+					"impression-id": json.RawMessage(`[{"bid": [],"seat": "openx", "ext": {"prebid": {"fledge": {"auctionconfigs": [{"impid": "1", "bidder": "openx", "adapter": "openx", "config": [1,2,3]}]}}}}]`),
+				},
+			},
+			expected: testResults{
+				adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+					openrtb_ext.BidderName("openx"): {
+						Bids: []*entities.PbsOrtbBid{},
+					},
+				},
+				liveAdapters: []openrtb_ext.BidderName{openrtb_ext.BidderName("openx")},
+				fledge: &openrtb_ext.Fledge{
+					AuctionConfigs: []*openrtb_ext.FledgeAuctionConfig{
+						{
+							ImpId:   "impression-id",
+							Bidder:  "openx",
+							Adapter: "openx",
+							Config:  json.RawMessage("[1,2,3]"),
+						},
+					},
+				},
+			},
+		},
 	}
 	for _, test := range testCases {
 
-		bids, adapters, err := buildStoredAuctionResponse(test.in.StoredAuctionResponses)
+		bids, fledge, adapters, err := buildStoredAuctionResponse(test.in.StoredAuctionResponses)
 		assert.NoErrorf(t, err, "%s. HoldAuction error: %v \n", test.desc, err)
 
 		assert.ElementsMatch(t, test.expected.liveAdapters, adapters, "Incorrect adapter list")
+		assert.Equal(t, fledge, test.expected.fledge, "Incorrect FLEDGE response")
 
 		for _, bidderName := range test.expected.liveAdapters {
 			assert.ElementsMatch(t, test.expected.adapterBids[bidderName].Bids, bids[bidderName].Bids, "Incorrect bids")
@@ -4676,6 +4708,7 @@ type exchangeSpec struct {
 	HostSChainFlag             bool                   `json:"host_schain_flag,omitempty"`
 	HostConfigBidValidation    config.Validations     `json:"host_bid_validations"`
 	AccountConfigBidValidation config.Validations     `json:"account_bid_validations"`
+	FledgeEnabled              bool                   `json:"fledge_enabled,omitempty"`
 }
 
 type exchangeRequest struct {
@@ -4710,8 +4743,9 @@ type bidderResponse struct {
 // The only real reason I'm not reusing that type is because I don't want people to think that the
 // JSON property tags on those types are contracts in prod.
 type bidderSeatBid struct {
-	Bids []bidderBid `json:"pbsBids,omitempty"`
-	Seat string      `json:"seat"`
+	Bids                 []bidderBid                        `json:"pbsBids,omitempty"`
+	Seat                 string                             `json:"seat"`
+	FledgeAuctionConfigs []*openrtb_ext.FledgeAuctionConfig `json:"fledgeAuctionConfigs,omitempty"`
 }
 
 // bidderBid is basically a subset of entities.PbsOrtbBid from exchange/bidder.go.
@@ -4773,9 +4807,10 @@ func (b *validatingBidder) requestBid(ctx context.Context, bidderRequest BidderR
 				}
 
 				seatBids = append(seatBids, &entities.PbsOrtbSeatBid{
-					Bids:      bids,
-					HttpCalls: mockResponse.HttpCalls,
-					Seat:      mockSeatBid.Seat,
+					Bids:                 bids,
+					HttpCalls:            mockResponse.HttpCalls,
+					Seat:                 mockSeatBid.Seat,
+					FledgeAuctionConfigs: mockSeatBid.FledgeAuctionConfigs,
 				})
 			}
 		} else {
diff --git a/exchange/exchangetest/fledge-no-bids.json b/exchange/exchangetest/fledge-no-bids.json
new file mode 100644
index 00000000..d941fadd
--- /dev/null
+++ b/exchange/exchangetest/fledge-no-bids.json
@@ -0,0 +1,117 @@
+{
+    "description": "FLEDGE request/response when no contextual bids exist",
+    "fledge_enabled": true,
+    "debuglog": {
+        "enabled": false,
+        "debug_override": false,
+        "debug_enabled_or_overridden": false
+    },
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [
+                {
+                    "id": "my-imp-id",
+                    "banner": {
+                        "format": [{"w": 728, "h": 90}]
+                    },
+                    "ext": {
+                        "ae": 1,
+                        "prebid": {
+                            "bidder": {
+                                "openx": {
+                                    "unit": "539439964",
+                                    "delDomain": "se-demo-d.openx.net"
+                                }
+                            }
+                        }
+                    }
+                }
+            ]
+        }
+    },
+    "outgoingRequests": {
+        "openx": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [{
+                        "id": "my-imp-id",
+                        "banner": {
+                            "format": [{"w": 728, "h": 90}]
+                        },
+                        "ext": {
+                            "ae": 1,
+                            "bidder": {
+                                "unit": "539439964",
+                                "delDomain": "se-demo-d.openx.net"
+                            }
+                        }
+                    }]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [],
+                        "seat": "openx",
+                        "fledgeAuctionConfigs": [
+                            {
+                                "impid": "test-request-id",
+                                "bidder": "openx",
+                                "config": {
+                                    "seller": "openx.com",
+                                    "interestGroupBuyers": ["buyer1.com"],
+                                    "sellerTimeout": 0,
+                                    "perBuyerSignals": {
+                                        "buyer1.com": [1,"two",3,4, {}]
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                ]
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "seatbid": []
+        },
+        "ext": {
+            "prebid": {
+                "fledge": {
+                    "auctionconfigs": [
+                        {
+                            "impid": "test-request-id",
+                            "adapter":"openx",
+                            "bidder": "openx",
+                            "config": {
+                                "seller": "openx.com",
+                                "interestGroupBuyers": ["buyer1.com"],
+                                "sellerTimeout": 0,
+                                "perBuyerSignals": {
+                                    "buyer1.com": [1,"two",3,4, {}]
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "warnings": {
+                "general": [
+                    {
+                        "code": 10002,
+                        "message": "debug turned off for account"
+                    }
+                ]
+            }
+        }
+    }
+}
diff --git a/exchange/exchangetest/fledge-with-bids.json b/exchange/exchangetest/fledge-with-bids.json
new file mode 100644
index 00000000..0f998e69
--- /dev/null
+++ b/exchange/exchangetest/fledge-with-bids.json
@@ -0,0 +1,155 @@
+{
+    "description": "FLEDGE request/response with additional contextual bids exist",
+    "fledge_enabled": true,
+    "debuglog": {
+        "enabled": false,
+        "debug_override": false,
+        "debug_enabled_or_overridden": false
+    },
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [
+                {
+                    "id": "my-imp-id",
+                    "banner": {
+                        "format": [{"w": 728, "h": 90}]
+                    },
+                    "ext": {
+                        "ae": 1,
+                        "prebid": {
+                            "bidder": {
+                                "openx": {
+                                    "unit": "539439964",
+                                    "delDomain": "se-demo-d.openx.net"
+                                }
+                            }
+                        }
+                    }
+                }
+            ]
+        }
+    },
+    "outgoingRequests": {
+        "openx": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [{
+                        "id": "my-imp-id",
+                        "banner": {
+                            "format": [{"w": 728, "h": 90}]
+                        },
+                        "ext": {
+                            "ae": 1,
+                            "bidder": {
+                                "unit": "539439964",
+                                "delDomain": "se-demo-d.openx.net"
+                            }
+                        }
+                    }]
+                }
+            },
+            "mockResponse": {
+                "pbsSeatBids": [
+                    {
+                        "pbsBids": [
+                            {
+                                "ortbBid": {
+                                    "id": "apn-bid",
+                                    "impid": "my-imp-id",
+                                    "price": 0.3,
+                                    "w": 200,
+                                    "h": 250,
+                                    "crid": "creative-1",
+                                    "ext": {
+                                        "origbidcpm": 0.3,
+                                        "someField": "someValue"
+                                    }
+                                },
+                                "bidType": "video"
+                            }
+                        ],
+                        "seat": "openx",
+                        "fledgeAuctionConfigs": [
+                            {
+                                "impid": "test-request-id",
+                                "bidder": "openx",
+                                "config": {
+                                    "seller": "openx.com",
+                                    "interestGroupBuyers": ["buyer1.com"],
+                                    "sellerTimeout": 0,
+                                    "perBuyerSignals": {
+                                        "buyer1.com": [1,"two",3,4, {}]
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                ]
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [
+                {
+                    "seat": "openx",
+                    "bid": [
+                        {
+                            "id": "apn-bid",
+                            "impid": "my-imp-id",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 250,
+                            "crid": "creative-1",
+                            "ext": {
+                                "origbidcpm": 0.3,
+                                "someField": "someValue",
+                                "prebid": {
+                                    "type": "video"
+                                }
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        "ext": {
+            "prebid": {
+                "fledge": {
+                    "auctionconfigs": [
+                        {
+                            "impid": "test-request-id",
+                            "adapter":"openx",
+                            "bidder": "openx",
+                            "config": {
+                                "seller": "openx.com",
+                                "interestGroupBuyers": ["buyer1.com"],
+                                "sellerTimeout": 0,
+                                "perBuyerSignals": {
+                                    "buyer1.com": [1,"two",3,4, {}]
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "warnings": {
+                "general": [
+                    {
+                        "code": 10002,
+                        "message": "debug turned off for account"
+                    }
+                ]
+            }
+        }
+    }
+}
diff --git a/hooks/hookexecution/executor_test.go b/hooks/hookexecution/executor_test.go
index f0c3ec66..33553bbd 100644
--- a/hooks/hookexecution/executor_test.go
+++ b/hooks/hookexecution/executor_test.go
@@ -384,8 +384,9 @@ func TestMetricsAreGatheredDuringHookExecution(t *testing.T) {
 	metricEngine := &metrics.MetricsEngineMock{}
 	builder := TestAllHookResultsBuilder{}
 	exec := NewHookExecutor(TestAllHookResultsBuilder{}, "/openrtb2/auction", metricEngine)
+	moduleName := "module.x-1"
 	moduleLabels := metrics.ModuleLabels{
-		Module: "module-1",
+		Module: moduleReplacer.Replace(moduleName),
 		Stage:  "entrypoint",
 	}
 	rTime := func(dur time.Duration) bool { return dur.Nanoseconds() > 0 }
@@ -2576,12 +2577,12 @@ func (e TestAllHookResultsBuilder) PlanForEntrypointStage(_ string) hooks.Plan[h
 		hooks.Group[hookstage.Entrypoint]{
 			Timeout: 10 * time.Millisecond,
 			Hooks: []hooks.HookWrapper[hookstage.Entrypoint]{
-				{Module: "module-1", Code: "code-1", Hook: mockUpdateHeaderEntrypointHook{}},
-				{Module: "module-1", Code: "code-3", Hook: mockTimeoutHook{}},
-				{Module: "module-1", Code: "code-4", Hook: mockFailureHook{}},
-				{Module: "module-1", Code: "code-5", Hook: mockErrorHook{}},
-				{Module: "module-1", Code: "code-6", Hook: mockFailedMutationHook{}},
-				{Module: "module-1", Code: "code-7", Hook: mockModuleContextHook{key: "key", val: "val"}},
+				{Module: "module.x-1", Code: "code-1", Hook: mockUpdateHeaderEntrypointHook{}},
+				{Module: "module.x-1", Code: "code-3", Hook: mockTimeoutHook{}},
+				{Module: "module.x-1", Code: "code-4", Hook: mockFailureHook{}},
+				{Module: "module.x-1", Code: "code-5", Hook: mockErrorHook{}},
+				{Module: "module.x-1", Code: "code-6", Hook: mockFailedMutationHook{}},
+				{Module: "module.x-1", Code: "code-7", Hook: mockModuleContextHook{key: "key", val: "val"}},
 			},
 		},
 		// place the reject hook in a separate group because it rejects the stage completely
@@ -2589,7 +2590,7 @@ func (e TestAllHookResultsBuilder) PlanForEntrypointStage(_ string) hooks.Plan[h
 		hooks.Group[hookstage.Entrypoint]{
 			Timeout: 10 * time.Second,
 			Hooks: []hooks.HookWrapper[hookstage.Entrypoint]{
-				{Module: "module-1", Code: "code-2", Hook: mockRejectHook{}},
+				{Module: "module.x-1", Code: "code-2", Hook: mockRejectHook{}},
 			},
 		},
 	}
diff --git a/modules/prebid/ortb2blocking/module_test.go b/modules/prebid/ortb2blocking/module_test.go
index 654790c9..ce3bba00 100644
--- a/modules/prebid/ortb2blocking/module_test.go
+++ b/modules/prebid/ortb2blocking/module_test.go
@@ -8,6 +8,8 @@ import (
 
 	"github.com/prebid/openrtb/v17/adcom1"
 	"github.com/prebid/openrtb/v17/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/hooks/hookanalytics"
 	"github.com/prebid/prebid-server/hooks/hookexecution"
 	"github.com/prebid/prebid-server/hooks/hookstage"
 	"github.com/prebid/prebid-server/modules/moduledeps"
@@ -277,6 +279,9 @@ const bAttr8 adcom1.CreativeAttribute = 8
 const bAttr9 adcom1.CreativeAttribute = 9
 const bAttr10 adcom1.CreativeAttribute = 10
 
+const impID1 = "Some-impid-1"
+const impID2 = "Some-impid-2"
+
 func TestHandleBidderRequestHook(t *testing.T) {
 	testCases := []struct {
 		description        string
@@ -594,14 +599,923 @@ func TestHandleBidderRequestHook(t *testing.T) {
 	}
 }
 
-type numeric interface {
-	openrtb2.BannerAdType | adcom1.CreativeAttribute
-}
+func TestHandleRawBidderResponseHook(t *testing.T) {
+	testCases := []struct {
+		description        string
+		payload            hookstage.RawBidderResponsePayload
+		config             json.RawMessage
+		moduleCtx          hookstage.ModuleContext
+		expectedBids       []*adapters.TypedBid
+		expectedHookResult hookstage.HookResult[hookstage.RawBidderResponsePayload]
+		expectedError      error
+	}{
+		{
+			description: "Payload not changed when empty account config and empty module contexts are provided. Analytic tags have successful records",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ADomain: []string{"foo"}, ImpID: impID1},
+				},
+			}},
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ADomain: []string{"foo"}, ImpID: impID1},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			description: "Catch error if wrong data has been passed from previous hook. Payload not changed",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ADomain: []string{"foo"}, ImpID: impID1},
+				},
+			}},
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ADomain: []string{"foo"}, ImpID: impID1},
+				},
+			},
+			moduleCtx:     map[string]interface{}{bidder: "boo"},
+			expectedError: hookexecution.NewFailure("could not cast blocking attributes for bidder `appnexus`, module context has incorrect data"),
+		},
+		{
+			description: "Bid blocked by badv attribute check. Payload updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}, ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"badv":{"enforce_blocks": true}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			},
+			moduleCtx: map[string]interface{}{bidder: blockingAttributes{bAdv: []string{"forbidden_domain"}}},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status: hookanalytics.ResultStatusBlock,
+									Values: map[string]interface{}{
+										attributesAnalyticKey: []string{"badv"},
+										badvAnalyticKey:       []string{"forbidden_domain"},
+									},
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+				DebugMessages: []string{"Bid 1 from bidder appnexus has been rejected, failed checks: badv"},
+			},
+		},
+		{
+			description: "Bid not blocked because blocking conditions for current bidder do not exist. Payload not updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}, ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"badv":{"enforce_blocks": true}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}, ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			},
+			moduleCtx: map[string]interface{}{"other-bidder": blockingAttributes{bAdv: []string{"forbidden_domain"}}},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			description: "Bid not blocked because enforce blocking is disabled by account config. Payload not updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}, ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"badv":{"enforce_blocks": false}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}, ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			},
+			moduleCtx: map[string]interface{}{bidder: blockingAttributes{bAdv: []string{"forbidden_domain"}}},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			description: "Bid not blocked because enforce blocking overridden for given bidder. Payload not updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}, ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"badv":{"enforce_blocks": true, "action_overrides": {"enforce_blocks": [{"conditions": {"bidders": ["appnexus"]}, "override": false}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}, ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			},
+			moduleCtx: map[string]interface{}{bidder: blockingAttributes{bAdv: []string{"forbidden_domain"}}},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			description: "Bid blocked by badv attribute check (block unknown attributes). Payload updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"badv":{"enforce_blocks": true, "block_unknown_adomain": true}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			},
+			moduleCtx: map[string]interface{}{bidder: blockingAttributes{}},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status: hookanalytics.ResultStatusBlock,
+									Values: map[string]interface{}{
+										attributesAnalyticKey: []string{"badv"},
+										badvAnalyticKey:       []string(nil),
+									},
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+				DebugMessages: []string{"Bid 1 from bidder appnexus has been rejected, failed checks: badv"},
+			},
+		},
+		{
+			description: "Bid not blocked because block unknown overridden for given bidder. Payload not updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"badv":{"enforce_blocks": true, "block_unknown_adomain": true, "action_overrides": {"block_unknown_adomain": [{"conditions": {"bidders": ["appnexus"]}, "override": false}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ImpID: impID2},
+				},
+			},
+			moduleCtx: map[string]interface{}{bidder: blockingAttributes{}},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			description: "Bid not blocked due to deal exception. Payload not updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}, ImpID: impID1, DealID: "acceptDealID"},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"badv":{"enforce_blocks": true, "action_overrides": {"allowed_adomain_for_deals": [{"conditions": {"deal_ids": ["acceptDealID"]}, "override": ["forbidden_domain"]}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}, ImpID: impID1, DealID: "acceptDealID"},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
+				},
+			},
+			moduleCtx: map[string]interface{}{bidder: blockingAttributes{bAdv: []string{"forbidden_domain"}}},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			description: "Expect error if there is an issue processing enforce blocks overrides for badv attribute. Analytics should have error status tag",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}},
+				},
+			}},
+			config: json.RawMessage(`{"attributes": {"badv": {"enforce_blocks": true, "action_overrides": {"enforce_blocks": [{"conditions": {}}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusError,
+						},
+					},
+				},
+			},
+			expectedError: hookexecution.NewFailure("failed to process badv block checking: failed to get override for badv.enforce_blocks: bidders and media_types absent from conditions, at least one of the fields must be present"),
+		},
+		{
+			description: "Expect error if there is an issue processing block unknown domains overrides for badv attribute. Analytics should have error status tag",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}},
+				},
+			}},
+			config: json.RawMessage(`{"attributes": {"badv": {"enforce_blocks": true, "action_overrides": {"block_unknown_adomain": [{"conditions": {}}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusError,
+						},
+					},
+				},
+			},
+			expectedError: hookexecution.NewFailure("failed to process badv block checking: failed to get override for badv.block_unknown_adomain: bidders and media_types absent from conditions, at least one of the fields must be present"),
+		},
+		{
+			description: "Expect error if deal_ids not defined in config override conditions for badv attribute. Analytics should have error status tag",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}, DealID: "acceptDealID"},
+				},
+			}},
+			config: json.RawMessage(`{"attributes": {"badv": {"enforce_blocks": true, "action_overrides": {"allowed_adomain_for_deals": [{"conditions": {}}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", ADomain: []string{"forbidden_domain"}, DealID: "acceptDealID"},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusError,
+						},
+					},
+				},
+			},
+			expectedError: hookexecution.NewFailure("failed to process badv block checking: failed to get override for badv.allowed_adomain_for_deals: conditions field in account configuration must contain deal_ids"),
+		},
+		{
+			description: "Bid blocked by bcat attribute check. Payload updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Cat: []string{"fishing"}, ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", Cat: []string{"moto"}, ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"bcat":{"enforce_blocks": true}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "2", Cat: []string{"moto"}, ImpID: impID2},
+				},
+			},
+			moduleCtx: map[string]interface{}{bidder: blockingAttributes{bCat: []string{"fishing"}}},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status: hookanalytics.ResultStatusBlock,
+									Values: map[string]interface{}{
+										attributesAnalyticKey: []string{"bcat"},
+										"bcat":                []string{"fishing"},
+									},
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+				DebugMessages: []string{"Bid 1 from bidder appnexus has been rejected, failed checks: bcat"},
+			},
+		},
+		{
+			description: "Expect error if there is an issue processing enforce blocks overrides for bcat attribute. Analytics should have error status tag",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Cat: []string{"fishing"}, ImpID: impID1},
+				},
+			}},
+			config: json.RawMessage(`{"attributes": {"bcat": {"enforce_blocks": true, "action_overrides": {"enforce_blocks": [{"conditions": {}}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Cat: []string{"fishing"}, ImpID: impID1},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusError,
+						},
+					},
+				},
+			},
+			expectedError: hookexecution.NewFailure("failed to process bcat block checking: failed to get override for bcat.enforce_blocks: bidders and media_types absent from conditions, at least one of the fields must be present"),
+		},
+		{
+			description: "Expect error if there is an issue processing block unknown domains overrides for bcat attribute. Analytics should have error status tag",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Cat: []string{"fishing"}, ImpID: impID1},
+				},
+			}},
+			config: json.RawMessage(`{"attributes": {"bcat": {"enforce_blocks": true, "action_overrides": {"block_unknown_adv_cat": [{"conditions": {}}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Cat: []string{"fishing"}, ImpID: impID1},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusError,
+						},
+					},
+				},
+			},
+			expectedError: hookexecution.NewFailure("failed to process bcat block checking: failed to get override for bcat.block_unknown_adv_cat: bidders and media_types absent from conditions, at least one of the fields must be present"),
+		},
+		{
+			description: "Expect error if deal_ids not defined in config override conditions for bcat attribute. Analytics should have error status tag",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Cat: []string{"fishing"}, ImpID: impID1, DealID: "acceptDealID"},
+				},
+			}},
+			config: json.RawMessage(`{"attributes": {"bcat": {"enforce_blocks": true, "action_overrides": {"allowed_adv_cat_for_deals": [{"conditions": {}}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Cat: []string{"fishing"}, ImpID: impID1, DealID: "acceptDealID"},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusError,
+						},
+					},
+				},
+			},
+			expectedError: hookexecution.NewFailure("failed to process bcat block checking: failed to get override for bcat.allowed_adv_cat_for_deals: conditions field in account configuration must contain deal_ids"),
+		},
+		{
+			description: "Bid blocked by cattax attribute check. Payload updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", CatTax: 1, ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", CatTax: 2, ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"bcat":{"enforce_blocks": true}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "2", CatTax: 2, ImpID: impID2},
+				},
+			},
+			moduleCtx: map[string]interface{}{bidder: blockingAttributes{catTax: 2}},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status: hookanalytics.ResultStatusBlock,
+									Values: map[string]interface{}{
+										attributesAnalyticKey: []string{"cattax"},
+										cattaxAnalyticKey:     []adcom1.CategoryTaxonomy{1},
+									},
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+				DebugMessages: []string{"Bid 1 from bidder appnexus has been rejected, failed checks: cattax"},
+			},
+		},
+		{
+			description: "Bid blocked by cattax attribute check (the default value used if no blocking attribute passed). Payload updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", CatTax: 1, ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", CatTax: 2, ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"bcat":{"enforce_blocks": true}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", CatTax: 1, ImpID: impID1},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status: hookanalytics.ResultStatusBlock,
+									Values: map[string]interface{}{
+										attributesAnalyticKey: []string{"cattax"},
+										cattaxAnalyticKey:     []adcom1.CategoryTaxonomy{2},
+									},
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+				DebugMessages: []string{"Bid 2 from bidder appnexus has been rejected, failed checks: cattax"},
+			},
+		},
+		{
+			description: "Bid blocked by bapp attribute check. Payload updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Bundle: "forbidden_bundle", ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", Bundle: "allowed_bundle", ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"bapp":{"enforce_blocks": true}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "2", Bundle: "allowed_bundle", ImpID: impID2},
+				},
+			},
+			moduleCtx: map[string]interface{}{bidder: blockingAttributes{bApp: []string{"forbidden_bundle"}}},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status: hookanalytics.ResultStatusBlock,
+									Values: map[string]interface{}{
+										attributesAnalyticKey: []string{"bapp"},
+										bappAnalyticKey:       []string{"forbidden_bundle"},
+									},
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+				DebugMessages: []string{"Bid 1 from bidder appnexus has been rejected, failed checks: bapp"},
+			},
+		},
+		{
+			description: "Expect error if there is an issue processing enforce blocks overrides for bapp attribute. Analytics should have error status tag",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Bundle: "forbidden_bundle", ImpID: impID1},
+				},
+			}},
+			config: json.RawMessage(`{"attributes": {"bapp": {"enforce_blocks": true, "action_overrides": {"enforce_blocks": [{"conditions": {}}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Bundle: "forbidden_bundle", ImpID: impID1},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusError,
+						},
+					},
+				},
+			},
+			expectedError: hookexecution.NewFailure("failed to process bapp block checking: failed to get override for bapp.enforce_blocks: bidders and media_types absent from conditions, at least one of the fields must be present"),
+		},
+		{
+			description: "Expect error if deal_ids not defined in config override conditions for bapp attribute. Analytics should have error status tag",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Bundle: "forbidden_bundle", ImpID: impID1, DealID: "acceptDealID"},
+				},
+			}},
+			config: json.RawMessage(`{"attributes": {"bapp": {"enforce_blocks": true, "action_overrides": {"allowed_app_for_deals": [{"conditions": {}}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Bundle: "forbidden_bundle", ImpID: impID1, DealID: "acceptDealID"},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusError,
+						},
+					},
+				},
+			},
+			expectedError: hookexecution.NewFailure("failed to process bapp block checking: failed to get override for bapp.allowed_app_for_deals: conditions field in account configuration must contain deal_ids"),
+		},
+		{
+			description: "Bid blocked by battr attribute check. Payload updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Attr: []adcom1.CreativeAttribute{1}, ImpID: impID1},
+				},
+				{
+					Bid: &openrtb2.Bid{ID: "2", Attr: []adcom1.CreativeAttribute{2}, ImpID: impID2},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"battr":{"enforce_blocks": true}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "2", Attr: []adcom1.CreativeAttribute{2}, ImpID: impID2},
+				},
+			},
+			moduleCtx: map[string]interface{}{bidder: blockingAttributes{bAttr: map[string][]int{impID1: {1}}}},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status: hookanalytics.ResultStatusBlock,
+									Values: map[string]interface{}{
+										attributesAnalyticKey: []string{"battr"},
+										battrAnalyticKey:      []int{1},
+									},
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+				DebugMessages: []string{"Bid 1 from bidder appnexus has been rejected, failed checks: battr"},
+			},
+		},
+		{
+			description: "Expect error if there is an issue processing enforce blocks overrides for battr attribute. Analytics should have error status tag",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Attr: []adcom1.CreativeAttribute{1}, ImpID: impID1},
+				},
+			}},
+			config: json.RawMessage(`{"attributes": {"battr": {"enforce_blocks": true, "action_overrides": {"enforce_blocks": [{"conditions": {}}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Attr: []adcom1.CreativeAttribute{1}, ImpID: impID1},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusError,
+						},
+					},
+				},
+			},
+			expectedError: hookexecution.NewFailure("failed to process battr block checking: failed to get override for battr.enforce_blocks: bidders and media_types absent from conditions, at least one of the fields must be present"),
+		},
+		{
+			description: "Expect error if deal_ids not defined in config override conditions for battr attribute. Analytics should have error status tag",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Attr: []adcom1.CreativeAttribute{1}, ImpID: impID1, DealID: "acceptDealID"},
+				},
+			}},
+			config: json.RawMessage(`{"attributes": {"battr": {"enforce_blocks": true, "action_overrides": {"allowed_banner_attr_for_deals": [{"conditions": {}}]}}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{ID: "1", Attr: []adcom1.CreativeAttribute{1}, ImpID: impID1, DealID: "acceptDealID"},
+				},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusError,
+						},
+					},
+				},
+			},
+			expectedError: hookexecution.NewFailure("failed to process battr block checking: failed to get override for battr.allowed_banner_attr_for_deals: conditions field in account configuration must contain deal_ids"),
+		},
+		{
+			description: "Bid blocked by multiple attribute check. Payload updated. Analytic tags successfully collected",
+			payload: hookstage.RawBidderResponsePayload{Bidder: bidder, Bids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{
+						ID:      "1",
+						ADomain: []string{"forbidden_domain"},
+						Cat:     []string{"fishing"},
+						CatTax:  1,
+						Bundle:  "forbidden_bundle",
+						Attr:    []adcom1.CreativeAttribute{1},
+						ImpID:   impID1,
+					},
+				},
+				{
+					Bid: &openrtb2.Bid{
+						ID:      "2",
+						ADomain: []string{"allowed_domain"},
+						Cat:     []string{"moto"},
+						CatTax:  2,
+						Bundle:  "allowed_bundle",
+						Attr:    []adcom1.CreativeAttribute{2},
+						ImpID:   impID2,
+					},
+				},
+			}},
+			config: json.RawMessage(`{"attributes":{"badv":{"enforce_blocks": true}, "bcat":{"enforce_blocks": true}, "cattax":{"enforce_blocks": true}, "bapp":{"enforce_blocks": true}, "battr":{"enforce_blocks": true}}}`),
+			expectedBids: []*adapters.TypedBid{
+				{
+					Bid: &openrtb2.Bid{
+						ID:      "2",
+						ADomain: []string{"allowed_domain"},
+						Cat:     []string{"moto"},
+						CatTax:  2,
+						Bundle:  "allowed_bundle",
+						Attr:    []adcom1.CreativeAttribute{2},
+						ImpID:   impID2,
+					},
+				},
+			},
+			moduleCtx: map[string]interface{}{bidder: blockingAttributes{
+				bAdv:   []string{"forbidden_domain"},
+				bCat:   []string{"fishing"},
+				catTax: 2,
+				bApp:   []string{"forbidden_bundle"},
+				bAttr:  map[string][]int{impID1: {1}}},
+			},
+			expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
+				AnalyticsTags: hookanalytics.Analytics{
+					Activities: []hookanalytics.Activity{
+						{
+							Name:   enforceBlockingTag,
+							Status: hookanalytics.ActivityStatusSuccess,
+							Results: []hookanalytics.Result{
+								{
+									Status: hookanalytics.ResultStatusBlock,
+									Values: map[string]interface{}{
+										attributesAnalyticKey: []string{"badv", "bcat", "cattax", "bapp", "battr"},
+										badvAnalyticKey:       []string{"forbidden_domain"},
+										cattaxAnalyticKey:     []adcom1.CategoryTaxonomy{1},
+										bappAnalyticKey:       []string{"forbidden_bundle"},
+										battrAnalyticKey:      []int{1},
+									},
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID1}},
+								},
+								{
+									Status:    hookanalytics.ResultStatusAllow,
+									AppliedTo: hookanalytics.AppliedTo{Bidder: bidder, ImpIds: []string{impID2}},
+								},
+							},
+						},
+					},
+				},
+				DebugMessages: []string{"Bid 1 from bidder appnexus has been rejected, failed checks: badv, bcat, cattax, bapp, battr"},
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.description, func(t *testing.T) {
+			result, err := Builder(nil, moduledeps.ModuleDeps{})
+			assert.NoError(t, err, "Failed to build module.")
+
+			module, ok := result.(Module)
+			assert.True(t, ok, "Failed to cast module type.")
+
+			hookResult, err := module.HandleRawBidderResponseHook(
+				context.Background(),
+				hookstage.ModuleInvocationContext{
+					AccountConfig: test.config,
+					Endpoint:      hookexecution.EndpointAuction,
+					ModuleContext: test.moduleCtx,
+				},
+				test.payload,
+			)
+			assert.Equal(t, test.expectedError, err, "Invalid hook execution error.")
 
-func toInt[T numeric](values []T) []int {
-	ints := make([]int, len(values))
-	for i := range values {
-		ints[i] = int(values[i])
+			// test mutations separately
+			for _, mut := range hookResult.ChangeSet.Mutations() {
+				newPayload, err := mut.Apply(test.payload)
+				assert.NoError(t, err)
+				test.payload = newPayload
+			}
+			assert.Equal(t, test.expectedBids, test.payload.Bids, "Invalid Bids returned after executing RawBidderResponse hook.")
+
+			// reset ChangeSet not to break hookResult assertion, we validated ChangeSet separately
+			hookResult.ChangeSet = hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{}
+			assert.Equal(t, test.expectedHookResult, hookResult, "Invalid hook execution result.")
+		})
 	}
-	return ints
 }