Repository: prebid/prebid-server
Pull request #2129 (2022-01-10 05:35:49)

PBS Version In Response Header
Implements https://github.com/prebid/prebid-server/issues/2010


Patch:
diff --git a/adapters/adagio/adagio.go b/adapters/adagio/adagio.go
deleted file mode 100644
index 0da4d6ac..00000000
--- a/adapters/adagio/adagio.go
+++ /dev/null
@@ -1,139 +0,0 @@
-package adagio
-
-import (
-	"bytes"
-	"compress/gzip"
-	"encoding/json"
-	"fmt"
-	"github.com/mxmCherry/openrtb/v15/openrtb2"
-	"github.com/prebid/prebid-server/adapters"
-	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/errortypes"
-	"github.com/prebid/prebid-server/openrtb_ext"
-	"net/http"
-)
-
-// Builder builds a new instance of the Adagio adapter for the given bidder with the given config.
-func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
-	bidder := &adapter{
-		endpoint: config.Endpoint,
-	}
-	return bidder, nil
-}
-
-type adapter struct {
-	endpoint string
-}
-
-type extBid struct {
-	Prebid *openrtb_ext.ExtBidPrebid
-}
-
-// MakeRequests prepares the HTTP requests which should be made to fetch bids.
-func (a *adapter) MakeRequests(request *openrtb2.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
-	json, err := json.Marshal(request)
-	if err != nil {
-		return nil, []error{err}
-	}
-
-	headers := http.Header{}
-	headers.Add("Content-Type", "application/json;charset=utf-8")
-	headers.Add("Accept", "application/json")
-
-	if request.Device != nil {
-		if len(request.Device.IPv6) > 0 {
-			headers.Add("X-Forwarded-For", request.Device.IPv6)
-		}
-		if len(request.Device.IP) > 0 {
-			headers.Add("X-Forwarded-For", request.Device.IP)
-		}
-	}
-
-	if request.Test == 0 {
-		// Gzip the body
-		// Note: Gzipping could be handled natively later: https://github.com/prebid/prebid-server/issues/1812
-		var bodyBuf bytes.Buffer
-		gz := gzip.NewWriter(&bodyBuf)
-		if _, err = gz.Write(json); err == nil {
-			if err = gz.Close(); err == nil {
-				json = bodyBuf.Bytes()
-				headers.Add("Content-Encoding", "gzip")
-				// /!\ Go already sets the `Accept-Encoding: gzip` header. Never add it manually, or Go won't decompress the response.
-				//headers.Add("Accept-Encoding", "gzip")
-			}
-		}
-	}
-
-	requestToBidder := &adapters.RequestData{
-		Method:  "POST",
-		Uri:     a.endpoint,
-		Body:    json,
-		Headers: headers,
-	}
-
-	return []*adapters.RequestData{requestToBidder}, nil
-}
-
-const unexpectedStatusCodeFormat = "Unexpected status code: %d. Run with request.debug = 1 for more info"
-
-// MakeBids unpacks the server's response into Bids.
-func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, _ *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
-	switch response.StatusCode {
-	case http.StatusOK:
-		break
-	case http.StatusNoContent:
-		return nil, nil
-	case http.StatusServiceUnavailable:
-		fallthrough
-	case http.StatusBadRequest:
-		fallthrough
-	case http.StatusUnauthorized:
-		fallthrough
-	case http.StatusForbidden:
-		err := &errortypes.BadInput{
-			Message: fmt.Sprintf(unexpectedStatusCodeFormat, response.StatusCode),
-		}
-		return nil, []error{err}
-	default:
-		err := &errortypes.BadServerResponse{
-			Message: fmt.Sprintf(unexpectedStatusCodeFormat, response.StatusCode),
-		}
-		return nil, []error{err}
-	}
-
-	var openRTBBidderResponse openrtb2.BidResponse
-	if err := json.Unmarshal(response.Body, &openRTBBidderResponse); err != nil {
-		return nil, []error{err}
-	}
-
-	bidsCapacity := len(internalRequest.Imp)
-	errs := make([]error, 0, bidsCapacity)
-	bidderResponse := adapters.NewBidderResponseWithBidsCapacity(bidsCapacity)
-	var typedBid *adapters.TypedBid
-	for _, seatBid := range openRTBBidderResponse.SeatBid {
-		for _, bid := range seatBid.Bid {
-			activeBid := bid
-
-			activeExt := &extBid{}
-			if err := json.Unmarshal(activeBid.Ext, activeExt); err != nil {
-				errs = append(errs, err)
-			}
-
-			var bidType openrtb_ext.BidType
-			if activeExt.Prebid != nil && activeExt.Prebid.Type != "" {
-				bidType = activeExt.Prebid.Type
-			} else {
-				err := &errortypes.BadServerResponse{
-					Message: fmt.Sprintf("Failed to find native/banner/video mediaType \"%s\" ", activeBid.ImpID),
-				}
-				errs = append(errs, err)
-				continue
-			}
-
-			typedBid = &adapters.TypedBid{Bid: &activeBid, BidType: bidType}
-			bidderResponse.Bids = append(bidderResponse.Bids, typedBid)
-		}
-	}
-
-	return bidderResponse, nil
-}
diff --git a/adapters/adyoulike/adyoulike.go b/adapters/adyoulike/adyoulike.go
index 4cccd5dc..17c42e4a 100644
--- a/adapters/adyoulike/adyoulike.go
+++ b/adapters/adyoulike/adyoulike.go
@@ -2,16 +2,15 @@ package adyoulike
 
 import (
 	"encoding/json"
-	"errors"
 	"fmt"
-	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/openrtb_ext"
 	"net/http"
 
 	"github.com/buger/jsonparser"
 	"github.com/mxmCherry/openrtb/v15/openrtb2"
 	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
@@ -88,7 +87,7 @@ func (a *adapter) MakeBids(
 	case http.StatusOK:
 		break
 	case http.StatusNoContent:
-		return nil, []error{errors.New("MakeBids error: No Content")}
+		return nil, nil
 	case http.StatusBadRequest:
 		err := &errortypes.BadInput{
 			Message: fmt.Sprintf(unexpectedStatusCodeFormat, bidderRawResponse.StatusCode),
diff --git a/adapters/algorix/algorix.go b/adapters/algorix/algorix.go
index 628207d6..d24521b4 100644
--- a/adapters/algorix/algorix.go
+++ b/adapters/algorix/algorix.go
@@ -19,6 +19,14 @@ type adapter struct {
 	EndpointTemplate *template.Template
 }
 
+type algorixVideoExt struct {
+	Rewarded int `json:"rewarded"`
+}
+
+type algorixResponseBidExt struct {
+	MediaType string `json:"mediaType"`
+}
+
 // Builder builds a new instance of the AlgoriX adapter for the given bidder with the given config.
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
 	endpoint, err := template.New("endpointTemplate").Parse(config.Endpoint)
@@ -121,6 +129,19 @@ func preProcess(request *openrtb2.BidRequest) {
 				request.Imp[i].Banner = &banner
 			}
 		}
+		if request.Imp[i].Video != nil {
+			var impExt adapters.ExtImpBidder
+			err := json.Unmarshal(request.Imp[i].Ext, &impExt)
+			if err != nil {
+				continue
+			}
+			if impExt.Prebid != nil && impExt.Prebid.IsRewardedInventory == 1 {
+				videoCopy := *request.Imp[i].Video
+				videoExt := algorixVideoExt{Rewarded: 1}
+				videoCopy.Ext, err = json.Marshal(&videoExt)
+				request.Imp[i].Video = &videoCopy
+			}
+		}
 	}
 }
 
@@ -147,32 +168,58 @@ func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest
 	}
 
 	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
+	var errs []error
 
 	for _, seatBid := range bidResp.SeatBid {
 		for idx := range seatBid.Bid {
-			mediaType := getBidType(seatBid.Bid[idx].ImpID, internalRequest.Imp)
-			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
-				Bid:     &seatBid.Bid[idx],
-				BidType: mediaType,
-			})
+			mediaType, err := getBidType(seatBid.Bid[idx], internalRequest.Imp)
+			if err != nil {
+				errs = append(errs, err)
+			} else {
+				bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+					Bid:     &seatBid.Bid[idx],
+					BidType: mediaType,
+				})
+			}
 		}
 	}
-	return bidResponse, nil
+
+	return bidResponse, errs
 }
 
-func getBidType(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
+func getBidType(bid openrtb2.Bid, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
+	var bidExt algorixResponseBidExt
+	err := json.Unmarshal(bid.Ext, &bidExt)
+	if err == nil {
+		switch bidExt.MediaType {
+		case "banner":
+			return openrtb_ext.BidTypeBanner, nil
+		case "native":
+			return openrtb_ext.BidTypeNative, nil
+		case "video":
+			return openrtb_ext.BidTypeVideo, nil
+		}
+	}
+	var mediaType openrtb_ext.BidType
+	var typeCnt = 0
 	for _, imp := range imps {
-		if imp.ID == impId {
+		if imp.ID == bid.ImpID {
 			if imp.Banner != nil {
-				break
+				typeCnt += 1
+				mediaType = openrtb_ext.BidTypeBanner
 			}
 			if imp.Native != nil {
-				return openrtb_ext.BidTypeNative
+				typeCnt += 1
+				mediaType = openrtb_ext.BidTypeNative
 			}
 			if imp.Video != nil {
-				return openrtb_ext.BidTypeVideo
+				typeCnt += 1
+				mediaType = openrtb_ext.BidTypeVideo
 			}
 		}
 	}
-	return openrtb_ext.BidTypeBanner
+	if typeCnt == 1 {
+		return mediaType, nil
+	}
+	return mediaType, fmt.Errorf("unable to fetch mediaType in multi-format: %s", bid.ImpID)
 }
diff --git a/adapters/vidoomy/vidoomy.go b/adapters/vidoomy/vidoomy.go
new file mode 100644
index 00000000..57fbb09d
--- /dev/null
+++ b/adapters/vidoomy/vidoomy.go
@@ -0,0 +1,173 @@
+package vidoomy
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"net/http"
+)
+
+type adapter struct {
+	endpoint string
+}
+
+func (a adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var errors []error
+
+	reqs := make([]*adapters.RequestData, 0, len(request.Imp))
+
+	header := getHeaders(request)
+	for _, imp := range request.Imp {
+
+		// Split up multi-impression requests into multiple requests so that
+		// each split request is only associated to a single impression
+		reqCopy := *request
+		reqCopy.Imp = []openrtb2.Imp{imp}
+
+		if err := changeRequestForBidService(&reqCopy); err != nil {
+			errors = append(errors, err)
+			continue
+		}
+
+		reqJSON, err := json.Marshal(&reqCopy)
+		if err != nil {
+			errors = append(errors, err)
+			continue
+		}
+
+		reqs = append(reqs, &adapters.RequestData{
+			Method:  "POST",
+			Uri:     a.endpoint,
+			Body:    reqJSON,
+			Headers: header,
+		})
+	}
+
+	return reqs, errors
+}
+
+func getHeaders(request *openrtb2.BidRequest) http.Header {
+	headers := http.Header{}
+
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	headers.Add("x-openrtb-version", "2.5")
+
+	if request.Device == nil {
+		return headers
+	}
+
+	if request.Device.UA != "" {
+		headers.Set("User-Agent", request.Device.UA)
+	}
+
+	if len(request.Device.IPv6) > 0 {
+		headers.Add("X-Forwarded-For", request.Device.IPv6)
+	}
+
+	if len(request.Device.IP) > 0 {
+		headers.Add("X-Forwarded-For", request.Device.IP)
+	}
+
+	return headers
+}
+
+func changeRequestForBidService(request *openrtb2.BidRequest) error {
+	if request.Imp[0].Banner == nil {
+		return nil
+	}
+
+	banner := *request.Imp[0].Banner
+	request.Imp[0].Banner = &banner
+
+	if banner.W != nil && banner.H != nil {
+		if *banner.W == 0 || *banner.H == 0 {
+			return fmt.Errorf("invalid sizes provided for Banner %d x %d", *banner.W, *banner.H)
+		}
+		return nil
+	}
+
+	if len(banner.Format) == 0 {
+		return fmt.Errorf("no sizes provided for Banner %v", banner.Format)
+	}
+
+	banner.W = openrtb2.Int64Ptr(banner.Format[0].W)
+	banner.H = openrtb2.Int64Ptr(banner.Format[0].H)
+
+	return nil
+}
+
+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.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode),
+		}}
+	}
+
+	var bidResp openrtb2.BidResponse
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Bad server response: %d.", err),
+		}}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(internalRequest.Imp))
+
+	for _, sb := range bidResp.SeatBid {
+		for _, bid := range sb.Bid {
+			bid := bid
+			exists, mediaType := getImpInfo(bid.ImpID, internalRequest.Imp)
+			if !exists {
+				return nil, []error{&errortypes.BadServerResponse{
+					Message: fmt.Sprintf("Unknown ad unit code '%s'", bid.ImpID),
+				}}
+			}
+
+			if openrtb_ext.BidTypeBanner != mediaType &&
+				openrtb_ext.BidTypeVideo != mediaType {
+				//only banner and video are supported, anything else is ignored
+				continue
+			}
+
+			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+				Bid:     &bid,
+				BidType: mediaType,
+			})
+		}
+	}
+
+	return bidResponse, nil
+}
+
+func getImpInfo(impId string, imps []openrtb2.Imp) (bool, openrtb_ext.BidType) {
+	var mediaType openrtb_ext.BidType
+	for _, imp := range imps {
+		if imp.ID == impId {
+
+			if imp.Video != nil {
+				mediaType = openrtb_ext.BidTypeVideo
+			} else if imp.Banner != nil {
+				mediaType = openrtb_ext.BidTypeBanner
+			}
+
+			return true, mediaType
+		}
+	}
+	return false, mediaType
+}
+
+// Builder builds a new instance of the Vidoomy adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+	return bidder, nil
+}
diff --git a/config/config.go b/config/config.go
index 1b415fdf..e7a9effa 100644
--- a/config/config.go
+++ b/config/config.go
@@ -288,7 +288,7 @@ type TCF2 struct {
 	Purpose8            TCF2Purpose             `mapstructure:"purpose8"`
 	Purpose9            TCF2Purpose             `mapstructure:"purpose9"`
 	Purpose10           TCF2Purpose             `mapstructure:"purpose10"`
-	SpecialPurpose1     TCF2Purpose             `mapstructure:"special_purpose1"`
+	SpecialFeature1     TCF2SpecialFeature      `mapstructure:"special_feature1"`
 	PurposeOneTreatment TCF2PurposeOneTreatment `mapstructure:"purpose_one_treatment"`
 }
 
@@ -302,6 +302,13 @@ type TCF2Purpose struct {
 	VendorExceptionMap map[openrtb_ext.BidderName]struct{}
 }
 
+type TCF2SpecialFeature struct {
+	Enforce bool `mapstructure:"enforce"`
+	// Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed
+	VendorExceptions   []openrtb_ext.BidderName `mapstructure:"vendor_exceptions"`
+	VendorExceptionMap map[openrtb_ext.BidderName]struct{}
+}
+
 type TCF2PurposeOneTreatment struct {
 	Enabled       bool `mapstructure:"enabled"`
 	AccessAllowed bool `mapstructure:"access_allowed"`
@@ -557,7 +564,6 @@ func New(v *viper.Viper) (*Configuration, error) {
 		&c.GDPR.TCF2.Purpose8,
 		&c.GDPR.TCF2.Purpose9,
 		&c.GDPR.TCF2.Purpose10,
-		&c.GDPR.TCF2.SpecialPurpose1,
 	}
 	for c := 0; c < len(purposeConfigs); c++ {
 		purposeConfigs[c].VendorExceptionMap = make(map[openrtb_ext.BidderName]struct{})
@@ -568,6 +574,14 @@ func New(v *viper.Viper) (*Configuration, error) {
 		}
 	}
 
+	// To look for a special feature's vendor exceptions in O(1) time, we fill this hash table with bidders located in the
+	// VendorExceptions field of the GDPR.TCF2.SpecialFeature1 struct defined in this file
+	c.GDPR.TCF2.SpecialFeature1.VendorExceptionMap = make(map[openrtb_ext.BidderName]struct{})
+	for v := 0; v < len(c.GDPR.TCF2.SpecialFeature1.VendorExceptions); v++ {
+		bidderName := c.GDPR.TCF2.SpecialFeature1.VendorExceptions[v]
+		c.GDPR.TCF2.SpecialFeature1.VendorExceptionMap[bidderName] = struct{}{}
+	}
+
 	// To look for a request's app_id in O(1) time, we fill this hash table located in the
 	// the BlacklistedApps field of the Configuration struct defined in this file
 	c.BlacklistedAppMap = make(map[string]bool)
@@ -793,7 +807,6 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.33across.partner_id", "")
 	v.SetDefault("adapters.aceex.endpoint", "http://bl-us.aceex.io/?uqhash={{.AccountID}}")
 	v.SetDefault("adapters.acuityads.endpoint", "http://{{.Host}}.admanmedia.com/bid?token={{.AccountID}}")
-	v.SetDefault("adapters.adagio.endpoint", "https://mp.4dex.io/ortb2")
 	v.SetDefault("adapters.adf.endpoint", "https://adx.adform.net/adx/openrtb")
 	v.SetDefault("adapters.adform.endpoint", "https://adx.adform.net/adx/openrtb")
 	v.SetDefault("adapters.adgeneration.endpoint", "https://d.socdm.com/adsv/v1")
@@ -929,6 +942,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.valueimpression.endpoint", "http://useast.quantumdex.io/auction/pbs")
 	v.SetDefault("adapters.verizonmedia.disabled", true)
 	v.SetDefault("adapters.videobyte.endpoint", "https://x.videobyte.com/ortbhb")
+	v.SetDefault("adapters.vidoomy.endpoint", "https://p.vidoomy.com/api/rtbserver/pbs")
 	v.SetDefault("adapters.viewdeos.endpoint", "http://ghb.sync.viewdeos.com/pbs/ortb")
 	v.SetDefault("adapters.visx.endpoint", "https://t.visx.net/s2s_bid?wrapperType=s2s_prebid_standard:0.1.0")
 	v.SetDefault("adapters.vrtcal.endpoint", "http://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804")
@@ -977,8 +991,6 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("gdpr.tcf2.purpose8.vendor_exceptions", []openrtb_ext.BidderName{})
 	v.SetDefault("gdpr.tcf2.purpose9.vendor_exceptions", []openrtb_ext.BidderName{})
 	v.SetDefault("gdpr.tcf2.purpose10.vendor_exceptions", []openrtb_ext.BidderName{})
-	v.SetDefault("gdpr.tcf2.special_purpose1.enabled", true)
-	v.SetDefault("gdpr.tcf2.special_purpose1.vendor_exceptions", []openrtb_ext.BidderName{})
 	v.SetDefault("gdpr.amp_exception", false)
 	v.SetDefault("gdpr.eea_countries", []string{"ALA", "AUT", "BEL", "BGR", "HRV", "CYP", "CZE", "DNK", "EST",
 		"FIN", "FRA", "GUF", "DEU", "GIB", "GRC", "GLP", "GGY", "HUN", "ISL", "IRL", "IMN", "ITA", "JEY", "LVA",
@@ -1035,6 +1047,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	// Migrate config settings to maintain compatibility with old configs
 	migrateConfig(v)
 	migrateConfigPurposeOneTreatment(v)
+	migrateConfigSpecialFeature1(v)
 	migrateConfigTCF2PurposeEnabledFlags(v)
 
 	// These defaults must be set after the migrate functions because those functions look for the presence of these
@@ -1062,6 +1075,8 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("gdpr.tcf2.purpose10.enforce_purpose", TCF2FullEnforcement)
 	v.SetDefault("gdpr.tcf2.purpose_one_treatment.enabled", true)
 	v.SetDefault("gdpr.tcf2.purpose_one_treatment.access_allowed", true)
+	v.SetDefault("gdpr.tcf2.special_feature1.enforce", true)
+	v.SetDefault("gdpr.tcf2.special_feature1.vendor_exceptions", []openrtb_ext.BidderName{})
 }
 
 func migrateConfig(v *viper.Viper) {
@@ -1089,6 +1104,19 @@ func migrateConfigPurposeOneTreatment(v *viper.Viper) {
 	}
 }
 
+func migrateConfigSpecialFeature1(v *viper.Viper) {
+	if oldConfig, ok := v.Get("gdpr.tcf2.special_purpose1").(map[string]interface{}); ok {
+		if v.IsSet("gdpr.tcf2.special_feature1") {
+			glog.Warning("using gdpr.tcf2.special_feature1 and ignoring deprecated gdpr.tcf2.special_purpose1")
+		} else {
+			glog.Warning("gdpr.tcf2.special_purpose1.enabled is deprecated and should be changed to gdpr.tcf2.special_feature1.enforce")
+			glog.Warning("gdpr.tcf2.special_purpose1.vendor_exceptions is deprecated and should be changed to gdpr.tcf2.special_feature1.vendor_exceptions")
+			v.Set("gdpr.tcf2.special_feature1.enforce", oldConfig["enabled"])
+			v.Set("gdpr.tcf2.special_feature1.vendor_exceptions", oldConfig["vendor_exceptions"])
+		}
+	}
+}
+
 func migrateConfigTCF2PurposeEnabledFlags(v *viper.Viper) {
 	for i := 1; i <= 10; i++ {
 		oldField := fmt.Sprintf("gdpr.tcf2.purpose%d.enabled", i)
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 38b27970..452e7ed5 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -26,6 +26,7 @@ import (
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
 	"github.com/prebid/prebid-server/usersync"
 	"github.com/prebid/prebid-server/util/iputil"
+	"github.com/prebid/prebid-server/version"
 
 	"github.com/buger/jsonparser"
 	"github.com/golang/glog"
@@ -133,6 +134,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 	// and "Access-Control-Allow-Credentials" are handled in CORS middleware
 	w.Header().Set("AMP-Access-Control-Allow-Source-Origin", origin)
 	w.Header().Set("Access-Control-Expose-Headers", "AMP-Access-Control-Allow-Source-Origin")
+	w.Header().Set("X-Prebid", version.BuildXPrebidHeader(version.Ver))
 
 	req, errL := deps.parseAmpRequest(r)
 	ao.Errors = append(ao.Errors, errL...)
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 4d68db50..3f0a35e8 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"github.com/prebid/prebid-server/firstpartydata"
+	"github.com/prebid/prebid-server/version"
 
 	"github.com/buger/jsonparser"
 	jsonpatch "github.com/evanphx/json-patch"
@@ -148,6 +149,8 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 		deps.analytics.LogAuctionObject(&ao)
 	}()
 
+	w.Header().Set("X-Prebid", version.BuildXPrebidHeader(version.Ver))
+
 	req, impExtInfoMap, errL := deps.parseRequest(r)
 	if errortypes.ContainsFatalError(errL) && writeError(errL, w, &labels) {
 		return
@@ -234,7 +237,6 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 	enc := json.NewEncoder(w)
 	enc.SetEscapeHTML(false)
 
-	// Fixes #328
 	w.Header().Set("Content-Type", "application/json")
 
 	// If an error happens when encoding the response, there isn't much we can do.
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index cbffe2e3..1f16f92a 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -22,6 +22,7 @@ import (
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/util/iputil"
 	"github.com/prebid/prebid-server/util/uuidutil"
+	"github.com/prebid/prebid-server/version"
 
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
@@ -153,6 +154,8 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		deps.analytics.LogVideoObject(&vo)
 	}()
 
+	w.Header().Set("X-Prebid", version.BuildXPrebidHeader(version.Ver))
+
 	lr := &io.LimitedReader{
 		R: r.Body,
 		N: deps.cfg.MaxRequestSize,
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 8075b7f1..850b41ab 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -5,7 +5,6 @@ import (
 	"github.com/prebid/prebid-server/adapters/33across"
 	"github.com/prebid/prebid-server/adapters/aceex"
 	"github.com/prebid/prebid-server/adapters/acuityads"
-	"github.com/prebid/prebid-server/adapters/adagio"
 	"github.com/prebid/prebid-server/adapters/adf"
 	"github.com/prebid/prebid-server/adapters/adgeneration"
 	"github.com/prebid/prebid-server/adapters/adhese"
@@ -125,6 +124,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/unicorn"
 	"github.com/prebid/prebid-server/adapters/unruly"
 	"github.com/prebid/prebid-server/adapters/videobyte"
+	"github.com/prebid/prebid-server/adapters/vidoomy"
 	"github.com/prebid/prebid-server/adapters/visx"
 	"github.com/prebid/prebid-server/adapters/vrtcal"
 	"github.com/prebid/prebid-server/adapters/yahoossp"
@@ -144,7 +144,6 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.Bidder33Across:          ttx.Builder,
 		openrtb_ext.BidderAceex:             aceex.Builder,
 		openrtb_ext.BidderAcuityAds:         acuityads.Builder,
-		openrtb_ext.BidderAdagio:            adagio.Builder,
 		openrtb_ext.BidderAdf:               adf.Builder,
 		openrtb_ext.BidderAdform:            adf.Builder,
 		openrtb_ext.BidderAdgeneration:      adgeneration.Builder,
@@ -272,6 +271,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderValueImpression:   apacdex.Builder,
 		openrtb_ext.BidderVerizonMedia:      yahoossp.Builder,
 		openrtb_ext.BidderVideoByte:         videobyte.Builder,
+		openrtb_ext.BidderVidoomy:           vidoomy.Builder,
 		openrtb_ext.BidderViewdeos:          adtelligent.Builder,
 		openrtb_ext.BidderVisx:              visx.Builder,
 		openrtb_ext.BidderVrtcal:            vrtcal.Builder,
diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go
index 9ab27561..50308dae 100644
--- a/exchange/adapter_util.go
+++ b/exchange/adapter_util.go
@@ -79,6 +79,7 @@ func GetActiveBidders(infos config.BidderInfos) map[string]openrtb_ext.BidderNam
 func GetDisabledBiddersErrorMessages(infos config.BidderInfos) map[string]string {
 	disabledBidders := map[string]string{
 		"lifestreet": `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`,
+		"adagio":     `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`,
 	}
 
 	for name, info := range infos {
diff --git a/exchange/bidder.go b/exchange/bidder.go
index 2aae63e2..40e78e83 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -140,7 +140,7 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb2.B
 		}
 		return nil, errs
 	}
-	xPrebidHeader := buildXPrebidHeader(request, version.Ver)
+	xPrebidHeader := version.BuildXPrebidHeaderForRequest(request, version.Ver)
 
 	for i := 0; i < len(reqData); i++ {
 		if reqData[i].Headers != nil {
diff --git a/exchange/utils.go b/exchange/utils.go
index ec5b666d..44737af3 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -5,7 +5,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"math/rand"
-	"strings"
 
 	"github.com/mxmCherry/openrtb/v15/openrtb2"
 	"github.com/prebid/go-gdpr/vendorconsent"
@@ -713,42 +712,6 @@ func getExtBidAdjustmentFactors(requestExt *openrtb_ext.ExtRequest) map[string]f
 	return bidAdjustmentFactors
 }
 
-func buildXPrebidHeader(bidRequest *openrtb2.BidRequest, version string) string {
-	req := &openrtb_ext.RequestWrapper{BidRequest: bidRequest}
-
-	sb := &strings.Builder{}
-	writeNameVersionRecord(sb, "pbs-go", version)
-
-	if reqExt, err := req.GetRequestExt(); err == nil && reqExt != nil {
-		if prebidExt := reqExt.GetPrebid(); prebidExt != nil {
-			if channel := prebidExt.Channel; channel != nil {
-				writeNameVersionRecord(sb, channel.Name, channel.Version)
-			}
-		}
-	}
-	if appExt, err := req.GetAppExt(); err == nil && appExt != nil {
-		if prebidExt := appExt.GetPrebid(); prebidExt != nil {
-			writeNameVersionRecord(sb, prebidExt.Source, prebidExt.Version)
-		}
-	}
-	return sb.String()
-}
-
-func writeNameVersionRecord(sb *strings.Builder, name, version string) {
-	if name == "" {
-		return
-	}
-	if version == "" {
-		version = "unknown"
-	}
-	if sb.Len() != 0 {
-		sb.WriteString(",")
-	}
-	sb.WriteString(name)
-	sb.WriteString("/")
-	sb.WriteString(version)
-}
-
 func applyFPD(fpd *firstpartydata.ResolvedFirstPartyData, bidReq *openrtb2.BidRequest) {
 	if fpd.Site != nil {
 		bidReq.Site = fpd.Site
diff --git a/gdpr/impl.go b/gdpr/impl.go
index 380665c4..c85c9d28 100644
--- a/gdpr/impl.go
+++ b/gdpr/impl.go
@@ -132,9 +132,9 @@ func (p *permissionsImpl) allowActivities(ctx context.Context, vendorID uint16,
 		return
 	}
 
-	if p.cfg.TCF2.SpecialPurpose1.Enabled {
-		vendorException := p.isSpecialPurposeVendorException(bidder)
-		passGeo = vendorException || (consentMeta.SpecialFeatureOptIn(1) && (vendor.SpecialPurpose(1) || weakVendorEnforcement))
+	if p.cfg.TCF2.SpecialFeature1.Enforce {
+		vendorException := p.isSpecialFeatureVendorException(bidder)
+		passGeo = vendorException || (consentMeta.SpecialFeatureOptIn(1) && (vendor.SpecialFeature(1) || weakVendorEnforcement))
 	} else {
 		passGeo = true
 	}
@@ -162,8 +162,8 @@ func (p *permissionsImpl) isVendorException(purpose consentconstants.Purpose, bi
 	return
 }
 
-func (p *permissionsImpl) isSpecialPurposeVendorException(bidder openrtb_ext.BidderName) (vendorException bool) {
-	if _, ok := p.cfg.TCF2.SpecialPurpose1.VendorExceptionMap[bidder]; ok {
+func (p *permissionsImpl) isSpecialFeatureVendorException(bidder openrtb_ext.BidderName) (vendorException bool) {
+	if _, ok := p.cfg.TCF2.SpecialFeature1.VendorExceptionMap[bidder]; ok {
 		vendorException = true
 	}
 	return
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 941f1a56..7cb7a07b 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -79,7 +79,6 @@ const (
 	Bidder33Across          BidderName = "33across"
 	BidderAceex             BidderName = "aceex"
 	BidderAcuityAds         BidderName = "acuityads"
-	BidderAdagio            BidderName = "adagio"
 	BidderAdf               BidderName = "adf"
 	BidderAdform            BidderName = "adform"
 	BidderAdgeneration      BidderName = "adgeneration"
@@ -207,6 +206,7 @@ const (
 	BidderValueImpression   BidderName = "valueimpression"
 	BidderVerizonMedia      BidderName = "verizonmedia"
 	BidderVideoByte         BidderName = "videobyte"
+	BidderVidoomy           BidderName = "vidoomy"
 	BidderVisx              BidderName = "visx"
 	BidderViewdeos          BidderName = "viewdeos"
 	BidderVrtcal            BidderName = "vrtcal"
@@ -225,7 +225,6 @@ func CoreBidderNames() []BidderName {
 		Bidder33Across,
 		BidderAceex,
 		BidderAcuityAds,
-		BidderAdagio,
 		BidderAdf,
 		BidderAdform,
 		BidderAdgeneration,
@@ -353,6 +352,7 @@ func CoreBidderNames() []BidderName {
 		BidderValueImpression,
 		BidderVerizonMedia,
 		BidderVideoByte,
+		BidderVidoomy,
 		BidderViewdeos,
 		BidderVisx,
 		BidderVrtcal,
diff --git a/openrtb_ext/imp_vidoomy.go b/openrtb_ext/imp_vidoomy.go
new file mode 100644
index 00000000..21072dae
--- /dev/null
+++ b/openrtb_ext/imp_vidoomy.go
@@ -0,0 +1,5 @@
+package openrtb_ext
+
+type ImpExtVidoomy struct {
+	ZoneID string `json:"zoneId"`
+}
diff --git a/static/bidder-info/adagio.yaml b/static/bidder-info/adagio.yaml
deleted file mode 100644
index 891b2a6c..00000000
--- a/static/bidder-info/adagio.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-maintainer:
-  email: "dev@adagio.io"
-gvlVendorID: 617
-modifyingVastXmlAllowed: false
-debug:
-  allow: true
-capabilities:
-  site:
-    mediaTypes:
-      - banner
-      - video
-      - native
-userSync:
-  redirect:
-    url: "https://mp.4dex.io/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}"
-    userMacro: "{{UID}}"
diff --git a/static/bidder-info/vidoomy.yaml b/static/bidder-info/vidoomy.yaml
new file mode 100644
index 00000000..1cd683a7
--- /dev/null
+++ b/static/bidder-info/vidoomy.yaml
@@ -0,0 +1,16 @@
+maintainer:
+  email: support@vidoomy.com
+gvlVendorID: 380
+capabilities:
+  site:
+    mediaTypes:
+      - banner
+      - video
+  app:
+    mediaTypes:
+      - banner
+      - video
+userSync:
+    iframe:
+      url: "https://vid.vidoomy.com/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}"
+      userMacro: "{{VID}}"
\ No newline at end of file
diff --git a/static/bidder-params/adagio.json b/static/bidder-params/adagio.json
deleted file mode 100644
index 955c58c7..00000000
--- a/static/bidder-params/adagio.json
+++ /dev/null
@@ -1,94 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-04/schema#",
-  "title": "Adagio Adapter Params",
-  "description": "A schema which validates params accepted by the Adagio adapter",
-  "type": "object",
-  "required": [
-    "organizationId",
-    "site",
-    "placement"
-  ],
-  "properties": {
-    "organizationId": {
-      "type": "string",
-      "description": "Name to identify the organization",
-      "minLength": 1
-    },
-    "site": {
-      "type": "string",
-      "description": "Name to identify the site",
-      "minLength": 1
-    },
-    "placement": {
-      "type": "string",
-      "description": "Name to identify the placement",
-      "minLength": 1
-    },
-    "pageviewId": {
-      "type": "string",
-      "description": "Name to identify the pageview"
-    },
-    "pagetype": {
-      "type": "string",
-      "description": "Name to identify the page type"
-    },
-    "category": {
-      "type": "string",
-      "description": "Name to identify the category"
-    },
-    "subcategory": {
-      "type": "string",
-      "description": "Name to identify the subcategory"
-    },
-    "environment": {
-      "type": "string",
-      "description": "Name to identify the environment"
-    },
-    "features": {
-      "type": "object",
-      "patternProperties": {
-        "^[a-zA-Z_]": { "type": "string" }
-      }
-    },
-    "prebidVersion:": {
-      "type": "string",
-      "description": "Name to identify the version of Prebid.js"
-    },
-    "debug": {
-      "type": "object",
-      "properties": {
-        "enabled": {
-          "type": "boolean"
-        },
-        "cpm": {
-          "type": "number"
-        },
-        "lazyLoad": {
-          "type": "object",
-          "properties": {
-            "enabled": {
-              "type": "boolean"
-            },
-            "threshold": {
-              "type": "number"
-            },
-            "rootMargin":  {
-              "type": "string"
-            }
-          }
-        }
-      }
-    },
-    "native": {
-      "type": "object",
-      "properties": {
-        "context": {
-          "type": "number"
-        },
-        "plcmttype": {
-          "type": "number"
-        }
-      }
-    }
-  }
-}
diff --git a/static/bidder-params/vidoomy.json b/static/bidder-params/vidoomy.json
new file mode 100644
index 00000000..9c49daf7
--- /dev/null
+++ b/static/bidder-params/vidoomy.json
@@ -0,0 +1,14 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Vidoomy Adapter Params",
+  "description": "A schema which validates params accepted by the Vidoomy adapter",
+  "type": "object",
+  "properties": {
+    "zoneId": {
+      "type": "string",
+      "description": "Specific zone ID for this placement, belonging to app/site",
+      "minLength": 1
+    }
+  },
+  "required": ["zoneId"]
+}
\ No newline at end of file
diff --git a/version/version.go b/version/version.go
index e9310276..4292abbe 100644
--- a/version/version.go
+++ b/version/version.go
@@ -6,6 +6,9 @@ package version
 // Populated automatically at build / releases in the Docker image
 var Ver string
 
+// VerUnknown is the version used if Ver has not been set by ldflags.
+const VerUnknown = "unknown"
+
 // Rev holds binary revision string
 // Populated using:
 //    go build -ldflags "-X github.com/prebid/prebid-server/version.Rev=`git rev-parse --short HEAD`"
diff --git a/version/xprebidheader.go b/version/xprebidheader.go
new file mode 100644
index 00000000..5d91ca49
--- /dev/null
+++ b/version/xprebidheader.go
@@ -0,0 +1,53 @@
+package version
+
+import (
+	"strings"
+
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+const xPrebidHeaderVersionPrefix = "pbs-go"
+
+func BuildXPrebidHeader(version string) string {
+	sb := &strings.Builder{}
+	writeNameVersionRecord(sb, xPrebidHeaderVersionPrefix, version)
+	return sb.String()
+}
+
+func BuildXPrebidHeaderForRequest(bidRequest *openrtb2.BidRequest, version string) string {
+	req := &openrtb_ext.RequestWrapper{BidRequest: bidRequest}
+
+	sb := &strings.Builder{}
+	writeNameVersionRecord(sb, xPrebidHeaderVersionPrefix, version)
+
+	if reqExt, err := req.GetRequestExt(); err == nil && reqExt != nil {
+		if prebidExt := reqExt.GetPrebid(); prebidExt != nil {
+			if channel := prebidExt.Channel; channel != nil {
+				writeNameVersionRecord(sb, channel.Name, channel.Version)
+			}
+		}
+	}
+	if appExt, err := req.GetAppExt(); err == nil && appExt != nil {
+		if prebidExt := appExt.GetPrebid(); prebidExt != nil {
+			writeNameVersionRecord(sb, prebidExt.Source, prebidExt.Version)
+		}
+	}
+	return sb.String()
+}
+
+func writeNameVersionRecord(sb *strings.Builder, name, version string) {
+	if name == "" {
+		return
+	}
+	if version == "" {
+		version = VerUnknown
+	}
+	if sb.Len() != 0 {
+		sb.WriteString(",")
+	}
+	sb.WriteString(name)
+	sb.WriteString("/")
+	sb.WriteString(version)
+}


Test patch:
diff --git a/adapters/adagio/adagio_test.go b/adapters/adagio/adagio_test.go
deleted file mode 100644
index d5e25c78..00000000
--- a/adapters/adagio/adagio_test.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package adagio
-
-import (
-	"encoding/json"
-	"github.com/mxmCherry/openrtb/v15/openrtb2"
-	"github.com/stretchr/testify/assert"
-	"testing"
-
-	"github.com/prebid/prebid-server/adapters/adapterstest"
-	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/openrtb_ext"
-)
-
-func buildFakeBidRequest() openrtb2.BidRequest {
-	imp1 := openrtb2.Imp{
-		ID:     "some-impression-id",
-		Banner: &openrtb2.Banner{},
-		Ext:    json.RawMessage(`{"bidder": {"organizationId": "1000", "site": "site-name", "placement": "ban_atf"}}`),
-	}
-
-	fakeBidRequest := openrtb2.BidRequest{
-		ID:  "some-request-id",
-		Imp: []openrtb2.Imp{imp1},
-	}
-
-	return fakeBidRequest
-}
-
-func TestJsonSamples(t *testing.T) {
-	bidder, buildErr := Builder(openrtb_ext.BidderAdagio, config.Adapter{
-		Endpoint: "http://localhost/prebid_server"})
-
-	if buildErr != nil {
-		t.Fatalf("Builder returned unexpected error %v", buildErr)
-	}
-
-	adapterstest.RunJSONBidderTest(t, "adagiotest", bidder)
-}
-
-func TestMakeRequests_NoGzip(t *testing.T) {
-	fakeBidRequest := buildFakeBidRequest()
-	fakeBidRequest.Test = 1 // Do not use Gzip in Test Mode.
-
-	bidder, buildErr := Builder(openrtb_ext.BidderAdagio, config.Adapter{
-		Endpoint: "http://localhost/prebid_server"})
-
-	if buildErr != nil {
-		t.Fatalf("Builder returned unexpected error %v", buildErr)
-	}
-
-	requestData, errs := bidder.MakeRequests(&fakeBidRequest, nil)
-
-	assert.Nil(t, errs)
-	assert.Equal(t, 1, len(requestData))
-
-	body := &openrtb2.BidRequest{}
-	err := json.Unmarshal(requestData[0].Body, body)
-	assert.NoError(t, err, "Request body unmarshalling error should be nil")
-	assert.Equal(t, 1, len(body.Imp))
-}
-
-func TestMakeRequests_Gzip(t *testing.T) {
-	fakeBidRequest := buildFakeBidRequest()
-
-	bidder, buildErr := Builder(openrtb_ext.BidderAdagio, config.Adapter{
-		Endpoint: "http://localhost/prebid_server"})
-
-	if buildErr != nil {
-		t.Fatalf("Builder returned unexpected error %v", buildErr)
-	}
-
-	requestData, errs := bidder.MakeRequests(&fakeBidRequest, nil)
-	assert.Empty(t, errs, "Got errors while making requests")
-	assert.Equal(t, []string{"gzip"}, requestData[0].Headers["Content-Encoding"])
-}
diff --git a/adapters/adagio/adagiotest/exemplary/banner-web.json b/adapters/adagio/adagiotest/exemplary/banner-web.json
deleted file mode 100644
index 732b40d2..00000000
--- a/adapters/adagio/adagiotest/exemplary/banner-web.json
+++ /dev/null
@@ -1,155 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "device": {
-      "ua": "test-user-agent",
-      "ip": "123.123.123.123",
-      "language": "en",
-      "dnt": 0
-    },
-    "tmax": 1000,
-    "user": {
-      "buyeruid": "awesome-user"
-    },
-    "site": {
-      "page": "test.com",
-      "publisher": {
-        "id": "123456789"
-      }
-    },
-    "imp": [
-      {
-        "id": "some-impression-id",
-        "banner": {
-          "w":320,
-          "h":50
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "ban_atf"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ],
-          "X-Forwarded-For": [
-            "123.123.123.123"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "device": {
-            "ua": "test-user-agent",
-            "ip": "123.123.123.123",
-            "language": "en",
-            "dnt": 0
-          },
-          "imp": [
-            {
-              "id": "some-impression-id",
-              "banner": {
-                "w":320,
-                "h":50
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "ban_atf"
-                }
-              }
-            }
-          ],
-          "site": {
-            "page": "test.com",
-            "publisher": {
-              "id": "123456789"
-            }
-          },
-          "user": {
-            "buyeruid": "awesome-user"
-          },
-          "tmax": 1000
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "awesome-resp-id",
-          "seatbid": [
-            {
-              "bid": [
-                {
-                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
-                  "impid": "some-impression-id",
-                  "price": 3.5,
-                  "adm": "awesome-markup",
-                  "adomain": [
-                    "awesome.com"
-                  ],
-                  "crid": "20",
-                  "w": 320,
-                  "h": 50,
-                  "ext": {
-                    "prebid": {
-                      "type": "banner"
-                    }
-                  }
-                }
-              ],
-              "seat": "adagio"
-            }
-          ],
-          "cur": "USD",
-          "ext": {
-            "responsetimemillis": {
-              "adagio": 154
-            },
-            "tmaxrequest": 1000
-          }
-        }
-      }
-    }
-  ],
-  "expectedBidResponses": [
-    {
-      "bids":[
-        {
-          "bid":  {
-            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
-            "impid": "some-impression-id",
-            "price": 3.5,
-            "adm": "awesome-markup",
-            "crid": "20",
-            "adomain": [
-              "awesome.com"
-            ],
-            "w": 320,
-            "h": 50,
-            "ext": {
-              "prebid": {
-                "type": "banner"
-              }
-            }
-          },
-          "type": "banner"
-        }
-      ]
-    }
-  ]
-}
diff --git a/adapters/adagio/adagiotest/exemplary/multi-format.json b/adapters/adagio/adagiotest/exemplary/multi-format.json
deleted file mode 100644
index 85e0be26..00000000
--- a/adapters/adagio/adagiotest/exemplary/multi-format.json
+++ /dev/null
@@ -1,165 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "device": {
-      "ua": "test-user-agent",
-      "ip": "123.123.123.123",
-      "language": "en",
-      "dnt": 0
-    },
-    "tmax": 1000,
-    "user": {
-      "buyeruid": "awesome-user"
-    },
-    "site": {
-      "page": "test.com",
-      "publisher": {
-        "id": "123456789"
-      }
-    },
-    "imp": [
-      {
-        "id": "multi-format-id",
-        "banner": {
-          "w":320,
-          "h":50
-        },
-        "video": {
-          "mimes": ["video\/mp4"],
-          "w": 640,
-          "h": 480
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "ban_atf"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ],
-          "X-Forwarded-For": [
-            "123.123.123.123"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "device": {
-            "ua": "test-user-agent",
-            "ip": "123.123.123.123",
-            "language": "en",
-            "dnt": 0
-          },
-          "imp": [
-            {
-              "id": "multi-format-id",
-              "banner": {
-                "w":320,
-                "h":50
-              },
-              "video": {
-                "mimes": ["video\/mp4"],
-                "w": 640,
-                "h": 480
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "ban_atf"
-                }
-              }
-            }
-          ],
-          "site": {
-            "page": "test.com",
-            "publisher": {
-              "id": "123456789"
-            }
-          },
-          "user": {
-            "buyeruid": "awesome-user"
-          },
-          "tmax": 1000
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "awesome-resp-id",
-          "seatbid": [
-            {
-              "bid": [
-                {
-                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
-                  "impid": "multi-format-id",
-                  "price": 3.5,
-                  "adm": "awesome-markup",
-                  "adomain": [
-                    "awesome.com"
-                  ],
-                  "crid": "20",
-                  "w": 320,
-                  "h": 50,
-                  "ext": {
-                    "prebid": {
-                      "type": "banner"
-                    }
-                  }
-                }
-              ],
-              "seat": "adagio"
-            }
-          ],
-          "cur": "USD",
-          "ext": {
-            "responsetimemillis": {
-              "adagio": 154
-            },
-            "tmaxrequest": 1000
-          }
-        }
-      }
-    }
-  ],
-  "expectedBidResponses": [
-    {
-      "bids":[
-        {
-          "bid":  {
-            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
-            "impid": "multi-format-id",
-            "price": 3.5,
-            "adm": "awesome-markup",
-            "crid": "20",
-            "adomain": [
-              "awesome.com"
-            ],
-            "w": 320,
-            "h": 50,
-            "ext": {
-              "prebid": {
-                "type": "banner"
-              }
-            }
-          },
-          "type": "banner"
-        }
-      ]
-    }
-  ]
-}
diff --git a/adapters/adagio/adagiotest/exemplary/multi-imp.json b/adapters/adagio/adagiotest/exemplary/multi-imp.json
deleted file mode 100644
index 66af28ea..00000000
--- a/adapters/adagio/adagiotest/exemplary/multi-imp.json
+++ /dev/null
@@ -1,222 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "device": {
-      "ua": "test-user-agent",
-      "ip": "123.123.123.123",
-      "language": "en",
-      "dnt": 0
-    },
-    "tmax": 1000,
-    "user": {
-      "buyeruid": "awesome-user"
-    },
-    "site": {
-      "page": "test.com",
-      "publisher": {
-        "id": "123456789"
-      }
-    },
-    "imp": [
-      {
-        "id": "banner-impression-id",
-        "banner": {
-          "w":320,
-          "h":50
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "ban_atf"
-          }
-        }
-      },
-      {
-        "id": "video-impression-id",
-        "video": {
-          "mimes": ["video\/mp4"],
-          "w": 640,
-          "h": 480
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "in_article"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ],
-          "X-Forwarded-For": [
-            "123.123.123.123"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "device": {
-            "ua": "test-user-agent",
-            "ip": "123.123.123.123",
-            "language": "en",
-            "dnt": 0
-          },
-          "imp": [
-            {
-              "id": "banner-impression-id",
-              "banner": {
-                "w":320,
-                "h":50
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "ban_atf"
-                }
-              }
-            },
-            {
-              "id": "video-impression-id",
-              "video": {
-                "mimes": ["video\/mp4"],
-                "w": 640,
-                "h": 480
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "in_article"
-                }
-              }
-            }
-          ],
-          "site": {
-            "page": "test.com",
-            "publisher": {
-              "id": "123456789"
-            }
-          },
-          "user": {
-            "buyeruid": "awesome-user"
-          },
-          "tmax": 1000
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "awesome-resp-id",
-          "seatbid": [
-            {
-              "bid": [
-                {
-                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
-                  "impid": "banner-impression-id",
-                  "price": 3.5,
-                  "adm": "awesome-markup",
-                  "adomain": [
-                    "awesome.com"
-                  ],
-                  "crid": "20",
-                  "w": 320,
-                  "h": 50,
-                  "ext": {
-                    "prebid": {
-                      "type": "banner"
-                    }
-                  }
-                },
-                {
-                  "id": "b4ae1b4e2fc24a4fb45540082e98e162",
-                  "impid": "video-impression-id",
-                  "price": 3.5,
-                  "adm": "awesome-markup",
-                  "adomain": [
-                    "awesome.com"
-                  ],
-                  "crid": "20",
-                  "w": 640,
-                  "h": 480,
-                  "ext": {
-                    "prebid": {
-                      "type": "video"
-                    }
-                  }
-                }
-              ],
-              "seat": "adagio"
-            }
-          ],
-          "cur": "USD",
-          "ext": {
-            "responsetimemillis": {
-              "adagio": 154
-            },
-            "tmaxrequest": 1000
-          }
-        }
-      }
-    }
-  ],
-  "expectedBidResponses": [
-    {
-      "bids":[
-        {
-          "bid":  {
-            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
-            "impid": "banner-impression-id",
-            "price": 3.5,
-            "adm": "awesome-markup",
-            "crid": "20",
-            "adomain": [
-              "awesome.com"
-            ],
-            "w": 320,
-            "h": 50,
-            "ext": {
-              "prebid": {
-                "type": "banner"
-              }
-            }
-          },
-          "type": "banner"
-        },
-        {
-          "bid":  {
-            "id": "b4ae1b4e2fc24a4fb45540082e98e162",
-            "impid": "video-impression-id",
-            "price": 3.5,
-            "adm": "awesome-markup",
-            "crid": "20",
-            "adomain": [
-              "awesome.com"
-            ],
-            "w": 640,
-            "h": 480,
-            "ext": {
-              "prebid": {
-                "type": "video"
-              }
-            }
-          },
-          "type": "video"
-        }
-      ]
-    }
-  ]
-}
diff --git a/adapters/adagio/adagiotest/exemplary/native-web.json b/adapters/adagio/adagiotest/exemplary/native-web.json
deleted file mode 100644
index 0085aadd..00000000
--- a/adapters/adagio/adagiotest/exemplary/native-web.json
+++ /dev/null
@@ -1,151 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "device": {
-      "ua": "test-user-agent",
-      "ipv6": "2607:fb90:f27:4512:d800:cb23:a603:e245",
-      "language": "en",
-      "dnt": 0
-    },
-    "tmax": 1000,
-    "user": {
-      "buyeruid": "awesome-user"
-    },
-    "site": {
-      "page": "test.com",
-      "publisher": {
-        "id": "123456789"
-      }
-    },
-    "imp": [
-      {
-        "id": "some-impression-id",
-        "native": {
-          "ver":"1.1",
-          "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "in_article"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ],
-          "X-Forwarded-For": [
-            "2607:fb90:f27:4512:d800:cb23:a603:e245"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "device": {
-            "ua": "test-user-agent",
-            "ipv6": "2607:fb90:f27:4512:d800:cb23:a603:e245",
-            "language": "en",
-            "dnt": 0
-          },
-          "imp": [
-            {
-              "id": "some-impression-id",
-              "native": {
-                "ver":"1.1",
-                "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "in_article"
-                }
-              }
-            }
-          ],
-          "site": {
-            "page": "test.com",
-            "publisher": {
-              "id": "123456789"
-            }
-          },
-          "user": {
-            "buyeruid": "awesome-user"
-          },
-          "tmax": 1000
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "awesome-resp-id",
-          "seatbid": [
-            {
-              "bid": [
-                {
-                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
-                  "impid": "some-impression-id",
-                  "price": 3.5,
-                  "adm": "awesome-markup",
-                  "adomain": [
-                    "awesome.com"
-                  ],
-                  "crid": "20",
-                  "ext": {
-                    "prebid": {
-                      "type": "native"
-                    }
-                  }
-                }
-              ],
-              "seat": "adagio"
-            }
-          ],
-          "cur": "USD",
-          "ext": {
-            "responsetimemillis": {
-              "acuityads": 154
-            },
-            "tmaxrequest": 1000
-          }
-        }
-      }
-    }
-  ],
-  "expectedBidResponses": [
-    {
-      "bids":[
-        {
-          "bid":  {
-            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
-            "impid": "some-impression-id",
-            "price": 3.5,
-            "adm": "awesome-markup",
-            "crid": "20",
-            "adomain": [
-              "awesome.com"
-            ],
-            "ext": {
-              "prebid": {
-                "type": "native"
-              }
-            }
-          },
-          "type": "native"
-        }
-      ]
-    }
-  ]
-}
diff --git a/adapters/adagio/adagiotest/exemplary/video-web.json b/adapters/adagio/adagiotest/exemplary/video-web.json
deleted file mode 100644
index 353420ee..00000000
--- a/adapters/adagio/adagiotest/exemplary/video-web.json
+++ /dev/null
@@ -1,175 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "device": {
-      "ua": "test-user-agent",
-      "ip": "123.123.123.123",
-      "language": "en",
-      "dnt": 0
-    },
-    "tmax": 1000,
-    "user": {
-      "buyeruid": "awesome-user"
-    },
-    "site": {
-      "page": "test.com",
-      "publisher": {
-        "id": "123456789"
-      }
-    },
-    "imp": [
-      {
-        "id": "some-impression-id",
-        "video": {
-          "mimes": [
-            "video\/mp4"
-          ],
-          "w": 640,
-          "h": 480,
-          "minduration": 120,
-          "maxduration": 150
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "in_article"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ],
-          "X-Forwarded-For": [
-            "123.123.123.123"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "device": {
-            "ua": "test-user-agent",
-            "ip": "123.123.123.123",
-            "language": "en",
-            "dnt": 0
-          },
-          "imp": [
-            {
-              "id": "some-impression-id",
-              "video": {
-                "mimes": [
-                  "video\/mp4"
-                ],
-                "minduration": 120,
-                "maxduration": 150,
-                "w": 640,
-                "h": 480
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "in_article"
-                }
-              }
-            }
-          ],
-          "site": {
-            "page": "test.com",
-            "publisher": {
-              "id": "123456789"
-            }
-          },
-          "user": {
-            "buyeruid": "awesome-user"
-          },
-          "tmax": 1000
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "awesome-resp-id",
-          "seatbid": [
-            {
-              "bid": [
-                {
-                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
-                  "impid": "some-impression-id",
-                  "price": 3.5,
-                  "adm": "awesome-markup",
-                  "adomain": [
-                    "awesome.com"
-                  ],
-                  "crid": "20",
-                  "w": 1280,
-                  "h": 720,
-                  "ext": {
-                    "adagio": {
-                      "outstream": {
-                        "bvwUrl": "https://cool.io"
-                      }
-                    },
-                    "prebid": {
-                      "type": "video"
-                    }
-                  }
-                }
-              ],
-              "seat": "adagio"
-            }
-          ],
-          "cur": "USD",
-          "ext": {
-            "responsetimemillis": {
-              "adagio": 154
-            },
-            "tmaxrequest": 1000
-          }
-        }
-      }
-    }
-  ],
-  "expectedBidResponses": [
-    {
-      "bids": [
-        {
-          "bid": {
-            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
-            "impid": "some-impression-id",
-            "price": 3.5,
-            "adm": "awesome-markup",
-            "adomain": [
-              "awesome.com"
-            ],
-            "crid": "20",
-            "w": 1280,
-            "h": 720,
-            "ext": {
-              "adagio": {
-                "outstream": {
-                  "bvwUrl": "https://cool.io"
-                }
-              },
-              "prebid": {
-                "type": "video"
-              }
-            }
-          },
-          "type":"video"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/adapters/adagio/adagiotest/supplemental/response-miss-ext-bid-type.json b/adapters/adagio/adagiotest/supplemental/response-miss-ext-bid-type.json
deleted file mode 100644
index 7b1267f6..00000000
--- a/adapters/adagio/adagiotest/supplemental/response-miss-ext-bid-type.json
+++ /dev/null
@@ -1,130 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "device": {
-      "ua": "test-user-agent",
-      "ip": "123.123.123.123",
-      "language": "en",
-      "dnt": 0
-    },
-    "tmax": 1000,
-    "user": {
-      "buyeruid": "awesome-user"
-    },
-    "site": {
-      "page": "test.com",
-      "publisher": {
-        "id": "123456789"
-      }
-    },
-    "imp": [
-      {
-        "id": "some-impression-id",
-        "banner": {
-          "w": 320,
-          "h": 50
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "ban_atf"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ],
-          "X-Forwarded-For": [
-            "123.123.123.123"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "device": {
-            "ua": "test-user-agent",
-            "ip": "123.123.123.123",
-            "language": "en",
-            "dnt": 0
-          },
-          "imp": [
-            {
-              "id": "some-impression-id",
-              "banner": {
-                "w": 320,
-                "h": 50
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "ban_atf"
-                }
-              }
-            }
-          ],
-          "site": {
-            "page": "test.com",
-            "publisher": {
-              "id": "123456789"
-            }
-          },
-          "user": {
-            "buyeruid": "awesome-user"
-          },
-          "tmax": 1000
-        }
-      },
-      "mockResponse": {
-        "status": 200,
-        "body": {
-          "id": "awesome-resp-id",
-          "seatbid": [
-            {
-              "bid": [
-                {
-                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
-                  "impid": "some-impression-id",
-                  "price": 3.5,
-                  "adm": "awesome-markup",
-                  "adomain": [
-                    "awesome.com"
-                  ],
-                  "crid": "20",
-                  "w": 320,
-                  "h": 50,
-                  "ext": {}
-                }
-              ],
-              "seat": "adagio"
-            }
-          ],
-          "cur": "USD",
-          "ext": {
-            "responsetimemillis": {
-              "adagio": 154
-            },
-            "tmaxrequest": 1000
-          }
-        }
-      }
-    }
-  ],
-  "expectedBidResponses": [
-    {
-      "bids": []
-    }
-  ]
-}
diff --git a/adapters/adagio/adagiotest/supplemental/status-204.json b/adapters/adagio/adagiotest/supplemental/status-204.json
deleted file mode 100644
index 4d604a01..00000000
--- a/adapters/adagio/adagiotest/supplemental/status-204.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "imp": [
-      {
-        "id": "some-impression-id",
-        "banner": {
-          "w": 320,
-          "h": 50
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "ban_atf"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "imp": [
-            {
-              "id": "some-impression-id",
-              "banner": {
-                "w": 320,
-                "h": 50
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "ban_atf"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 204,
-        "body": {}
-      }
-    }
-  ],
-  "expectedBidResponses": []
-}
diff --git a/adapters/adagio/adagiotest/supplemental/status-400.json b/adapters/adagio/adagiotest/supplemental/status-400.json
deleted file mode 100644
index 093c5458..00000000
--- a/adapters/adagio/adagiotest/supplemental/status-400.json
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "imp": [
-      {
-        "id": "some-impression-id",
-        "banner": {
-          "w": 320,
-          "h": 50
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "ban_atf"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "imp": [
-            {
-              "id": "some-impression-id",
-              "banner": {
-                "w": 320,
-                "h": 50
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "ban_atf"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 400,
-        "body": "bad request"
-      }
-    }
-  ],
-  "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/adagio/adagiotest/supplemental/status-401.json b/adapters/adagio/adagiotest/supplemental/status-401.json
deleted file mode 100644
index a33aca20..00000000
--- a/adapters/adagio/adagiotest/supplemental/status-401.json
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "imp": [
-      {
-        "id": "some-impression-id",
-        "banner": {
-          "w": 320,
-          "h": 50
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "ban_atf"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "imp": [
-            {
-              "id": "some-impression-id",
-              "banner": {
-                "w": 320,
-                "h": 50
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "ban_atf"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 401,
-        "body": "unauthorized"
-      }
-    }
-  ],
-  "expectedMakeBidsErrors": [
-    {
-      "value": "Unexpected status code: 401. Run with request.debug = 1 for more info",
-      "comparison": "literal"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/adapters/adagio/adagiotest/supplemental/status-403.json b/adapters/adagio/adagiotest/supplemental/status-403.json
deleted file mode 100644
index 59c5a9cb..00000000
--- a/adapters/adagio/adagiotest/supplemental/status-403.json
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "imp": [
-      {
-        "id": "some-impression-id",
-        "banner": {
-          "w": 320,
-          "h": 50
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "ban_atf"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "imp": [
-            {
-              "id": "some-impression-id",
-              "banner": {
-                "w": 320,
-                "h": 50
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "ban_atf"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 403,
-        "body": "forbidden"
-      }
-    }
-  ],
-  "expectedMakeBidsErrors": [
-    {
-      "value": "Unexpected status code: 403. Run with request.debug = 1 for more info",
-      "comparison": "literal"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/adapters/adagio/adagiotest/supplemental/status-500.json b/adapters/adagio/adagiotest/supplemental/status-500.json
deleted file mode 100644
index 0077d745..00000000
--- a/adapters/adagio/adagiotest/supplemental/status-500.json
+++ /dev/null
@@ -1,68 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "debug": 1,
-    "id": "some-request-id",
-    "imp": [
-      {
-        "id": "some-impression-id",
-        "banner": {
-          "w": 320,
-          "h": 50
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "ban_atf"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "imp": [
-            {
-              "id": "some-impression-id",
-              "banner": {
-                "w": 320,
-                "h": 50
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "ban_atf"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 500,
-        "body": "internal error"
-      }
-    }
-  ],
-  "expectedMakeBidsErrors": [
-    {
-      "value": "Unexpected status code: 500. Run with request.debug = 1 for more info",
-      "comparison": "literal"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/adapters/adagio/adagiotest/supplemental/status-503.json b/adapters/adagio/adagiotest/supplemental/status-503.json
deleted file mode 100644
index d2a52893..00000000
--- a/adapters/adagio/adagiotest/supplemental/status-503.json
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "imp": [
-      {
-        "id": "some-impression-id",
-        "banner": {
-          "w": 320,
-          "h": 50
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "ban_atf"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "imp": [
-            {
-              "id": "some-impression-id",
-              "banner": {
-                "w": 320,
-                "h": 50
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "ban_atf"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 503,
-        "body": "Service unavailable"
-      }
-    }
-  ],
-  "expectedMakeBidsErrors": [
-    {
-      "value": "Unexpected status code: 503. Run with request.debug = 1 for more info",
-      "comparison": "literal"
-    }
-  ]
-}
diff --git a/adapters/adagio/adagiotest/supplemental/status-504.json b/adapters/adagio/adagiotest/supplemental/status-504.json
deleted file mode 100644
index 1b779d5c..00000000
--- a/adapters/adagio/adagiotest/supplemental/status-504.json
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-  "mockBidRequest": {
-    "test": 1,
-    "id": "some-request-id",
-    "imp": [
-      {
-        "id": "some-impression-id",
-        "banner": {
-          "w": 320,
-          "h": 50
-        },
-        "ext": {
-          "bidder": {
-            "organizationId": "1000",
-            "site": "site-name",
-            "placement": "ban_atf"
-          }
-        }
-      }
-    ]
-  },
-  "httpCalls": [
-    {
-      "expectedRequest": {
-        "headers": {
-          "Content-Type": [
-            "application/json;charset=utf-8"
-          ],
-          "Accept": [
-            "application/json"
-          ]
-        },
-        "uri": "http://localhost/prebid_server",
-        "body": {
-          "test": 1,
-          "id": "some-request-id",
-          "imp": [
-            {
-              "id": "some-impression-id",
-              "banner": {
-                "w": 320,
-                "h": 50
-              },
-              "ext": {
-                "bidder": {
-                  "organizationId": "1000",
-                  "site": "site-name",
-                  "placement": "ban_atf"
-                }
-              }
-            }
-          ]
-        }
-      },
-      "mockResponse": {
-        "status": 504,
-        "body": "gateway timeout"
-      }
-    }
-  ],
-  "expectedMakeBidsErrors": [
-    {
-      "value": "Unexpected status code: 504. Run with request.debug = 1 for more info",
-      "comparison": "literal"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/adapters/adagio/params_test.go b/adapters/adagio/params_test.go
deleted file mode 100644
index ee8f702e..00000000
--- a/adapters/adagio/params_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package adagio
-
-import (
-	"encoding/json"
-	"testing"
-
-	"github.com/prebid/prebid-server/openrtb_ext"
-)
-
-var validParams = []string{
-	`{ "organizationId": "1234", "site": "adagio-io", "placement": "ban_atf" }`,
-	`{ "organizationId": "1234", "site": "adagio-io", "placement": "ban_atf", "_unknown": "ban_atf"}`,
-	`{ "organizationId": "1234", "site": "adagio-io", "placement": "ban_atf", "features": {"a": "a", "b": "b"}}`,
-}
-
-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.BidderAdagio, json.RawMessage(validParam)); err != nil {
-			t.Errorf("Schema rejected Adagio params: %s", validParam)
-		}
-	}
-}
-
-var invalidParams = []string{
-	``,
-	`null`,
-	`true`,
-	`5`,
-	`4.2`,
-	`[]`,
-	`{}`,
-	`{"adCode": "string", "seatCode": 5, "originalPublisherid": "string"}`,
-	`{ "organizationId": "", "site": "", "placement": "" }`,
-	`{ "organizationId": "", "site": "2", "placement": "3" }`,
-	`{ "organizationId": "1", "site": "", "placement": "3" }`,
-	`{ "organizationId": "1", "site": "2", "placement": "" }`,
-	`{ "organizationId": 1, "site": "2", "placement": "3" }`,
-	`{ "organizationId": "1234", "site": "adagio-io", "placement": "ban_atf", "features": {"a": "a", "notastring": true}}`,
-}
-
-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.BidderAdagio, json.RawMessage(invalidParam)); err == nil {
-			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
-		}
-	}
-}
diff --git a/adapters/adyoulike/adyouliketest/supplemental/status-no-content.json b/adapters/adyoulike/adyouliketest/supplemental/status-no-content.json
index 9ef51e88..d7d7df19 100644
--- a/adapters/adyoulike/adyouliketest/supplemental/status-no-content.json
+++ b/adapters/adyoulike/adyouliketest/supplemental/status-no-content.json
@@ -56,11 +56,5 @@
   ],
 
   "expectedBidResponses": [],
-
-  "expectedMakeBidsErrors": [
-    {
-      "value": "MakeBids error: No Content",
-      "comparison": "literal"
-    }
-  ]
+  "expectedMakeBidsErrors": []
 }
\ No newline at end of file
diff --git a/adapters/algorix/algorixtest/exemplary/sample-banner-with-mediatype.json b/adapters/algorix/algorixtest/exemplary/sample-banner-with-mediatype.json
new file mode 100644
index 00000000..c9f9f877
--- /dev/null
+++ b/adapters/algorix/algorixtest/exemplary/sample-banner-with-mediatype.json
@@ -0,0 +1,105 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 320, "h": 50}]
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 320,
+          "h": 480
+        },
+        "ext": {
+          "bidder": {
+            "sid": "testSid",
+            "token": "testToken",
+            "region": "APAC"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://apac.xyz.test.com?sid=testSid&token=testToken",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id":"test-imp-id",
+              "banner": {
+                "format": [{"w": 320, "h": 50}],
+                "w": 320,
+                "h": 50
+              },
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "w": 320,
+                "h": 480
+              },
+              "ext": {
+                "bidder": {
+                  "sid": "testSid",
+                  "token": "testToken",
+                  "region": "APAC"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "ttx",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "test-imp-id",
+                "price": 0.5,
+                "adm": "some-ads",
+                "crid": "crid_testid",
+                "ext": {
+                  "mediaType": "video"
+                }
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-ads",
+            "crid": "crid_testid",
+            "ext": {
+              "mediaType": "video"
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/algorix/algorixtest/exemplary/sample-rewarded-video.json b/adapters/algorix/algorixtest/exemplary/sample-rewarded-video.json
new file mode 100644
index 00000000..0d98de60
--- /dev/null
+++ b/adapters/algorix/algorixtest/exemplary/sample-rewarded-video.json
@@ -0,0 +1,102 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "w": 320,
+          "h": 480,
+          "mimes": [
+            "video/mp4"
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "sid": "testSid",
+            "token": "testToken",
+            "placementId": "testPlacementId",
+            "appId": "testAppId"
+          },
+          "prebid": {
+            "is_rewarded_inventory": 1
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://xyz.test.com?sid=testSid&token=testToken",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id":"test-imp-id",
+              "video": {
+                "w": 320,
+                "h": 480,
+                "mimes": [
+                  "video/mp4"
+                ],
+                "ext": {
+                  "rewarded": 1
+                }
+              },
+              "ext": {
+                "bidder": {
+                  "sid": "testSid",
+                  "token": "testToken",
+                  "placementId": "testPlacementId",
+                  "appId": "testAppId"
+                },
+                "prebid": {
+                  "is_rewarded_inventory": 1
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "ttx",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "test-imp-id",
+                "price": 1.2,
+                "adm": "some-ads",
+                "crid": "crid_testid"
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id",
+            "price": 1.2,
+            "adm": "some-ads",
+            "crid": "crid_testid"
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/algorix/algorixtest/supplemental/multiformat-no-mediatype-response.json b/adapters/algorix/algorixtest/supplemental/multiformat-no-mediatype-response.json
new file mode 100644
index 00000000..3828c52d
--- /dev/null
+++ b/adapters/algorix/algorixtest/supplemental/multiformat-no-mediatype-response.json
@@ -0,0 +1,90 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 320,
+          "h": 480
+        },
+        "banner": {
+          "format": [{"w": 320, "h": 50}]
+        },
+        "ext": {
+          "bidder": {
+            "sid": "testSid",
+            "token": "testToken",
+            "region": "APAC"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://apac.xyz.test.com?sid=testSid&token=testToken",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id":"test-imp-id",
+              "banner": {
+                "format": [{"w": 320, "h": 50}],
+                "w": 320,
+                "h": 50
+              },
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "w": 320,
+                "h": 480
+              },
+              "ext": {
+                "bidder": {
+                  "sid": "testSid",
+                  "token": "testToken",
+                  "region": "APAC"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "ttx",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "test-imp-id",
+                "price": 0.5,
+                "adm": "some-ads",
+                "crid": "crid_testid",
+                "ext": {
+                }
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "comparison": "literal",
+      "value": "unable to fetch mediaType in multi-format: test-imp-id"
+    }
+  ]
+}
diff --git a/adapters/algorix/algorixtest/supplemental/sample-banner-with-other-mediatype.json b/adapters/algorix/algorixtest/supplemental/sample-banner-with-other-mediatype.json
new file mode 100644
index 00000000..df535318
--- /dev/null
+++ b/adapters/algorix/algorixtest/supplemental/sample-banner-with-other-mediatype.json
@@ -0,0 +1,91 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 320, "h": 50}]
+        },
+        "ext": {
+          "bidder": {
+            "sid": "testSid",
+            "token": "testToken",
+            "region": "APAC"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://apac.xyz.test.com?sid=testSid&token=testToken",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id":"test-imp-id",
+              "banner": {
+                "format": [{"w": 320, "h": 50}],
+                "w": 320,
+                "h": 50
+              },
+              "ext": {
+                "bidder": {
+                  "sid": "testSid",
+                  "token": "testToken",
+                  "region": "APAC"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "ttx",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "test-imp-id",
+                "price": 0.5,
+                "adm": "some-ads",
+                "crid": "crid_testid",
+                "ext": {
+                  "mediaType": "video"
+                }
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-ads",
+            "crid": "crid_testid",
+            "ext": {
+              "mediaType": "video"
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/vidoomy/params_test.go b/adapters/vidoomy/params_test.go
new file mode 100644
index 00000000..d910b654
--- /dev/null
+++ b/adapters/vidoomy/params_test.go
@@ -0,0 +1,57 @@
+package vidoomy
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/vidoomy.json
+//
+// These also validate the format of the external API: request.imp[i].ext.vidoomy
+
+// TestValidParams makes sure that the vidoomy 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.BidderVidoomy, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected vidoomy params: %s", validParam)
+		}
+	}
+}
+
+// TestInvalidParams makes sure that the Vidoomy 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.BidderVidoomy, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"zoneId":"123"}`,
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{"notmid":"123"}`,
+	`{"zoneId":123}`,
+	`{"zoneId":""}`,
+}
diff --git a/adapters/vidoomy/vidoomy_test.go b/adapters/vidoomy/vidoomy_test.go
new file mode 100644
index 00000000..c9181395
--- /dev/null
+++ b/adapters/vidoomy/vidoomy_test.go
@@ -0,0 +1,34 @@
+package vidoomy
+
+import (
+	"testing"
+
+	"github.com/influxdata/influxdb/pkg/testing/assert"
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestVidoomyBidderEndpointConfig(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderVidoomy, config.Adapter{
+		Endpoint: "http://localhost/bid",
+	})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	bidderVidoomy := bidder.(*adapter)
+
+	assert.Equal(t, "http://localhost/bid", bidderVidoomy.endpoint)
+}
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderVidoomy, config.Adapter{})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "vidoomytest", bidder)
+}
diff --git a/adapters/vidoomy/vidoomytest/exemplary/multi-impression-video-banner.json b/adapters/vidoomy/vidoomytest/exemplary/multi-impression-video-banner.json
new file mode 100644
index 00000000..723248dd
--- /dev/null
+++ b/adapters/vidoomy/vidoomytest/exemplary/multi-impression-video-banner.json
@@ -0,0 +1,226 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}]
+        },
+        "ext": {
+          "bidder": {
+            "zoneId": "zone-id-123"
+          }
+        }
+      },
+      {
+        "id": "test-imp-video-id",
+        "video": {
+          "w": 900,
+          "h": 250,
+          "mimes": [
+            "video/x-flv",
+            "video/mp4"
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "zoneId": "zone-id-123"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+      "ip": "127.0.0.1"
+    },
+    "site": {
+      "id": "test-site-id"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "X-Forwarded-For": [
+            "127.0.0.1"
+          ]
+        },
+        "uri": "",
+        "body": {
+          "device": {
+            "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+            "ip": "127.0.0.1"
+          },
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [{"w": 728, "h": 90}],
+                "w": 728,
+                "h": 90
+              },
+              "ext": {
+                "bidder": {
+                  "zoneId": "zone-id-123"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "test-site-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "vidoomy",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "test-imp-id",
+                "price": 0.500000,
+                "adm": "some-test-ad",
+                "crid": "crid_10",
+                "h": 90,
+                "w": 728
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    },
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "X-Forwarded-For": [
+            "127.0.0.1"
+          ]
+        },
+        "uri": "",
+        "body": {
+          "device": {
+            "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+            "ip": "127.0.0.1"
+          },
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-video-id",
+              "video": {
+                "w": 900,
+                "h": 250,
+                "mimes": [
+                  "video/x-flv",
+                  "video/mp4"
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "zoneId": "zone-id-123"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "test-site-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "cur": "USD",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "fe69dd6d-e85c-4186-80a7-605b530bc23b",
+                  "crid": "72745",
+                  "adomain": [
+                    "ad-domain.com"
+                  ],
+                  "price": 3,
+                  "impid": "test-imp-video-id",
+                  "adid": "564",
+                  "adm": "<?xml version=\"1.0\"?><VAST version=\"2.0\"></VAST>"
+                }
+              ],
+              "seat": "vidoomy"
+            }
+          ],
+          "id": "test-request-id"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-ad",
+            "crid": "crid_10",
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        }
+      ]
+    },
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "fe69dd6d-e85c-4186-80a7-605b530bc23b",
+            "crid": "72745",
+            "adomain": [
+              "ad-domain.com"
+            ],
+            "price": 3,
+            "impid": "test-imp-video-id",
+            "adid": "564",
+            "adm": "<?xml version=\"1.0\"?><VAST version=\"2.0\"></VAST>"
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/vidoomy/vidoomytest/exemplary/simple-app-banner.json b/adapters/vidoomy/vidoomytest/exemplary/simple-app-banner.json
new file mode 100644
index 00000000..145524ea
--- /dev/null
+++ b/adapters/vidoomy/vidoomytest/exemplary/simple-app-banner.json
@@ -0,0 +1,116 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}]
+        },
+        "ext": {
+          "bidder": {
+            "zoneId": "zone-id-123"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+      "ip": "127.0.0.1"
+    },
+    "app": {
+      "id": "test-app-id"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "X-Forwarded-For": [
+            "127.0.0.1"
+          ]
+        },
+        "uri": "",
+        "body": {
+          "device": {
+            "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+            "ip": "127.0.0.1"
+          },
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [{"w": 728, "h": 90}],
+                "w": 728,
+                "h": 90
+              },
+              "ext": {
+                "bidder": {
+                  "zoneId": "zone-id-123"
+                }
+              }
+            }
+          ],
+          "app": {
+            "id": "test-app-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "vidoomy",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "test-imp-id",
+                "price": 0.500000,
+                "adm": "some-test-ad",
+                "crid": "crid_10",
+                "h": 90,
+                "w": 728
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-ad",
+            "crid": "crid_10",
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/vidoomy/vidoomytest/exemplary/simple-banner.json b/adapters/vidoomy/vidoomytest/exemplary/simple-banner.json
new file mode 100644
index 00000000..36d27856
--- /dev/null
+++ b/adapters/vidoomy/vidoomytest/exemplary/simple-banner.json
@@ -0,0 +1,116 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}]
+        },
+        "ext": {
+          "bidder": {
+            "zoneId": "zone-id-123"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+      "ip": "127.0.0.1"
+    },
+    "site": {
+      "id": "test-site-id"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "X-Forwarded-For": [
+            "127.0.0.1"
+          ]
+        },
+        "uri": "",
+        "body": {
+          "device": {
+            "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+            "ip": "127.0.0.1"
+          },
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [{"w": 728, "h": 90}],
+                "w": 728,
+                "h": 90
+              },
+              "ext": {
+                "bidder": {
+                  "zoneId": "zone-id-123"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "test-site-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "vidoomy",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "test-imp-id",
+                "price": 0.500000,
+                "adm": "some-test-ad",
+                "crid": "crid_10",
+                "h": 90,
+                "w": 728
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-ad",
+            "crid": "crid_10",
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/vidoomy/vidoomytest/exemplary/simple-site-video.json b/adapters/vidoomy/vidoomytest/exemplary/simple-site-video.json
new file mode 100644
index 00000000..d2d6e028
--- /dev/null
+++ b/adapters/vidoomy/vidoomytest/exemplary/simple-site-video.json
@@ -0,0 +1,121 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-video-id",
+        "video": {
+          "w": 900,
+          "h": 250,
+          "mimes": [
+            "video/x-flv",
+            "video/mp4"
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "zoneId": "zone-id-123"
+          }
+        }
+      }
+    ],
+    "site": {
+      "id": "test",
+      "publisher": {
+        "id": "123"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "method": "GET",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ]
+        },
+        "uri": "",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-video-id",
+              "video": {
+                "w": 900,
+                "h": 250,
+                "mimes": [
+                  "video/x-flv",
+                  "video/mp4"
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "zoneId": "zone-id-123"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "test",
+            "publisher": {
+              "id": "123"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "cur": "USD",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "fe69dd6d-e85c-4186-80a7-605b530bc23b",
+                  "crid": "72745",
+                  "adomain": [
+                    "ad-domain.com"
+                  ],
+                  "price": 3,
+                  "impid": "test-imp-video-id",
+                  "adid": "564",
+                  "adm": "<?xml version=\"1.0\"?><VAST version=\"2.0\"></VAST>"
+                }
+              ],
+              "seat": "vidoomy"
+            }
+          ],
+          "id": "test-request-id"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "fe69dd6d-e85c-4186-80a7-605b530bc23b",
+            "crid": "72745",
+            "adomain": [
+              "ad-domain.com"
+            ],
+            "price": 3,
+            "impid": "test-imp-video-id",
+            "adid": "564",
+            "adm": "<?xml version=\"1.0\"?><VAST version=\"2.0\"></VAST>"
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/vidoomy/vidoomytest/supplemental/server-error.json b/adapters/vidoomy/vidoomytest/supplemental/server-error.json
new file mode 100644
index 00000000..ebeedb14
--- /dev/null
+++ b/adapters/vidoomy/vidoomytest/supplemental/server-error.json
@@ -0,0 +1,70 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}]
+        },
+        "ext": {
+          "bidder": {
+            "zoneId": "zone-id-123"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+      "ip": "127.0.0.1"
+    },
+    "site": {
+      "id": "site-id"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "",
+        "body": {
+          "device": {
+            "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+            "ip": "127.0.0.1"
+          },
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [{"w": 728, "h": 90}],
+                "w": 728,
+                "h": 90
+              },
+              "ext": {
+                "bidder": {
+                  "zoneId": "zone-id-123"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "site-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 500,
+        "body": {
+        }
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 500.",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/vidoomy/vidoomytest/supplemental/server-response-wrong-impid.json b/adapters/vidoomy/vidoomytest/supplemental/server-response-wrong-impid.json
new file mode 100644
index 00000000..dad8f7cc
--- /dev/null
+++ b/adapters/vidoomy/vidoomytest/supplemental/server-response-wrong-impid.json
@@ -0,0 +1,103 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}]
+        },
+        "ext": {
+          "bidder": {
+            "zoneId": "zone-id-123"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+      "ip": "127.0.0.1"
+    },
+    "site": {
+      "id": "site-id"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "X-Forwarded-For": [
+            "127.0.0.1"
+          ]
+        },
+        "uri": "",
+        "body": {
+          "device": {
+            "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+            "ip": "127.0.0.1"
+          },
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [{"w": 728, "h": 90}],
+                "w": 728,
+                "h": 90
+              },
+              "ext": {
+                "bidder": {
+                  "zoneId": "zone-id-123"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "site-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "vidoomy",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "wrong",
+                "price": 0.500000,
+                "adm": "some-test-ad",
+                "crid": "crid_10",
+                "h": 90,
+                "w": 728
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unknown ad unit code 'wrong'",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/vidoomy/vidoomytest/supplemental/simple-banner-no-response.json b/adapters/vidoomy/vidoomytest/supplemental/simple-banner-no-response.json
new file mode 100644
index 00000000..0964351a
--- /dev/null
+++ b/adapters/vidoomy/vidoomytest/supplemental/simple-banner-no-response.json
@@ -0,0 +1,85 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}],
+          "w": 728,
+          "h": 90
+        },
+        "ext": {
+          "bidder": {
+            "zoneId": "zone-id-123"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+      "ipv6": "127.0.0.1"
+    },
+    "site": {
+      "id": "test-site-id"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "X-Forwarded-For": [
+            "127.0.0.1"
+          ]
+        },
+        "uri": "",
+        "body": {
+          "device": {
+            "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+            "ipv6": "127.0.0.1"
+          },
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [{"w": 728, "h": 90}],
+                "w": 728,
+                "h": 90
+              },
+              "ext": {
+                "bidder": {
+                  "zoneId": "zone-id-123"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "test-site-id"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 204,
+        "body": {}
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {}
+  ]
+}
diff --git a/adapters/vidoomy/vidoomytest/supplemental/wrong-banner-format.json b/adapters/vidoomy/vidoomytest/supplemental/wrong-banner-format.json
new file mode 100644
index 00000000..e38f37f4
--- /dev/null
+++ b/adapters/vidoomy/vidoomytest/supplemental/wrong-banner-format.json
@@ -0,0 +1,32 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": []
+        },
+        "ext": {
+          "bidder": {
+            "zoneId": "zone-id-123"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+      "ip": "127.0.0.1"
+    },
+    "site": {
+      "id": "test-site-id"
+    }
+  },
+
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "no sizes provided for Banner []",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/vidoomy/vidoomytest/supplemental/wrong-banner-width-high.json b/adapters/vidoomy/vidoomytest/supplemental/wrong-banner-width-high.json
new file mode 100644
index 00000000..b5c22546
--- /dev/null
+++ b/adapters/vidoomy/vidoomytest/supplemental/wrong-banner-width-high.json
@@ -0,0 +1,34 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}],
+          "w": 0,
+          "h": 720
+        },
+        "ext": {
+          "bidder": {
+            "zoneId": "zone-id-123"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+      "ip": "127.0.0.1"
+    },
+    "site": {
+      "id": "test-site-id"
+    }
+  },
+
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "invalid sizes provided for Banner 0 x 720",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/vidoomy/vidoomytest/supplemental/wrong-banner-with-format.json b/adapters/vidoomy/vidoomytest/supplemental/wrong-banner-with-format.json
new file mode 100644
index 00000000..e4541c9c
--- /dev/null
+++ b/adapters/vidoomy/vidoomytest/supplemental/wrong-banner-with-format.json
@@ -0,0 +1,34 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [{"w": 728, "h": 90}],
+          "w": 0,
+          "h" : 90
+        },
+        "ext": {
+          "bidder": {
+            "zoneId": "zone-id-123"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
+      "ip": "127.0.0.1"
+    },
+    "site": {
+      "id": "test-site-id"
+    }
+  },
+
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "invalid sizes provided for Banner 0 x 90",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/config/config_test.go b/config/config_test.go
index f86f13bd..766244c4 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -217,8 +217,8 @@ func TestDefaults(t *testing.T) {
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 		},
-		SpecialPurpose1: TCF2Purpose{
-			Enabled:            true,
+		SpecialFeature1: TCF2SpecialFeature{
+			Enforce:            true,
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 		},
@@ -272,7 +272,7 @@ gdpr:
     purpose10:
       enforce_vendors: false
       vendor_exceptions: ["foo10"]
-    special_purpose1:
+    special_feature1:
       vendor_exceptions: ["fooSP1"]
 ccpa:
   enforce: true
@@ -540,8 +540,8 @@ func TestFullConfig(t *testing.T) {
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo10")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo10"): {}},
 		},
-		SpecialPurpose1: TCF2Purpose{
-			Enabled:            true, // true by default
+		SpecialFeature1: TCF2SpecialFeature{
+			Enforce:            true, // true by default
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("fooSP1")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("fooSP1"): {}},
 		},
@@ -765,6 +765,91 @@ func TestMigrateConfigPurposeOneTreatment(t *testing.T) {
 	}
 }
 
+func TestMigrateConfigSpecialFeature1(t *testing.T) {
+	oldSpecialFeature1Config := []byte(`
+      gdpr:
+        tcf2:
+          special_purpose1:
+            enabled: true
+            vendor_exceptions: ["appnexus"]
+    `)
+	newSpecialFeature1Config := []byte(`
+      gdpr:
+        tcf2:
+          special_feature1:
+            enforce: true
+            vendor_exceptions: ["appnexus"]
+    `)
+	oldAndNewSpecialFeature1Config := []byte(`
+      gdpr:
+        tcf2:
+          special_purpose1:
+            enabled: false
+            vendor_exceptions: ["appnexus"]
+          special_feature1:
+            enforce: true
+            vendor_exceptions: ["rubicon"]
+    `)
+
+	tests := []struct {
+		description                         string
+		config                              []byte
+		wantSpecialFeature1Enforce          bool
+		wantSpecialFeature1VendorExceptions []string
+	}{
+		{
+			description: "New config and old config not set",
+			config:      []byte{},
+		},
+		{
+			description:                         "New config not set, old config set",
+			config:                              oldSpecialFeature1Config,
+			wantSpecialFeature1Enforce:          true,
+			wantSpecialFeature1VendorExceptions: []string{"appnexus"},
+		},
+		{
+			description:                         "New config set, old config not set",
+			config:                              newSpecialFeature1Config,
+			wantSpecialFeature1Enforce:          true,
+			wantSpecialFeature1VendorExceptions: []string{"appnexus"},
+		},
+		{
+			description:                         "New config and old config set",
+			config:                              oldAndNewSpecialFeature1Config,
+			wantSpecialFeature1Enforce:          true,
+			wantSpecialFeature1VendorExceptions: []string{"rubicon"},
+		},
+	}
+
+	for _, tt := range tests {
+		v := viper.New()
+		v.SetConfigType("yaml")
+		v.ReadConfig(bytes.NewBuffer(tt.config))
+
+		migrateConfigSpecialFeature1(v)
+
+		if len(tt.config) > 0 {
+			assert.Equal(t, tt.wantSpecialFeature1Enforce, v.Get("gdpr.tcf2.special_feature1.enforce").(bool), tt.description)
+			assert.Equal(t, tt.wantSpecialFeature1VendorExceptions, v.GetStringSlice("gdpr.tcf2.special_feature1.vendor_exceptions"), tt.description)
+		} else {
+			assert.Nil(t, v.Get("gdpr.tcf2.special_feature1.enforce"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.special_feature1.vendor_exceptions"), tt.description)
+		}
+
+		var c Configuration
+		err := v.Unmarshal(&c)
+		assert.NoError(t, err, tt.description)
+		assert.Equal(t, tt.wantSpecialFeature1Enforce, c.GDPR.TCF2.SpecialFeature1.Enforce, tt.description)
+
+		// convert expected vendor exceptions to type BidderName
+		expectedVendorExceptions := make([]openrtb_ext.BidderName, 0, 0)
+		for _, ve := range tt.wantSpecialFeature1VendorExceptions {
+			expectedVendorExceptions = append(expectedVendorExceptions, openrtb_ext.BidderName(ve))
+		}
+		assert.ElementsMatch(t, expectedVendorExceptions, c.GDPR.TCF2.SpecialFeature1.VendorExceptions, tt.description)
+	}
+}
+
 func TestMigrateConfigTCF2PurposeEnabledFlags(t *testing.T) {
 	trueStr := "true"
 	falseStr := "false"
@@ -1291,3 +1376,58 @@ func doTimeoutTest(t *testing.T, expected int, requested int, max uint64, def ui
 	limited := cfg.LimitAuctionTimeout(time.Duration(requested) * time.Millisecond)
 	assert.Equal(t, limited, expectedDuration, "Expected %dms timeout, got %dms", expectedDuration, limited/time.Millisecond)
 }
+
+func TestSpecialFeature1VendorExceptionMap(t *testing.T) {
+	baseConfig := []byte(`
+    gdpr:
+      default_value: 0
+      tcf2:
+        special_feature1:
+          vendor_exceptions: `)
+
+	tests := []struct {
+		description             string
+		configVendorExceptions  []byte
+		wantVendorExceptions    []openrtb_ext.BidderName
+		wantVendorExceptionsMap map[openrtb_ext.BidderName]struct{}
+	}{
+		{
+			description:             "nil vendor exceptions",
+			configVendorExceptions:  []byte(`null`),
+			wantVendorExceptions:    []openrtb_ext.BidderName{},
+			wantVendorExceptionsMap: map[openrtb_ext.BidderName]struct{}{},
+		},
+		{
+			description:             "no vendor exceptions",
+			configVendorExceptions:  []byte(`[]`),
+			wantVendorExceptions:    []openrtb_ext.BidderName{},
+			wantVendorExceptionsMap: map[openrtb_ext.BidderName]struct{}{},
+		},
+		{
+			description:             "one vendor exception",
+			configVendorExceptions:  []byte(`["vendor1"]`),
+			wantVendorExceptions:    []openrtb_ext.BidderName{openrtb_ext.BidderName("vendor1")},
+			wantVendorExceptionsMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("vendor1"): {}},
+		},
+		{
+			description:             "many exceptions",
+			configVendorExceptions:  []byte(`["vendor1","vendor2"]`),
+			wantVendorExceptions:    []openrtb_ext.BidderName{openrtb_ext.BidderName("vendor1"), openrtb_ext.BidderName("vendor2")},
+			wantVendorExceptionsMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("vendor1"): {}, openrtb_ext.BidderName("vendor2"): {}},
+		},
+	}
+
+	for _, tt := range tests {
+		config := append(baseConfig, tt.configVendorExceptions...)
+
+		v := viper.New()
+		SetupViper(v, "")
+		v.SetConfigType("yaml")
+		v.ReadConfig(bytes.NewBuffer(config))
+		cfg, err := New(v)
+		assert.NoError(t, err, "Setting up config error", tt.description)
+
+		assert.Equal(t, tt.wantVendorExceptions, cfg.GDPR.TCF2.SpecialFeature1.VendorExceptions, tt.description)
+		assert.Equal(t, tt.wantVendorExceptionsMap, cfg.GDPR.TCF2.SpecialFeature1.VendorExceptionMap, tt.description)
+	}
+}
diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go
index 62b0b604..d266bc38 100644
--- a/endpoints/openrtb2/amp_auction_test.go
+++ b/endpoints/openrtb2/amp_auction_test.go
@@ -1266,7 +1266,7 @@ func TestBuildAmpObject(t *testing.T) {
 
 	for _, test := range testCases {
 		// Set up test, declare a new mock logger every time
-		actualAmpObject, endpoint := AmpObjectTestSetup(t, test.inTagId, test.inStoredRequest, false)
+		actualAmpObject, endpoint := ampObjectTestSetup(t, test.inTagId, test.inStoredRequest, false)
 		// Run test
 		endpoint(recorder, request, nil)
 
@@ -1332,13 +1332,13 @@ func TestIdGeneration(t *testing.T) {
 
 	for _, test := range testCases {
 		// Set up and run test
-		actualAmpObject, endpoint := AmpObjectTestSetup(t, "test", test.givenInStoredRequest, test.givenGenerateRequestID)
+		actualAmpObject, endpoint := ampObjectTestSetup(t, "test", test.givenInStoredRequest, test.givenGenerateRequestID)
 		endpoint(recorder, request, nil)
 		assert.Equalf(t, test.expectedID, actualAmpObject.Request.ID, "Bid Request ID is incorrect: %s\n", test.description)
 	}
 }
 
-func AmpObjectTestSetup(t *testing.T, inTagId string, inStoredRequest json.RawMessage, generateRequestID bool) (*analytics.AmpObject, httprouter.Handle) {
+func ampObjectTestSetup(t *testing.T, inTagId string, inStoredRequest json.RawMessage, generateRequestID bool) (*analytics.AmpObject, httprouter.Handle) {
 	actualAmpObject := analytics.AmpObject{}
 	logger := newMockLogger(&actualAmpObject)
 
@@ -1363,3 +1363,65 @@ func AmpObjectTestSetup(t *testing.T, inTagId string, inStoredRequest json.RawMe
 	)
 	return &actualAmpObject, endpoint
 }
+
+func TestAmpAuctionResponseHeaders(t *testing.T) {
+	testCases := []struct {
+		description         string
+		requestURLArguments string
+		expectedStatus      int
+		expectedHeaders     func(http.Header)
+	}{
+		{
+			description:         "Success Response",
+			requestURLArguments: "?tag_id=1&__amp_source_origin=foo",
+			expectedStatus:      200,
+			expectedHeaders: func(h http.Header) {
+				h.Set("AMP-Access-Control-Allow-Source-Origin", "foo")
+				h.Set("Access-Control-Expose-Headers", "AMP-Access-Control-Allow-Source-Origin")
+				h.Set("X-Prebid", "pbs-go/unknown")
+				h.Set("Content-Type", "text/plain; charset=utf-8")
+			},
+		},
+		{
+			description:         "Failure Response",
+			requestURLArguments: "?tag_id=invalid&__amp_source_origin=foo",
+			expectedStatus:      400,
+			expectedHeaders: func(h http.Header) {
+				h.Set("AMP-Access-Control-Allow-Source-Origin", "foo")
+				h.Set("Access-Control-Expose-Headers", "AMP-Access-Control-Allow-Source-Origin")
+				h.Set("X-Prebid", "pbs-go/unknown")
+			},
+		},
+	}
+
+	storedRequests := map[string]json.RawMessage{
+		"1": json.RawMessage(validRequest(t, "site.json")),
+	}
+	exchange := &nobidExchange{}
+	endpoint, _ := NewAmpEndpoint(
+		fakeUUIDGenerator{},
+		exchange,
+		newParamsValidator(t),
+		&mockAmpStoredReqFetcher{storedRequests},
+		empty_fetcher.EmptyFetcher{},
+		&config.Configuration{MaxRequestSize: maxSize},
+		&metricsConfig.NilMetricsEngine{},
+		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		map[string]string{},
+		[]byte{},
+		openrtb_ext.BuildBidderMap(),
+	)
+
+	for _, test := range testCases {
+		httpReq := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp"+test.requestURLArguments), nil)
+		recorder := httptest.NewRecorder()
+
+		endpoint(recorder, httpReq, nil)
+
+		expectedHeaders := http.Header{}
+		test.expectedHeaders(expectedHeaders)
+
+		assert.Equal(t, test.expectedStatus, recorder.Result().StatusCode, test.description+":statuscode")
+		assert.Equal(t, expectedHeaders, recorder.Result().Header, test.description+":statuscode")
+	}
+}
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index c42580fc..9b2bf1dd 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -3577,6 +3577,61 @@ func TestValidateNativeAssetData(t *testing.T) {
 	}
 }
 
+func TestAuctionResponseHeaders(t *testing.T) {
+	testCases := []struct {
+		description     string
+		requestBody     string
+		expectedStatus  int
+		expectedHeaders func(http.Header)
+	}{
+		{
+			description:    "Success Response",
+			requestBody:    validRequest(t, "site.json"),
+			expectedStatus: 200,
+			expectedHeaders: func(h http.Header) {
+				h.Set("X-Prebid", "pbs-go/unknown")
+				h.Set("Content-Type", "application/json")
+			},
+		},
+		{
+			description:    "Failure Response",
+			requestBody:    "{}",
+			expectedStatus: 400,
+			expectedHeaders: func(h http.Header) {
+				h.Set("X-Prebid", "pbs-go/unknown")
+			},
+		},
+	}
+
+	exchange := &nobidExchange{}
+	endpoint, _ := NewEndpoint(
+		fakeUUIDGenerator{},
+		exchange,
+		newParamsValidator(t),
+		empty_fetcher.EmptyFetcher{},
+		empty_fetcher.EmptyFetcher{},
+		&config.Configuration{MaxRequestSize: maxSize},
+		&metricsConfig.NilMetricsEngine{},
+		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		map[string]string{},
+		[]byte{},
+		openrtb_ext.BuildBidderMap(),
+		empty_fetcher.EmptyFetcher{})
+
+	for _, test := range testCases {
+		httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(test.requestBody))
+		recorder := httptest.NewRecorder()
+
+		endpoint(recorder, httpReq, nil)
+
+		expectedHeaders := http.Header{}
+		test.expectedHeaders(expectedHeaders)
+
+		assert.Equal(t, test.expectedStatus, recorder.Result().StatusCode, test.description+":statuscode")
+		assert.Equal(t, expectedHeaders, recorder.Result().Header, test.description+":statuscode")
+	}
+}
+
 // warningsCheckExchange is a well-behaved exchange which stores all incoming warnings.
 type warningsCheckExchange struct {
 	auctionRequest exchange.AuctionRequest
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index 91512a2e..c4d8602a 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -1,7 +1,6 @@
 package openrtb2
 
 import (
-	"bytes"
 	"context"
 	"encoding/json"
 	"errors"
@@ -29,11 +28,7 @@ import (
 
 func TestVideoEndpointImpressionsNumber(t *testing.T) {
 	ex := &mockExchangeVideo{}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
-	reqBody := string(getRequestPayload(t, reqData))
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
 	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
 	recorder := httptest.NewRecorder()
 
@@ -62,16 +57,11 @@ func TestVideoEndpointImpressionsNumber(t *testing.T) {
 	assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response")
 
 	assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s", "Incorrect number of Ad Pods in response")
-
 }
 
 func TestVideoEndpointImpressionsDuration(t *testing.T) {
 	ex := &mockExchangeVideo{}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_different_durations.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
-	reqBody := string(getRequestPayload(t, reqData))
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_different_durations.json")
 	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
 	recorder := httptest.NewRecorder()
 
@@ -102,7 +92,6 @@ func TestVideoEndpointImpressionsDuration(t *testing.T) {
 	assert.Equal(t, ex.lastRequest.Imp[17].ID, "2_5", "Incorrect impression id in request")
 	assert.Equal(t, ex.lastRequest.Imp[17].Video.MaxDuration, int64(30), "Incorrect impression max duration in request")
 	assert.Equal(t, ex.lastRequest.Imp[17].Video.MinDuration, int64(30), "Incorrect impression min duration in request")
-
 }
 
 func TestCreateBidExtension(t *testing.T) {
@@ -143,7 +132,6 @@ func TestCreateBidExtension(t *testing.T) {
 	}
 	assert.Equal(t, resExt.Prebid.Targeting.DurationRangeSec, durationRange, "Duration range seconds is incorrect")
 	assert.Equal(t, resExt.Prebid.Targeting.PriceGranularity.Ranges, priceGranRanges, "Price granularity is incorrect")
-
 }
 
 func TestCreateBidExtensionExactDurTrueNoPriceRange(t *testing.T) {
@@ -183,11 +171,7 @@ func TestVideoEndpointDebugQueryTrue(t *testing.T) {
 	ex := &mockExchangeVideo{
 		cache: &mockCacheClient{},
 	}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
-	reqBody := string(getRequestPayload(t, reqData))
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
 	req := httptest.NewRequest("POST", "/openrtb2/video?debug=true", strings.NewReader(reqBody))
 	recorder := httptest.NewRecorder()
 
@@ -225,11 +209,7 @@ func TestVideoEndpointDebugQueryFalse(t *testing.T) {
 	ex := &mockExchangeVideo{
 		cache: &mockCacheClient{},
 	}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
-	reqBody := string(getRequestPayload(t, reqData))
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
 	req := httptest.NewRequest("POST", "/openrtb2/video?debug=123", strings.NewReader(reqBody))
 	recorder := httptest.NewRecorder()
 
@@ -267,11 +247,7 @@ func TestVideoEndpointDebugError(t *testing.T) {
 	ex := &mockExchangeVideo{
 		cache: &mockCacheClient{},
 	}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_invalid_sample.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
-	reqBody := string(getRequestPayload(t, reqData))
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_invalid_sample.json")
 	req := httptest.NewRequest("POST", "/openrtb2/video?debug=true", strings.NewReader(reqBody))
 	recorder := httptest.NewRecorder()
 
@@ -289,11 +265,7 @@ func TestVideoEndpointDebugNoAdPods(t *testing.T) {
 	ex := &mockExchangeVideoNoBids{
 		cache: &mockCacheClient{},
 	}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
-	reqBody := string(getRequestPayload(t, reqData))
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
 	req := httptest.NewRequest("POST", "/openrtb2/video?debug=true", strings.NewReader(reqBody))
 	recorder := httptest.NewRecorder()
 
@@ -323,11 +295,7 @@ func TestVideoEndpointDebugNoAdPods(t *testing.T) {
 
 func TestVideoEndpointNoPods(t *testing.T) {
 	ex := &mockExchangeVideo{}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_invalid_sample.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
-	reqBody := string(getRequestPayload(t, reqData))
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_invalid_sample.json")
 	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
 	recorder := httptest.NewRecorder()
 
@@ -877,11 +845,7 @@ func TestHandleError(t *testing.T) {
 
 func TestHandleErrorMetrics(t *testing.T) {
 	ex := &mockExchangeVideo{}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_invalid_sample.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
-	reqBody := string(getRequestPayload(t, reqData))
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_invalid_sample.json")
 	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
 	recorder := httptest.NewRecorder()
 
@@ -899,87 +863,64 @@ func TestHandleErrorMetrics(t *testing.T) {
 
 func TestParseVideoRequestWithUserAgentAndHeader(t *testing.T) {
 	ex := &mockExchangeVideo{}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_with_device_user_agent.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_with_device_user_agent.json")
 	headers := http.Header{}
 	headers.Add("User-Agent", "TestHeader")
 
 	deps := mockDeps(t, ex)
-	reqBody := string(getRequestPayload(t, reqData))
 	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
 
 	assert.Equal(t, "TestHeaderSample", req.Device.UA, "Header should be taken from original request")
 	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
 	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
-
 }
 
 func TestParseVideoRequestWithUserAgentAndEmptyHeader(t *testing.T) {
 	ex := &mockExchangeVideo{}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_with_device_user_agent.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_with_device_user_agent.json")
 
 	headers := http.Header{}
 
 	deps := mockDeps(t, ex)
-	reqBody := string(getRequestPayload(t, reqData))
 	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
 
 	assert.Equal(t, "TestHeaderSample", req.Device.UA, "Header should be taken from original request")
 	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
 	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
-
 }
 
 func TestParseVideoRequestWithoutUserAgentWithHeader(t *testing.T) {
 	ex := &mockExchangeVideo{}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_without_device_user_agent.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
-
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_without_device_user_agent.json")
 	headers := http.Header{}
 	headers.Add("User-Agent", "TestHeader")
 
 	deps := mockDeps(t, ex)
-	reqBody := string(getRequestPayload(t, reqData))
 	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
 
 	assert.Equal(t, "TestHeader", req.Device.UA, "Device.ua should be taken from request header")
 	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
 	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
-
 }
 
 func TestParseVideoRequestWithoutUserAgentAndEmptyHeader(t *testing.T) {
 	ex := &mockExchangeVideo{}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_without_device_user_agent.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_without_device_user_agent.json")
 
 	headers := http.Header{}
 
 	deps := mockDeps(t, ex)
-	reqBody := string(getRequestPayload(t, reqData))
+
 	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
 
 	assert.Equal(t, "", req.Device.UA, "Device.ua should be empty")
 	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
 	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
-
 }
 
 func TestParseVideoRequestWithEncodedUserAgentInHeader(t *testing.T) {
 	ex := &mockExchangeVideo{}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_without_device_user_agent.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_without_device_user_agent.json")
 
 	uaEncoded := "Mozilla%2F5.0%20%28Macintosh%3B%20Intel%20Mac%20OS%20X%2010_14_6%29%20AppleWebKit%2F537.36%20%28KHTML%2C%20like%20Gecko%29%20Chrome%2F78.0.3904.87%20Safari%2F537.36"
 	uaDecoded := "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"
@@ -988,21 +929,16 @@ func TestParseVideoRequestWithEncodedUserAgentInHeader(t *testing.T) {
 	headers.Add("User-Agent", uaEncoded)
 
 	deps := mockDeps(t, ex)
-	reqBody := string(getRequestPayload(t, reqData))
 	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
 
 	assert.Equal(t, uaDecoded, req.Device.UA, "Device.ua should be taken from request header")
 	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
 	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
-
 }
 
 func TestParseVideoRequestWithDecodedUserAgentInHeader(t *testing.T) {
 	ex := &mockExchangeVideo{}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_without_device_user_agent.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_without_device_user_agent.json")
 
 	uaDecoded := "Mozilla/5.0+(Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"
 
@@ -1010,13 +946,11 @@ func TestParseVideoRequestWithDecodedUserAgentInHeader(t *testing.T) {
 	headers.Add("User-Agent", uaDecoded)
 
 	deps := mockDeps(t, ex)
-	reqBody := string(getRequestPayload(t, reqData))
 	req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers)
 
 	assert.Equal(t, uaDecoded, req.Device.UA, "Device.ua should be taken from request header")
 	assert.Equal(t, []error(nil), valErr, "No validation errors should be returned")
 	assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned")
-
 }
 
 func TestHandleErrorDebugLog(t *testing.T) {
@@ -1111,15 +1045,10 @@ func TestCCPA(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		// Load Test Request
-		requestContainerBytes, err := ioutil.ReadFile(test.testFilePath)
-		if err != nil {
-			t.Fatalf("%s: Failed to fetch a valid request: %v", test.description, err)
-		}
-		requestBytes := getRequestPayload(t, requestContainerBytes)
+		reqBody := readVideoTestFile(t, test.testFilePath)
 
 		// Create HTTP Request + Response Recorder
-		httpRequest := httptest.NewRequest("POST", "/openrtb2/video", bytes.NewReader(requestBytes))
+		httpRequest := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
 		httpResponseRecorder := httptest.NewRecorder()
 
 		// Run Test
@@ -1132,7 +1061,7 @@ func TestCCPA(t *testing.T) {
 			t.Fatalf("%s: The request never made it into the exchange.", test.description)
 		}
 		extRegs := &openrtb_ext.ExtRegs{}
-		if err = json.Unmarshal(ex.lastRequest.Regs.Ext, extRegs); err != nil {
+		if err := json.Unmarshal(ex.lastRequest.Regs.Ext, extRegs); err != nil {
 			t.Fatalf("%s: Failed to unmarshal reg.ext in request to the exchange: %v", test.description, err)
 		}
 		if test.expectConsentString {
@@ -1154,11 +1083,7 @@ func TestCCPA(t *testing.T) {
 
 func TestVideoEndpointAppendBidderNames(t *testing.T) {
 	ex := &mockExchangeAppendBidderNames{}
-	reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_appendbiddernames.json")
-	if err != nil {
-		t.Fatalf("Failed to fetch a valid request: %v", err)
-	}
-	reqBody := string(getRequestPayload(t, reqData))
+	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_appendbiddernames.json")
 	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
 	recorder := httptest.NewRecorder()
 
@@ -1191,7 +1116,6 @@ func TestVideoEndpointAppendBidderNames(t *testing.T) {
 	assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response")
 
 	assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s_appnexus", "Incorrect number of Ad Pods in response")
-
 }
 
 func TestFormatTargetingKey(t *testing.T) {
@@ -1204,6 +1128,50 @@ func TestFormatTargetingKeyLongKey(t *testing.T) {
 	assert.Equal(t, "hb_pb_20.00", res, "Tergeting key constructed incorrectly")
 }
 
+func TestVideoAuctionResponseHeaders(t *testing.T) {
+	testCases := []struct {
+		description     string
+		givenTestFile   string
+		expectedStatus  int
+		expectedHeaders func(http.Header)
+	}{
+		{
+			description:    "Success Response",
+			givenTestFile:  "sample-requests/video/video_valid_sample.json",
+			expectedStatus: 200,
+			expectedHeaders: func(h http.Header) {
+				h.Set("X-Prebid", "pbs-go/unknown")
+				h.Set("Content-Type", "application/json")
+			},
+		}, {
+			description:    "Failure Response",
+			givenTestFile:  "sample-requests/video/video_invalid_sample.json",
+			expectedStatus: 500,
+			expectedHeaders: func(h http.Header) {
+				h.Set("X-Prebid", "pbs-go/unknown")
+			},
+		},
+	}
+
+	exchange := &mockExchangeVideo{}
+	endpoint := mockDeps(t, exchange)
+
+	for _, test := range testCases {
+		requestBody := readVideoTestFile(t, test.givenTestFile)
+
+		httpReq := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(requestBody))
+		recorder := httptest.NewRecorder()
+
+		endpoint.VideoAuctionEndpoint(recorder, httpReq, nil)
+
+		expectedHeaders := http.Header{}
+		test.expectedHeaders(expectedHeaders)
+
+		assert.Equal(t, test.expectedStatus, recorder.Result().StatusCode, test.description+":statuscode")
+		assert.Equal(t, expectedHeaders, recorder.Result().Header, test.description+":statuscode")
+	}
+}
+
 func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *metrics.Metrics, *mockAnalyticsModule) {
 	mockModule := &mockAnalyticsModule{}
 
@@ -1253,7 +1221,7 @@ func (m *mockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject) { return }
 func (m *mockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent) { return }
 
 func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
-	deps := &endpointDeps{
+	return &endpointDeps{
 		fakeUUIDGenerator{},
 		ex,
 		newParamsValidator(t),
@@ -1272,8 +1240,6 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 		hardcodedResponseIPValidator{response: true},
 		empty_fetcher.EmptyFetcher{},
 	}
-
-	return deps
 }
 
 func mockDepsAppendBidderNames(t *testing.T, ex *mockExchangeAppendBidderNames) *endpointDeps {
@@ -1443,3 +1409,12 @@ var testVideoStoredImpData = map[string]json.RawMessage{
 var testVideoStoredRequestData = map[string]json.RawMessage{
 	"80ce30c53c16e6ede735f123ef6e32361bfc7b22": json.RawMessage(`{"accountid": "11223344", "site": {"page": "mygame.foo.com"}}`),
 }
+
+func readVideoTestFile(t *testing.T, filename string) string {
+	requestData, err := ioutil.ReadFile(filename)
+	if err != nil {
+		t.Fatalf("Failed to fetch a valid request: %v", err)
+	}
+
+	return string(getRequestPayload(t, requestData))
+}
diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go
index b3a05cdc..9f2c7b8f 100644
--- a/exchange/adapter_util_test.go
+++ b/exchange/adapter_util_test.go
@@ -238,6 +238,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) {
 			bidderInfos: map[string]config.BidderInfo{},
 			expected: map[string]string{
 				"lifestreet": `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`,
+				"adagio":     `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`,
 			},
 		},
 		{
@@ -245,6 +246,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) {
 			bidderInfos: map[string]config.BidderInfo{"appnexus": infoEnabled},
 			expected: map[string]string{
 				"lifestreet": `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`,
+				"adagio":     `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`,
 			},
 		},
 		{
@@ -252,6 +254,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) {
 			bidderInfos: map[string]config.BidderInfo{"appnexus": infoDisabled},
 			expected: map[string]string{
 				"lifestreet": `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`,
+				"adagio":     `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`,
 				"appnexus":   `Bidder "appnexus" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`,
 			},
 		},
@@ -260,6 +263,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) {
 			bidderInfos: map[string]config.BidderInfo{"appnexus": infoDisabled, "openx": infoEnabled},
 			expected: map[string]string{
 				"lifestreet": `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`,
+				"adagio":     `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`,
 				"appnexus":   `Bidder "appnexus" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`,
 			},
 		},
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index f67b6ffb..e250b65e 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -2513,120 +2513,6 @@ func TestRemoveUnpermissionedEidsEmptyValidations(t *testing.T) {
 	}
 }
 
-func TestBuildXPrebidHeader(t *testing.T) {
-	testCases := []struct {
-		description   string
-		version       string
-		requestExt    *openrtb_ext.ExtRequest
-		requestAppExt *openrtb_ext.ExtApp
-		result        string
-	}{
-		{
-			description: "No versions",
-			version:     "",
-			result:      "pbs-go/unknown",
-		},
-		{
-			description: "pbs",
-			version:     "test-version",
-			result:      "pbs-go/test-version",
-		},
-		{
-			description: "prebid.js",
-			version:     "test-version",
-			requestExt: &openrtb_ext.ExtRequest{
-				Prebid: openrtb_ext.ExtRequestPrebid{
-					Channel: &openrtb_ext.ExtRequestPrebidChannel{
-						Name:    "pbjs",
-						Version: "test-pbjs-version",
-					},
-				},
-			},
-			result: "pbs-go/test-version,pbjs/test-pbjs-version",
-		},
-		{
-			description: "unknown prebid.js",
-			version:     "test-version",
-			requestExt: &openrtb_ext.ExtRequest{
-				Prebid: openrtb_ext.ExtRequestPrebid{
-					Channel: &openrtb_ext.ExtRequestPrebidChannel{
-						Name: "pbjs",
-					},
-				},
-			},
-			result: "pbs-go/test-version,pbjs/unknown",
-		},
-		{
-			description: "channel without a name",
-			version:     "test-version",
-			requestExt: &openrtb_ext.ExtRequest{
-				Prebid: openrtb_ext.ExtRequestPrebid{
-					Channel: &openrtb_ext.ExtRequestPrebidChannel{
-						Version: "test-version",
-					},
-				},
-			},
-			result: "pbs-go/test-version",
-		},
-		{
-			description: "prebid-mobile",
-			version:     "test-version",
-			requestAppExt: &openrtb_ext.ExtApp{
-				Prebid: openrtb_ext.ExtAppPrebid{
-					Source:  "prebid-mobile",
-					Version: "test-prebid-mobile-version",
-				},
-			},
-			result: "pbs-go/test-version,prebid-mobile/test-prebid-mobile-version",
-		},
-		{
-			description: "app ext without a source",
-			version:     "test-version",
-			requestAppExt: &openrtb_ext.ExtApp{
-				Prebid: openrtb_ext.ExtAppPrebid{
-					Version: "test-version",
-				},
-			},
-			result: "pbs-go/test-version",
-		},
-		{
-			description: "Version found in both req.Ext and req.App.Ext",
-			version:     "test-version",
-			requestExt: &openrtb_ext.ExtRequest{
-				Prebid: openrtb_ext.ExtRequestPrebid{
-					Channel: &openrtb_ext.ExtRequestPrebidChannel{
-						Name:    "pbjs",
-						Version: "test-pbjs-version",
-					},
-				},
-			},
-			requestAppExt: &openrtb_ext.ExtApp{
-				Prebid: openrtb_ext.ExtAppPrebid{
-					Source:  "prebid-mobile",
-					Version: "test-prebid-mobile-version",
-				},
-			},
-			result: "pbs-go/test-version,pbjs/test-pbjs-version,prebid-mobile/test-prebid-mobile-version",
-		},
-	}
-
-	for _, test := range testCases {
-		req := &openrtb2.BidRequest{}
-		if test.requestExt != nil {
-			reqExt, err := json.Marshal(test.requestExt)
-			assert.NoError(t, err, test.description+":err marshalling reqExt")
-			req.Ext = reqExt
-		}
-		if test.requestAppExt != nil {
-			reqAppExt, err := json.Marshal(test.requestAppExt)
-			assert.NoError(t, err, test.description+":err marshalling reqAppExt")
-			req.App = &openrtb2.App{Ext: reqAppExt}
-		}
-		result := buildXPrebidHeader(req, test.version)
-		assert.Equal(t, test.result, result, test.description+":result")
-	}
-}
-
 func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) {
 	req := &openrtb2.BidRequest{
 		Site: &openrtb2.Site{},
diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go
index 3064a9fd..9bdf7f85 100644
--- a/gdpr/impl_test.go
+++ b/gdpr/impl_test.go
@@ -341,7 +341,7 @@ func buildVendorList34() vendorList {
 				ID:               6,
 				Purposes:         []int{1, 2, 4},
 				LegIntPurposes:   []int{7},
-				SpecialPurposes:  []int{1},
+				SpecialFeatures:  []int{1},
 				FlexiblePurposes: []int{1, 2, 4, 7},
 			},
 			"8": {
@@ -352,7 +352,7 @@ func buildVendorList34() vendorList {
 			"10": {
 				ID:              10,
 				Purposes:        []int{2, 4, 7},
-				SpecialPurposes: []int{1},
+				SpecialFeatures: []int{1},
 			},
 			"20": {
 				ID:               20,
@@ -384,7 +384,7 @@ func allPurposesEnabledPermissions() (perms permissionsImpl) {
 				Purpose8:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
 				Purpose9:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
 				Purpose10:       config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
-				SpecialPurpose1: config.TCF2Purpose{Enabled: true, EnforceVendors: true},
+				SpecialFeature1: config.TCF2SpecialFeature{Enforce: true},
 			},
 		},
 	}
@@ -431,7 +431,7 @@ func TestAllowActivitiesGeoAndID(t *testing.T) {
 		}),
 	}
 
-	// COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : full consents to purposes and vendors 2, 6, 8
+	// COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : full consents to purposes and vendors 2, 6, 8 and special feature 1 opt-in
 	testDefs := []testDef{
 		{
 			description: "Appnexus vendor test, insufficient purposes claimed",
@@ -536,7 +536,7 @@ func TestAllowActivitiesPubRestrict(t *testing.T) {
 	}
 
 	// COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA - vendors 1-10 legit interest only,
-	// Pub restriction on purpose 7, consent only ... no allowPI will pass, no Special purpose 1 consent
+	// Pub restriction on purpose 7, consent only ... no allowPI will pass, no special feature 1 consent
 	testDefs := []testDef{
 		{
 			description: "Appnexus vendor test, insufficient purposes claimed",
@@ -840,7 +840,7 @@ func TestAllowActivitiesVendorException(t *testing.T) {
 	testDefs := []struct {
 		description           string
 		p2VendorExceptionMap  map[openrtb_ext.BidderName]struct{}
-		sp1VendorExceptionMap map[openrtb_ext.BidderName]struct{}
+		sf1VendorExceptionMap map[openrtb_ext.BidderName]struct{}
 		bidder                openrtb_ext.BidderName
 		consent               string
 		allowBid              bool
@@ -859,7 +859,7 @@ func TestAllowActivitiesVendorException(t *testing.T) {
 		{
 			description:           "Bid/ID allowed by vendor exception - p2 enabled with p2 vendor exception, pub restricts none",
 			p2VendorExceptionMap:  map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
-			sp1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			sf1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 			bidder:                openrtb_ext.BidderAppnexus,
 			consent:               noPurposeOrVendorConsentAndPubRestrictsNone,
 			allowBid:              true,
@@ -867,9 +867,9 @@ func TestAllowActivitiesVendorException(t *testing.T) {
 			passID:                true,
 		},
 		{
-			description:           "Geo blocked - sp1 enabled but no consent",
+			description:           "Geo blocked - sf1 enabled but no consent",
 			p2VendorExceptionMap:  map[openrtb_ext.BidderName]struct{}{},
-			sp1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
+			sf1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 			bidder:                openrtb_ext.BidderAppnexus,
 			consent:               noPurposeOrVendorConsentAndPubRestrictsNone,
 			allowBid:              false,
@@ -877,9 +877,9 @@ func TestAllowActivitiesVendorException(t *testing.T) {
 			passID:                false,
 		},
 		{
-			description:           "Geo allowed by vendor exception - sp1 enabled with sp1 vendor exception",
+			description:           "Geo allowed by vendor exception - sf1 enabled with sf1 vendor exception",
 			p2VendorExceptionMap:  map[openrtb_ext.BidderName]struct{}{},
-			sp1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
+			sf1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}},
 			bidder:                openrtb_ext.BidderAppnexus,
 			consent:               noPurposeOrVendorConsentAndPubRestrictsNone,
 			allowBid:              false,
@@ -896,7 +896,7 @@ func TestAllowActivitiesVendorException(t *testing.T) {
 				TCF2: config.TCF2{
 					Enabled:         true,
 					Purpose2:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, VendorExceptionMap: td.p2VendorExceptionMap},
-					SpecialPurpose1: config.TCF2Purpose{Enabled: true, VendorExceptionMap: td.sp1VendorExceptionMap},
+					SpecialFeature1: config.TCF2SpecialFeature{Enforce: true, VendorExceptionMap: td.sf1VendorExceptionMap},
 				},
 			},
 			vendorIDs: map[openrtb_ext.BidderName]uint16{
diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go
index 95529e4e..a1a926f5 100644
--- a/gdpr/vendorlist-fetching_test.go
+++ b/gdpr/vendorlist-fetching_test.go
@@ -189,7 +189,7 @@ type vendor struct {
 	Purposes         []int  `json:"purposes"`
 	LegIntPurposes   []int  `json:"legIntPurposes"`
 	FlexiblePurposes []int  `json:"flexiblePurposes"`
-	SpecialPurposes  []int  `json:"specialPurposes"`
+	SpecialFeatures  []int  `json:"specialFeatures"`
 }
 
 func MarshalVendorList(vendorList vendorList) string {
diff --git a/version/xprebidheader_test.go b/version/xprebidheader_test.go
new file mode 100644
index 00000000..1529a8f7
--- /dev/null
+++ b/version/xprebidheader_test.go
@@ -0,0 +1,149 @@
+package version
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/stretchr/testify/assert"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestBuildXPrebidHeader(t *testing.T) {
+	testCases := []struct {
+		description string
+		version     string
+		result      string
+	}{
+		{
+			description: "No Version",
+			version:     "",
+			result:      "pbs-go/unknown",
+		},
+		{
+			description: "Version",
+			version:     "0.100.0",
+			result:      "pbs-go/0.100.0",
+		},
+	}
+
+	for _, test := range testCases {
+		result := BuildXPrebidHeader(test.version)
+		assert.Equal(t, test.result, result, test.description)
+	}
+}
+
+func TestBuildXPrebidHeaderForRequest(t *testing.T) {
+	testCases := []struct {
+		description   string
+		version       string
+		requestExt    *openrtb_ext.ExtRequest
+		requestAppExt *openrtb_ext.ExtApp
+		result        string
+	}{
+		{
+			description: "No versions",
+			version:     "",
+			result:      "pbs-go/unknown",
+		},
+		{
+			description: "pbs",
+			version:     "test-version",
+			result:      "pbs-go/test-version",
+		},
+		{
+			description: "prebid.js",
+			version:     "test-version",
+			requestExt: &openrtb_ext.ExtRequest{
+				Prebid: openrtb_ext.ExtRequestPrebid{
+					Channel: &openrtb_ext.ExtRequestPrebidChannel{
+						Name:    "pbjs",
+						Version: "test-pbjs-version",
+					},
+				},
+			},
+			result: "pbs-go/test-version,pbjs/test-pbjs-version",
+		},
+		{
+			description: "unknown prebid.js",
+			version:     "test-version",
+			requestExt: &openrtb_ext.ExtRequest{
+				Prebid: openrtb_ext.ExtRequestPrebid{
+					Channel: &openrtb_ext.ExtRequestPrebidChannel{
+						Name: "pbjs",
+					},
+				},
+			},
+			result: "pbs-go/test-version,pbjs/unknown",
+		},
+		{
+			description: "channel without a name",
+			version:     "test-version",
+			requestExt: &openrtb_ext.ExtRequest{
+				Prebid: openrtb_ext.ExtRequestPrebid{
+					Channel: &openrtb_ext.ExtRequestPrebidChannel{
+						Version: "test-version",
+					},
+				},
+			},
+			result: "pbs-go/test-version",
+		},
+		{
+			description: "prebid-mobile",
+			version:     "test-version",
+			requestAppExt: &openrtb_ext.ExtApp{
+				Prebid: openrtb_ext.ExtAppPrebid{
+					Source:  "prebid-mobile",
+					Version: "test-prebid-mobile-version",
+				},
+			},
+			result: "pbs-go/test-version,prebid-mobile/test-prebid-mobile-version",
+		},
+		{
+			description: "app ext without a source",
+			version:     "test-version",
+			requestAppExt: &openrtb_ext.ExtApp{
+				Prebid: openrtb_ext.ExtAppPrebid{
+					Version: "test-version",
+				},
+			},
+			result: "pbs-go/test-version",
+		},
+		{
+			description: "Version found in both req.Ext and req.App.Ext",
+			version:     "test-version",
+			requestExt: &openrtb_ext.ExtRequest{
+				Prebid: openrtb_ext.ExtRequestPrebid{
+					Channel: &openrtb_ext.ExtRequestPrebidChannel{
+						Name:    "pbjs",
+						Version: "test-pbjs-version",
+					},
+				},
+			},
+			requestAppExt: &openrtb_ext.ExtApp{
+				Prebid: openrtb_ext.ExtAppPrebid{
+					Source:  "prebid-mobile",
+					Version: "test-prebid-mobile-version",
+				},
+			},
+			result: "pbs-go/test-version,pbjs/test-pbjs-version,prebid-mobile/test-prebid-mobile-version",
+		},
+	}
+
+	for _, test := range testCases {
+		req := &openrtb2.BidRequest{}
+		if test.requestExt != nil {
+			reqExt, err := json.Marshal(test.requestExt)
+			assert.NoError(t, err, test.description+":err marshalling reqExt")
+			req.Ext = reqExt
+		}
+		if test.requestAppExt != nil {
+			reqAppExt, err := json.Marshal(test.requestAppExt)
+			assert.NoError(t, err, test.description+":err marshalling reqAppExt")
+			req.App = &openrtb2.App{Ext: reqAppExt}
+		}
+		result := BuildXPrebidHeaderForRequest(req, test.version)
+		assert.Equal(t, test.result, result, test.description+":result")
+	}
+}