Repository: prebid/prebid-server
Pull request #2195 (2022-03-21 20:25:17)

User Sync - Prefer IFrame Over Pixel
Implements https://github.com/prebid/prebid-server/issues/1971

SyntaxNode: Docs PR: https://github.com/prebid/prebid.github.io/pull/3656

Patch:
diff --git a/adapters/adyoulike/adyoulike.go b/adapters/adyoulike/adyoulike.go
index 17c42e4a..8537c54d 100644
--- a/adapters/adyoulike/adyoulike.go
+++ b/adapters/adyoulike/adyoulike.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"net/http"
+	"strings"
 
 	"github.com/buger/jsonparser"
 	"github.com/mxmCherry/openrtb/v15/openrtb2"
@@ -36,6 +37,23 @@ func (a *adapter) MakeRequests(
 	reqCopy := *openRTBRequest
 	reqCopy.Imp = []openrtb2.Imp{}
 	for ind, imp := range openRTBRequest.Imp {
+
+		// Check if imp comes with bid floor amount defined in a foreign currency
+		if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" {
+			// Convert to US dollars
+			convertedValue, err := reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD")
+			if err != nil {
+				return nil, []error{err}
+			}
+			// Update after conversion. All imp elements inside request.Imp are shallow copies
+			// therefore, their non-pointer values are not shared memory and are safe to modify.
+			imp.BidFloorCur = "USD"
+			imp.BidFloor = convertedValue
+		}
+
+		// Set the CUR of bid to USD after converting all floors
+		reqCopy.Cur = []string{"USD"}
+
 		reqCopy.Imp = append(reqCopy.Imp, imp)
 
 		tagID, err = jsonparser.GetString(reqCopy.Imp[ind].Ext, "bidder", "placement")
@@ -106,7 +124,8 @@ func (a *adapter) MakeBids(
 	}
 
 	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(openRTBRequest.Imp))
-	bidResponse.Currency = openRTBBidderResponse.Cur
+	bidResponse.Currency = "USD"
+
 	for _, seatBid := range openRTBBidderResponse.SeatBid {
 		for idx := range seatBid.Bid {
 			b := &adapters.TypedBid{
diff --git a/adapters/pubmatic/pubmatic.go b/adapters/pubmatic/pubmatic.go
index f855f917..79f07200 100644
--- a/adapters/pubmatic/pubmatic.go
+++ b/adapters/pubmatic/pubmatic.go
@@ -271,6 +271,14 @@ func parseImpressionObject(imp *openrtb2.Imp, extractWrapperExtFromImp, extractP
 		imp.Banner = bannerCopy
 	}
 
+	if pubmaticExt.Kadfloor != "" {
+		bidfloor, err := strconv.ParseFloat(strings.TrimSpace(pubmaticExt.Kadfloor), 64)
+		if err == nil {
+			//do not overwrite existing value if kadfloor is invalid
+			imp.BidFloor = bidfloor
+		}
+	}
+
 	extMap := make(map[string]interface{}, 0)
 	if pubmaticExt.Keywords != nil && len(pubmaticExt.Keywords) != 0 {
 		addKeywordsToExt(pubmaticExt.Keywords, extMap)
diff --git a/adapters/yahoossp/yahoossp.go b/adapters/yahoossp/yahoossp.go
index 652d57f9..f0cc23bf 100644
--- a/adapters/yahoossp/yahoossp.go
+++ b/adapters/yahoossp/yahoossp.go
@@ -117,14 +117,15 @@ func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest
 				}}
 			}
 
-			if openrtb_ext.BidTypeBanner != mediaTypeId {
-				//only banner is supported, anything else is ignored
+			if openrtb_ext.BidTypeBanner != mediaTypeId &&
+				openrtb_ext.BidTypeVideo != mediaTypeId {
+				//only banner and video are mediaTypeId, anything else is ignored
 				continue
 			}
 
 			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
 				Bid:     &bid,
-				BidType: openrtb_ext.BidTypeBanner,
+				BidType: mediaTypeId,
 			})
 		}
 	}
@@ -140,6 +141,8 @@ func getImpInfo(impId string, imps []openrtb2.Imp) (bool, openrtb_ext.BidType) {
 			exists = true
 			if imp.Banner != nil {
 				mediaType = openrtb_ext.BidTypeBanner
+			} else if imp.Video != nil {
+				mediaType = openrtb_ext.BidTypeVideo
 			}
 			break
 		}
@@ -156,13 +159,20 @@ func changeRequestForBidService(request *openrtb2.BidRequest, extension *openrtb
 		request.App.ID = extension.Dcn
 	}
 
-	if request.Imp[0].Banner == nil {
-		return nil
+	if request.Imp[0].Banner != nil {
+		banner := *request.Imp[0].Banner
+		request.Imp[0].Banner = &banner
+
+		err := validateBanner(&banner)
+		if err != nil {
+			return err
+		}
 	}
 
-	banner := *request.Imp[0].Banner
-	request.Imp[0].Banner = &banner
+	return nil
+}
 
+func validateBanner(banner *openrtb2.Banner) error {
 	if banner.W != nil && banner.H != nil {
 		if *banner.W == 0 || *banner.H == 0 {
 			return fmt.Errorf("Invalid sizes provided for Banner %dx%d", *banner.W, *banner.H)
diff --git a/analytics/event.go b/analytics/event.go
index 56b75769..0b0e65f0 100644
--- a/analytics/event.go
+++ b/analytics/event.go
@@ -5,8 +5,9 @@ type EventType string
 
 // Possible values of events Prebid Server can receive for an ad.
 const (
-	Win EventType = "win"
-	Imp EventType = "imp"
+	Win  EventType = "win"
+	Imp  EventType = "imp"
+	Vast EventType = "vast"
 )
 
 // ResponseFormat enumerates the values of a Prebid Server event.
@@ -19,6 +20,18 @@ const (
 	Image ResponseFormat = "i"
 )
 
+// VastType enumerates the values of vast type events Prebid Server can receive
+type VastType string
+
+// Possible value of VastType event prebid server can receive.
+const (
+	Start         VastType = "start"
+	FirstQuartile VastType = "firstQuartile"
+	MidPoint      VastType = "midPoint"
+	ThirdQuartile VastType = "thirdQuartile"
+	Complete      VastType = "complete"
+)
+
 // Analytics indicates if the notification event should be handled or not
 type Analytics string
 
@@ -36,4 +49,5 @@ type EventRequest struct {
 	Bidder      string         `json:"bidder,omitempty"`
 	Timestamp   int64          `json:"timestamp,omitempty"`
 	Integration string         `json:"integration,omitempty"`
+	VType       VastType       `json:"vtype,omitempty"`
 }
diff --git a/config/bidderinfo.go b/config/bidderinfo.go
index 536acf31..0892c0f1 100644
--- a/config/bidderinfo.go
+++ b/config/bidderinfo.go
@@ -51,11 +51,6 @@ type Syncer struct {
 	// as the key for consistency, but that is not enforced as a requirement.
 	Key string `yaml:"key" mapstructure:"key"`
 
-	// Default identifies which endpoint is preferred if both are allowed by the publisher. This is
-	// only required if there is more than one endpoint configured for the bidder. Valid values are
-	// `iframe` and `redirect`.
-	Default string `yaml:"default" mapstructure:"default"`
-
 	// Supports allows bidders to specify which user sync endpoints they support but which don't have
 	// good defaults. Host companies should contact the bidder for the endpoint configuration. Hosts
 	// may not override this value.
@@ -96,10 +91,6 @@ func (s *Syncer) Override(original *Syncer) *Syncer {
 		copy.Key = s.Key
 	}
 
-	if s.Default != "" {
-		copy.Default = s.Default
-	}
-
 	if original == nil {
 		copy.IFrame = s.IFrame.Override(nil)
 		copy.Redirect = s.Redirect.Override(nil)
diff --git a/endpoints/events/event.go b/endpoints/events/event.go
index fd47683a..8c9c252a 100644
--- a/endpoints/events/event.go
+++ b/endpoints/events/event.go
@@ -23,6 +23,7 @@ const (
 	// Required
 	TemplateUrl        = "%v/event?t=%v&b=%v&a=%v"
 	TypeParameter      = "t"
+	VTypeParameter     = "vtype"
 	BidIdParameter     = "b"
 	AccountIdParameter = "a"
 
@@ -144,6 +145,16 @@ func ParseEventRequest(r *http.Request) (*analytics.EventRequest, []error) {
 		errs = append(errs, err)
 	}
 
+	if event.Type == analytics.Vast {
+		if err := readVType(event, r); err != nil {
+			errs = append(errs, err)
+		}
+	} else {
+		if t := r.URL.Query().Get(VTypeParameter); t != "" {
+			errs = append(errs, &errortypes.BadInput{Message: "parameter 'vtype' is only required for t=vast"})
+		}
+	}
+
 	// validate bidid
 	if bidid, err := checkRequiredParameter(r, BidIdParameter); err != nil {
 		errs = append(errs, err)
@@ -261,11 +272,40 @@ func readType(er *analytics.EventRequest, httpRequest *http.Request) error {
 	case string(analytics.Win):
 		er.Type = analytics.Win
 		return nil
+	case string(analytics.Vast):
+		er.Type = analytics.Vast
+		return nil
 	default:
 		return &errortypes.BadInput{Message: fmt.Sprintf("unknown type: '%s'", t)}
 	}
 }
 
+// readVType validates analytics.EventRequest vtype
+func readVType(er *analytics.EventRequest, httpRequest *http.Request) error {
+	vtype, err := checkRequiredParameter(httpRequest, VTypeParameter)
+
+	if err != nil {
+		return err
+	}
+
+	switch vtype {
+	case string(analytics.Start):
+		er.VType = analytics.Start
+	case string(analytics.FirstQuartile):
+		er.VType = analytics.FirstQuartile
+	case string(analytics.MidPoint):
+		er.VType = analytics.MidPoint
+	case string(analytics.ThirdQuartile):
+		er.VType = analytics.ThirdQuartile
+	case string(analytics.Complete):
+		er.VType = analytics.Complete
+	default:
+		return &errortypes.BadInput{Message: fmt.Sprintf("unknown vtype: '%s'", vtype)}
+	}
+
+	return nil
+}
+
 // readFormat validates analytics.EventRequest format attribute
 func readFormat(er *analytics.EventRequest, httpRequest *http.Request) error {
 	f := httpRequest.URL.Query().Get(FormatParameter)
diff --git a/firstpartydata/first_party_data.go b/firstpartydata/first_party_data.go
index d1940448..78ab5076 100644
--- a/firstpartydata/first_party_data.go
+++ b/firstpartydata/first_party_data.go
@@ -221,20 +221,26 @@ func unmarshalJSONToInt64(input json.RawMessage) (int64, error) {
 	if err != nil {
 		return -1, err
 	}
-	resNum, err := num.Int64()
-	return resNum, err
+	result, err := num.Int64()
+	return result, err
 }
 
 func unmarshalJSONToString(input json.RawMessage) (string, error) {
-	var inputString string
-	err := json.Unmarshal(input, &inputString)
-	return inputString, err
+	var result string
+	err := json.Unmarshal(input, &result)
+	return result, err
+}
+
+func unmarshalJSONToData(input json.RawMessage) ([]openrtb2.Data, error) {
+	var result []openrtb2.Data
+	err := json.Unmarshal(input, &result)
+	return result, err
 }
 
 func unmarshalJSONToStringArray(input json.RawMessage) ([]string, error) {
-	var inputString []string
-	err := json.Unmarshal(input, &inputString)
-	return inputString, err
+	var result []string
+	err := json.Unmarshal(input, &result)
+	return result, err
 }
 
 //resolveExtension inserts remaining {site/app/user} attributes back to {site/app/user}.ext.data
@@ -294,6 +300,15 @@ func mergeUsers(original *openrtb2.User, fpdConfigUser map[string]json.RawMessag
 		delete(fpdConfigUser, yobKey)
 	}
 
+	if userData, present := fpdConfigUser[dataKey]; present {
+		newUserData, err := unmarshalJSONToData(userData)
+		if err != nil {
+			return newUser, err
+		}
+		newUser.Data = append(newUser.Data, newUserData...)
+		delete(fpdConfigUser, dataKey)
+	}
+
 	if len(fpdConfigUser) > 0 {
 		newUser.Ext, err = resolveExtension(fpdConfigUser, original.Ext)
 	}
diff --git a/openrtb_ext/imp_pubmatic.go b/openrtb_ext/imp_pubmatic.go
index cf5093a2..db96ca51 100644
--- a/openrtb_ext/imp_pubmatic.go
+++ b/openrtb_ext/imp_pubmatic.go
@@ -15,6 +15,7 @@ type ExtImpPubmatic struct {
 	PmZoneID    string                  `json:"pmzoneid"`
 	WrapExt     json.RawMessage         `json:"wrapper,omitempty"`
 	Keywords    []*ExtImpPubmaticKeyVal `json:"keywords,omitempty"`
+	Kadfloor    string                  `json:"kadfloor,omitempty"`
 }
 
 // ExtImpPubmaticKeyVal defines the contract for bidrequest.imp[i].ext.pubmatic.keywords[i]
diff --git a/static/bidder-info/adyoulike.yaml b/static/bidder-info/adyoulike.yaml
index 8fdca5e2..59711ff0 100644
--- a/static/bidder-info/adyoulike.yaml
+++ b/static/bidder-info/adyoulike.yaml
@@ -9,7 +9,6 @@ capabilities:
       - video
       - native
 userSync:
-  default: iframe
   iframe:
     url: "https://visitor.omnitagjs.com/visitor/isync?uid=19340f4f097d16f41f34fc0274981ca4&name=PrebidServer&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url={{.RedirectURL}}"
     userMacro: "[BUYER_USERID]"
diff --git a/static/bidder-info/apacdex.yaml b/static/bidder-info/apacdex.yaml
index 2e8cd08f..5ef24021 100644
--- a/static/bidder-info/apacdex.yaml
+++ b/static/bidder-info/apacdex.yaml
@@ -11,7 +11,6 @@ capabilities:
       - banner
       - video
 userSync:
-  default: iframe
   iframe:
     url: https://sync.quantumdex.io/usersync/pbs?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}
     userMacro: "[UID]"
diff --git a/static/bidder-info/openx.yaml b/static/bidder-info/openx.yaml
index 4b456014..96add482 100644
--- a/static/bidder-info/openx.yaml
+++ b/static/bidder-info/openx.yaml
@@ -12,7 +12,6 @@ capabilities:
       - video
 modifyingVastXmlAllowed: true
 userSync:
-  default: "iframe"
   iframe:
     url: "https://u.openx.net/w/1.0/cm?id=891039ac-a916-42bb-a651-4be9e3b201da&ph=a3aece0c-9e80-4316-8deb-faf804779bd1&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r={{.RedirectURL}}"
     userMacro: "{OPENX_ID}"
diff --git a/static/bidder-info/quantumdex.yaml b/static/bidder-info/quantumdex.yaml
index 2e8cd08f..5ef24021 100644
--- a/static/bidder-info/quantumdex.yaml
+++ b/static/bidder-info/quantumdex.yaml
@@ -11,7 +11,6 @@ capabilities:
       - banner
       - video
 userSync:
-  default: iframe
   iframe:
     url: https://sync.quantumdex.io/usersync/pbs?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}
     userMacro: "[UID]"
diff --git a/static/bidder-info/triplelift.yaml b/static/bidder-info/triplelift.yaml
index 84eea869..8a86428d 100644
--- a/static/bidder-info/triplelift.yaml
+++ b/static/bidder-info/triplelift.yaml
@@ -14,7 +14,6 @@ userSync:
   # Triplelift supports user syncing but requires configuration by the host as the RedirectURL domain must be allowlisted. 
   # Contact this bidder directly at the email address above to ask about enabling user sync.
   # 
-  default: iframe
   iframe:
     url: "https://eb2.3lift.com/sync?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir={{.RedirectURL}}"
     userMacro: $UID
diff --git a/static/bidder-info/triplelift_native.yaml b/static/bidder-info/triplelift_native.yaml
index dc60d572..96d49b32 100644
--- a/static/bidder-info/triplelift_native.yaml
+++ b/static/bidder-info/triplelift_native.yaml
@@ -12,7 +12,6 @@ userSync:
   # Triplelift supports user syncing but requires configuration by the host as the RedirectURL domain must be allowlisted. 
   # Contact this bidder directly at the email address above to ask about enabling user sync.
   # 
-  default: iframe
   iframe:
     url: "https://eb2.3lift.com/sync?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir={{.RedirectURL}}"
     userMacro: $UID
diff --git a/static/bidder-info/valueimpression.yaml b/static/bidder-info/valueimpression.yaml
index 2e8cd08f..5ef24021 100644
--- a/static/bidder-info/valueimpression.yaml
+++ b/static/bidder-info/valueimpression.yaml
@@ -11,7 +11,6 @@ capabilities:
       - banner
       - video
 userSync:
-  default: iframe
   iframe:
     url: https://sync.quantumdex.io/usersync/pbs?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}
     userMacro: "[UID]"
diff --git a/static/bidder-info/yahoossp.yaml b/static/bidder-info/yahoossp.yaml
index 06d0cb18..cefa603d 100644
--- a/static/bidder-info/yahoossp.yaml
+++ b/static/bidder-info/yahoossp.yaml
@@ -5,9 +5,11 @@ capabilities:
   app:
     mediaTypes:
       - banner
+      - video
   site:
     mediaTypes:
       - banner
+      - video
 userSync:
   # yahoossp supports user syncing, but requires configuration by the host. contact this
   # bidder directly at the email address in this file to ask about enabling user sync.
diff --git a/static/bidder-params/pubmatic.json b/static/bidder-params/pubmatic.json
index 5d41b9fc..a13b5906 100644
--- a/static/bidder-params/pubmatic.json
+++ b/static/bidder-params/pubmatic.json
@@ -16,6 +16,10 @@
 			"type": "string",
 			"description": "Comma separated zone id. Used im deal targeting & site section targeting. e.g drama,sport"
 		},
+		"kadfloor": {
+			"type": "string",
+			"description": "bid floor value set to imp.bidfloor"
+		},
 		"dctr": {
 			"type": "string",
 			"description": "Deals Custom Targeting, pipe separated key-value pairs e.g key1=V1,V2,V3|key2=v1|key3=v3,v5"
diff --git a/usersync/syncer.go b/usersync/syncer.go
index c944cf0b..c9ca39c2 100644
--- a/usersync/syncer.go
+++ b/usersync/syncer.go
@@ -60,9 +60,8 @@ const (
 
 var ErrSyncerEndpointRequired = errors.New("at least one endpoint (iframe and/or redirect) is required")
 var ErrSyncerKeyRequired = errors.New("key is required")
-var ErrSyncerDefaultSyncTypeRequired = errors.New("default sync type is required when more then one sync endpoint is configured")
 
-// NewSyncer creates a new Syncer from the provided configuration, or an error if macro substition
+// NewSyncer creates a new Syncer from the provided configuration, or return an error if macro substition
 // fails or an endpoint url is invalid.
 func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer) (Syncer, error) {
 	if syncerConfig.Key == "" {
@@ -74,14 +73,9 @@ func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer) (Syncer,
 	}
 
 	syncer := standardSyncer{
-		key:         syncerConfig.Key,
-		supportCORS: syncerConfig.SupportCORS != nil && *syncerConfig.SupportCORS,
-	}
-
-	if defaultSyncType, err := resolveDefaultSyncType(syncerConfig); err != nil {
-		return nil, err
-	} else {
-		syncer.defaultSyncType = defaultSyncType
+		key:             syncerConfig.Key,
+		defaultSyncType: resolveDefaultSyncType(syncerConfig),
+		supportCORS:     syncerConfig.SupportCORS != nil && *syncerConfig.SupportCORS,
 	}
 
 	if syncerConfig.IFrame != nil {
@@ -109,32 +103,11 @@ func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer) (Syncer,
 	return syncer, nil
 }
 
-func resolveDefaultSyncType(syncerConfig config.Syncer) (SyncType, error) {
-	if syncerConfig.Default == "" {
-		if syncerConfig.IFrame != nil && syncerConfig.Redirect != nil {
-			return SyncTypeUnknown, ErrSyncerDefaultSyncTypeRequired
-		} else if syncerConfig.IFrame != nil {
-			return SyncTypeIFrame, nil
-		} else {
-			return SyncTypeRedirect, nil
-		}
-	}
-
-	if syncType, err := SyncTypeParse(syncerConfig.Default); err == nil {
-		switch syncType {
-		case SyncTypeIFrame:
-			if syncerConfig.IFrame == nil {
-				return SyncTypeUnknown, errDefaultTypeMissingIFrame
-			}
-		case SyncTypeRedirect:
-			if syncerConfig.Redirect == nil {
-				return SyncTypeUnknown, errDefaultTypeMissingRedirect
-			}
-		}
-		return syncType, nil
+func resolveDefaultSyncType(syncerConfig config.Syncer) SyncType {
+	if syncerConfig.IFrame != nil {
+		return SyncTypeIFrame
 	}
-
-	return SyncTypeUnknown, fmt.Errorf("invalid default sync type '%s'", syncerConfig.Default)
+	return SyncTypeRedirect
 }
 
 // macro substitution regex
diff --git a/usersync/syncersbuilder.go b/usersync/syncersbuilder.go
index 95b3e989..4c4c4207 100644
--- a/usersync/syncersbuilder.go
+++ b/usersync/syncersbuilder.go
@@ -91,7 +91,7 @@ func shouldCreateSyncer(cfg config.BidderInfo) bool {
 
 	// a syncer may provide just a Supports field to provide hints to the host. we should only try to create a syncer
 	// if there is at least one non-Supports value populated.
-	return cfg.Syncer.Key != "" || cfg.Syncer.Default != "" || cfg.Syncer.IFrame != nil || cfg.Syncer.Redirect != nil || cfg.Syncer.SupportCORS != nil
+	return cfg.Syncer.Key != "" || cfg.Syncer.IFrame != nil || cfg.Syncer.Redirect != nil || cfg.Syncer.SupportCORS != nil
 }
 
 func chooseSyncerConfig(biddersSyncerConfig []namedSyncerConfig) (namedSyncerConfig, error) {
diff --git a/usersync/synctype.go b/usersync/synctype.go
index c9e59f36..5bacb9f0 100644
--- a/usersync/synctype.go
+++ b/usersync/synctype.go
@@ -1,10 +1,5 @@
 package usersync
 
-import (
-	"fmt"
-	"strings"
-)
-
 // SyncType specifies the mechanism used to perform a user sync.
 type SyncType string
 
@@ -21,19 +16,6 @@ const (
 	SyncTypeRedirect SyncType = "redirect"
 )
 
-// SyncTypeParse returns the SyncType parsed from a string, case insensitive.
-func SyncTypeParse(v string) (SyncType, error) {
-	if strings.EqualFold(v, string(SyncTypeIFrame)) {
-		return SyncTypeIFrame, nil
-	}
-
-	if strings.EqualFold(v, string(SyncTypeRedirect)) {
-		return SyncTypeRedirect, nil
-	}
-
-	return SyncTypeUnknown, fmt.Errorf("invalid sync type `%s`", v)
-}
-
 // SyncTypeFilter determines which sync types, if any, the bidder is permitted to use.
 type SyncTypeFilter struct {
 	IFrame   BidderFilter


Test patch:
diff --git a/adapters/adyoulike/adyoulike_test.go b/adapters/adyoulike/adyoulike_test.go
index 9ab689f3..e4d21a87 100644
--- a/adapters/adyoulike/adyoulike_test.go
+++ b/adapters/adyoulike/adyoulike_test.go
@@ -12,7 +12,8 @@ const testsBidderEndpoint = "https://localhost/bid/4"
 
 func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderAdyoulike, config.Adapter{
-		Endpoint: testsBidderEndpoint})
+		Endpoint: testsBidderEndpoint,
+	})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
diff --git a/adapters/adyoulike/adyouliketest/exemplary/currency-conversion.json b/adapters/adyoulike/adyouliketest/exemplary/currency-conversion.json
new file mode 100644
index 00000000..85e14fbe
--- /dev/null
+++ b/adapters/adyoulike/adyouliketest/exemplary/currency-conversion.json
@@ -0,0 +1,115 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "imp": [
+        {
+          "id": "banner-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "bidfloor": 1.00,
+          "bidfloorcur": "EUR",
+          "ext": {
+            "bidder": {
+              "placement": "12345"
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+          "currency": {
+            "rates": {
+              "EUR": {
+                "USD": 0.05
+              }
+            },
+            "usepbsrates": false
+          }
+        }
+      }
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "https://localhost/bid/4",
+          "body": {
+            "id": "test-request-id",
+            "cur": ["USD"],
+            "imp": [
+              {
+                "banner": {
+                  "format": [
+                    {
+                      "h": 250,
+                      "w": 300
+                    }
+                  ]
+                },
+                "bidfloor": 0.05,
+                "bidfloorcur": "USD",
+                "ext": {
+                  "bidder": {
+                    "placement": "12345"
+                  }
+                },
+                "id": "banner-imp-id",
+                "tagid": "12345"
+              }
+            ],
+            "ext": {
+              "prebid": {
+                "currency": {
+                  "rates": {
+                    "EUR": {
+                      "USD": 0.05
+                    }
+                  },
+                  "usepbsrates": false
+                }
+              }
+             }
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "id": "test-response-id",
+            "cur": "USD",
+            "seatbid": [{
+              "seat": "1",
+              "bid": [{
+                  "id":    "12340",
+                  "impid": "banner-imp-id",
+                  "price": 300,
+                  "adm": "%3C%3Fxml%20version%3D%221.0%22%20encod%2Fhtml%3E",
+                  "nurl": "http://example.com/winnoticeurl0"
+                }
+              ]
+            }]
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "bids": [
+          {
+            "bid": {
+              "id":    "12340",
+              "impid": "banner-imp-id",
+              "price": 300,
+              "nurl":  "http://example.com/winnoticeurl0",
+              "adm":   "%3C%3Fxml%20version%3D%221.0%22%20encod%2Fhtml%3E"
+            },
+            "type": "banner"
+          }
+        ]
+      }
+    ]
+  }
diff --git a/adapters/adyoulike/adyouliketest/exemplary/multiformat-impression.json b/adapters/adyoulike/adyouliketest/exemplary/multiformat-impression.json
index 2ae99c04..8e69fcf5 100644
--- a/adapters/adyoulike/adyouliketest/exemplary/multiformat-impression.json
+++ b/adapters/adyoulike/adyouliketest/exemplary/multiformat-impression.json
@@ -49,6 +49,7 @@
           "uri": "https://localhost/bid/4",
           "body": {
             "id": "test-request-id",
+            "cur": ["USD"],
             "imp": [
               {
                 "banner": {
diff --git a/adapters/adyoulike/adyouliketest/supplemental/currency-missing-conversion.json b/adapters/adyoulike/adyouliketest/supplemental/currency-missing-conversion.json
new file mode 100644
index 00000000..d7d65e43
--- /dev/null
+++ b/adapters/adyoulike/adyouliketest/supplemental/currency-missing-conversion.json
@@ -0,0 +1,43 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "imp": [
+        {
+          "id": "banner-imp-id",
+          "banner": {
+            "format": [
+              {
+                "w": 300,
+                "h": 250
+              }
+            ]
+          },
+          "bidfloor": 1.00,
+          "bidfloorcur": "JPY",
+          "ext": {
+            "bidder": {
+              "placement": "12345"
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+          "currency": {
+              "rates": {
+              "MXN": {
+                  "USD": 0.05
+              }
+              },
+              "usepbsrates": false
+          }
+        }
+      }
+    },
+    "expectedMakeRequestsErrors": [
+      {
+        "value": "Currency conversion rate not found: 'JPY' => 'USD'",
+        "comparison": "literal"
+      }
+    ]
+  }
diff --git a/adapters/adyoulike/adyouliketest/supplemental/invalid-bid-response.json b/adapters/adyoulike/adyouliketest/supplemental/invalid-bid-response.json
index bda8d3e6..01a80efd 100644
--- a/adapters/adyoulike/adyouliketest/supplemental/invalid-bid-response.json
+++ b/adapters/adyoulike/adyouliketest/supplemental/invalid-bid-response.json
@@ -26,6 +26,7 @@
           "uri": "https://localhost/bid/4",
           "body": {
             "id": "test-request-id",
+            "cur": ["USD"],
             "imp": [
               {
                 "banner": {
diff --git a/adapters/adyoulike/adyouliketest/supplemental/status-bad-request.json b/adapters/adyoulike/adyouliketest/supplemental/status-bad-request.json
index 83b5f361..5c4c2df3 100644
--- a/adapters/adyoulike/adyouliketest/supplemental/status-bad-request.json
+++ b/adapters/adyoulike/adyouliketest/supplemental/status-bad-request.json
@@ -26,6 +26,7 @@
           "uri": "https://localhost/bid/4",
           "body": {
             "id": "test-request-id",
+            "cur": ["USD"],
             "imp": [
               {
                 "banner": {
diff --git a/adapters/adyoulike/adyouliketest/supplemental/status-no-content.json b/adapters/adyoulike/adyouliketest/supplemental/status-no-content.json
index d7d7df19..c296128e 100644
--- a/adapters/adyoulike/adyouliketest/supplemental/status-no-content.json
+++ b/adapters/adyoulike/adyouliketest/supplemental/status-no-content.json
@@ -26,6 +26,7 @@
           "uri": "https://localhost/bid/4",
           "body": {
             "id": "test-request-id",
+            "cur": ["USD"],
             "imp": [
               {
                 "banner": {
diff --git a/adapters/adyoulike/adyouliketest/supplemental/status-service-unavailable.json b/adapters/adyoulike/adyouliketest/supplemental/status-service-unavailable.json
index 2b5872a1..445d01e2 100644
--- a/adapters/adyoulike/adyouliketest/supplemental/status-service-unavailable.json
+++ b/adapters/adyoulike/adyouliketest/supplemental/status-service-unavailable.json
@@ -26,6 +26,7 @@
           "uri": "https://localhost/bid/4",
           "body": {
             "id": "test-request-id",
+            "cur": ["USD"],
             "imp": [
               {
                 "banner": {
diff --git a/adapters/adyoulike/adyouliketest/supplemental/status-unknown.json b/adapters/adyoulike/adyouliketest/supplemental/status-unknown.json
index 0b9ce745..5177befd 100644
--- a/adapters/adyoulike/adyouliketest/supplemental/status-unknown.json
+++ b/adapters/adyoulike/adyouliketest/supplemental/status-unknown.json
@@ -26,6 +26,7 @@
           "uri": "https://localhost/bid/4",
           "body": {
             "id": "test-request-id",
+            "cur": ["USD"],
             "imp": [
               {
                 "banner": {
diff --git a/adapters/pubmatic/pubmatic_test.go b/adapters/pubmatic/pubmatic_test.go
index ac7dbdb7..da5f632e 100644
--- a/adapters/pubmatic/pubmatic_test.go
+++ b/adapters/pubmatic/pubmatic_test.go
@@ -1,11 +1,14 @@
 package pubmatic
 
 import (
+	"encoding/json"
 	"testing"
 
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
 	"github.com/prebid/prebid-server/adapters/adapterstest"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
 )
 
 func TestJsonSamples(t *testing.T) {
@@ -67,3 +70,83 @@ func TestGetBidTypeForUnsupportedCode(t *testing.T) {
 		t.Errorf("Expected Bid Type value was: %v, actual value is: %v", openrtb_ext.BidTypeBanner, actualBidTypeValue)
 	}
 }
+
+func TestParseImpressionObject(t *testing.T) {
+	type args struct {
+		imp                      *openrtb2.Imp
+		extractWrapperExtFromImp bool
+		extractPubIDFromImp      bool
+	}
+	tests := []struct {
+		name                string
+		args                args
+		expectedWrapperExt  *pubmaticWrapperExt
+		expectedPublisherId string
+		wantErr             bool
+		expectedBidfloor    float64
+	}{
+		{
+			name: "imp.bidfloor empty and kadfloor set",
+			args: args{
+				imp: &openrtb2.Imp{
+					Video: &openrtb2.Video{},
+					Ext:   json.RawMessage(`{"bidder":{"kadfloor":"0.12"}}`),
+				},
+			},
+			expectedBidfloor: 0.12,
+		},
+		{
+			name: "imp.bidfloor set and kadfloor empty",
+			args: args{
+				imp: &openrtb2.Imp{
+					BidFloor: 0.12,
+					Video:    &openrtb2.Video{},
+					Ext:      json.RawMessage(`{"bidder":{}}`),
+				},
+			},
+			expectedBidfloor: 0.12,
+		},
+		{
+			name: "imp.bidfloor set and kadfloor invalid",
+			args: args{
+				imp: &openrtb2.Imp{
+					BidFloor: 0.12,
+					Video:    &openrtb2.Video{},
+					Ext:      json.RawMessage(`{"bidder":{"kadfloor":"aaa"}}`),
+				},
+			},
+			expectedBidfloor: 0.12,
+		},
+		{
+			name: "imp.bidfloor set and kadfloor set, preference to kadfloor",
+			args: args{
+				imp: &openrtb2.Imp{
+					BidFloor: 0.12,
+					Video:    &openrtb2.Video{},
+					Ext:      json.RawMessage(`{"bidder":{"kadfloor":"0.11"}}`),
+				},
+			},
+			expectedBidfloor: 0.11,
+		},
+		{
+			name: "kadfloor string set with whitespace",
+			args: args{
+				imp: &openrtb2.Imp{
+					BidFloor: 0.12,
+					Video:    &openrtb2.Video{},
+					Ext:      json.RawMessage(`{"bidder":{"kadfloor":" \t  0.13  "}}`),
+				},
+			},
+			expectedBidfloor: 0.13,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			receivedWrapperExt, receivedPublisherId, err := parseImpressionObject(tt.args.imp, tt.args.extractWrapperExtFromImp, tt.args.extractPubIDFromImp)
+			assert.Equal(t, tt.wantErr, err != nil)
+			assert.Equal(t, tt.expectedWrapperExt, receivedWrapperExt)
+			assert.Equal(t, tt.expectedPublisherId, receivedPublisherId)
+			assert.Equal(t, tt.expectedBidfloor, tt.args.imp.BidFloor)
+		})
+	}
+}
diff --git a/adapters/pubmatic/pubmatictest/exemplary/banner.json b/adapters/pubmatic/pubmatictest/exemplary/banner.json
index f525673c..313099f2 100644
--- a/adapters/pubmatic/pubmatictest/exemplary/banner.json
+++ b/adapters/pubmatic/pubmatictest/exemplary/banner.json
@@ -22,6 +22,7 @@
                          "value": ["sports", "movies"]
                       }
                     ],
+                    "kadfloor": "0.12",
                     "wrapper": {
                         "version": 1,
                         "profile": 5123
@@ -56,10 +57,11 @@
                       "w": 300,
                       "h": 250
                     }
-                ],
-                "h": 250,
-                "w": 300
-            },
+                  ],
+                  "h": 250,
+                  "w": 300
+                },
+                "bidfloor": 0.12,
                 "ext": {
                     "pmZoneId": "Zone1,Zone2",
                     "preference": "sports,movies"
diff --git a/adapters/pubmatic/pubmatictest/exemplary/video.json b/adapters/pubmatic/pubmatictest/exemplary/video.json
index 1715e477..693ee3eb 100644
--- a/adapters/pubmatic/pubmatictest/exemplary/video.json
+++ b/adapters/pubmatic/pubmatictest/exemplary/video.json
@@ -28,6 +28,7 @@
                         "value": ["Zone1", "Zone2"]
                       }
                     ],
+                    "kadfloor": "0.12",
                     "wrapper": {
                         "version": 1,
                         "profile": 5123
@@ -72,6 +73,7 @@
                     "minbitrate": 10,
                     "maxbitrate": 10
                 },
+                "bidfloor": 0.12,
                 "ext": {
                     "pmZoneId": "Zone1,Zone2"
                 }
diff --git a/adapters/pubmatic/pubmatictest/supplemental/app.json b/adapters/pubmatic/pubmatictest/supplemental/app.json
index e9f1048e..c6ed97a5 100644
--- a/adapters/pubmatic/pubmatictest/supplemental/app.json
+++ b/adapters/pubmatic/pubmatictest/supplemental/app.json
@@ -22,6 +22,7 @@
                             "value": ["sports", "movies"]
                         }
                     ],
+                    "kadfloor": "0.12",
                     "wrapper": {
                         "version": 1,
                         "profile": 5123
@@ -56,10 +57,11 @@
                       "w": 300,
                       "h": 250
                     }
-                ],
-                "h": 250,
-                "w": 300
-            },
+                  ],
+                  "h": 250,
+                  "w": 300
+                },
+                "bidfloor": 0.12,
                 "ext": {
                     "pmZoneId": "Zone1,Zone2",
                     "preference": "sports,movies"
diff --git a/adapters/yahoossp/yahoossptest/exemplary/simple-video.json b/adapters/yahoossp/yahoossptest/exemplary/simple-video.json
new file mode 100644
index 00000000..d6227264
--- /dev/null
+++ b/adapters/yahoossp/yahoossptest/exemplary/simple-video.json
@@ -0,0 +1,132 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "w": 728, 
+          "h": 90,
+          "protocols": [2],
+          "placement": 1,
+          "startdelay": -2,
+          "playbackmethod": [2],
+          "mimes": ["foo", "bar"]
+        },
+        "ext": {
+          "bidder": {
+            "dcn": "dcn1",
+            "pos": "pos2"
+          }
+        }
+      }
+    ],
+    "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",
+      "publisher": {
+        "id": "1"
+      }
+    }
+  },
+
+  "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"
+          ]
+        },
+        "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",
+              "tagid": "pos2",
+              "video": {
+                "w": 728, 
+                "h": 90,
+                "protocols": [2],
+                "placement": 1,
+                "startdelay": -2,
+                "playbackmethod": [2],
+                "mimes": ["foo", "bar"]
+              },
+              "ext": {
+                "bidder": {
+                  "dcn": "dcn1",
+                  "pos": "pos2"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "dcn1",
+            "publisher": {
+              "id": "1"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "yahoossp",
+              "bid": [{
+                "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+                "impid": "test-imp-id",
+                "price": 0.500000,
+                "adm": "some-test-vast-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-vast-ad",
+            "crid": "crid_10",
+            "w": 728,
+            "h": 90
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/yahoossp/yahoossptest/supplemental/invalid-banner-height.json b/adapters/yahoossp/yahoossptest/supplemental/invalid-banner-height.json
new file mode 100644
index 00000000..da4f7918
--- /dev/null
+++ b/adapters/yahoossp/yahoossptest/supplemental/invalid-banner-height.json
@@ -0,0 +1,26 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "w": 728,
+          "h": 0
+        },
+        "ext": {
+          "bidder": {
+          }
+        }
+      }
+    ],
+    "site": {}
+  },
+
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Invalid sizes provided for Banner 728x0",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/yahoossp/yahoossptest/supplemental/non-banner-bids-ignored.json b/adapters/yahoossp/yahoossptest/supplemental/non-supported-requests-bids-ignored.json
similarity index 95%
rename from adapters/yahoossp/yahoossptest/supplemental/non-banner-bids-ignored.json
rename to adapters/yahoossp/yahoossptest/supplemental/non-supported-requests-bids-ignored.json
index 6f40e2a9..11a93f68 100644
--- a/adapters/yahoossp/yahoossptest/supplemental/non-banner-bids-ignored.json
+++ b/adapters/yahoossp/yahoossptest/supplemental/non-supported-requests-bids-ignored.json
@@ -4,8 +4,8 @@
     "imp": [
       {
         "id": "test-imp-id",
-        "video": {
-          "mimes": ["video/mp4"]
+        "audio": {
+          "mimes": ["audio/mp3"]
         },
         "ext": {
           "bidder": {
@@ -52,8 +52,8 @@
             {
               "id": "test-imp-id",
               "tagid": "pos2",
-              "video": {
-               "mimes": ["video/mp4"]
+              "audio": {
+               "mimes": ["audio/mp3"]
               },
               "ext": {
                 "bidder": {
diff --git a/adapters/yahoossp/yahoossptest/supplemental/simple-banner-ignore-width-when-height-missing.json b/adapters/yahoossp/yahoossptest/supplemental/simple-banner-ignore-width-when-height-missing.json
new file mode 100644
index 00000000..16e64c61
--- /dev/null
+++ b/adapters/yahoossp/yahoossptest/supplemental/simple-banner-ignore-width-when-height-missing.json
@@ -0,0 +1,118 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "format": [{"w": 728, "h": 90}]
+        },
+        "tagid": "123",
+        "ext": {
+          "bidder": {
+            "dcn": "dcn1",
+            "pos": "pos2"
+          }
+        }
+      }
+    ],
+    "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"
+          ]
+        },
+        "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",
+              "tagid": "pos2",
+              "banner": {
+                "format": [{"w": 728, "h": 90}],
+                "w": 728,
+                "h": 90
+              },
+              "ext": {
+                "bidder": {
+                  "dcn": "dcn1",
+                  "pos": "pos2"
+                }
+              }
+            }
+          ],
+          "site": {
+            "id": "dcn1"
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "yahoossp",
+              "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/config/bidderinfo_test.go b/config/bidderinfo_test.go
index 9cf5fe09..df5b8591 100644
--- a/config/bidderinfo_test.go
+++ b/config/bidderinfo_test.go
@@ -44,8 +44,7 @@ func TestLoadBidderInfoFromDisk(t *testing.T) {
 				},
 			},
 			Syncer: &Syncer{
-				Key:     "foo",
-				Default: "iframe",
+				Key: "foo",
 				IFrame: &SyncerEndpoint{
 					URL:         "https://foo.com/sync?mode=iframe&r={{.RedirectURL}}",
 					RedirectURL: "{{.ExternalURL}}/setuid/iframe",
@@ -208,12 +207,6 @@ func TestSyncerOverride(t *testing.T) {
 			givenOverride: &Syncer{Key: "override"},
 			expected:      &Syncer{Key: "override"},
 		},
-		{
-			description:   "Override Default",
-			givenOriginal: &Syncer{Default: "original"},
-			givenOverride: &Syncer{Default: "override"},
-			expected:      &Syncer{Default: "override"},
-		},
 		{
 			description:   "Override IFrame",
 			givenOriginal: &Syncer{IFrame: &SyncerEndpoint{URL: "original"}},
@@ -240,9 +233,9 @@ func TestSyncerOverride(t *testing.T) {
 		},
 		{
 			description:   "Override Partial - Other Fields Untouched",
-			givenOriginal: &Syncer{Key: "originalKey", Default: "originalDefault"},
-			givenOverride: &Syncer{Default: "overrideDefault"},
-			expected:      &Syncer{Key: "originalKey", Default: "overrideDefault"},
+			givenOriginal: &Syncer{Key: "originalKey", ExternalURL: "originalExternalURL"},
+			givenOverride: &Syncer{ExternalURL: "overrideExternalURL"},
+			expected:      &Syncer{Key: "originalKey", ExternalURL: "overrideExternalURL"},
 		},
 	}
 
diff --git a/endpoints/events/event_test.go b/endpoints/events/event_test.go
index 09ad6d57..f700c31e 100644
--- a/endpoints/events/event_test.go
+++ b/endpoints/events/event_test.go
@@ -14,6 +14,7 @@ import (
 
 	"github.com/prebid/prebid-server/analytics"
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/stored_requests"
 	"github.com/stretchr/testify/assert"
 )
@@ -645,6 +646,16 @@ func TestShouldParseEventCorrectly(t *testing.T) {
 				Analytics: analytics.Enabled,
 			},
 		},
+		"three - vtype = start": {
+			req: httptest.NewRequest("GET", "/event?t=vast&vtype=start&b=bidId&ts=0&a=accountId", strings.NewReader("")),
+			expected: &analytics.EventRequest{
+				Type:      analytics.Vast,
+				VType:     analytics.Start,
+				BidID:     "bidId",
+				Timestamp: 0,
+				Analytics: analytics.Enabled,
+			},
+		},
 	}
 
 	for name, test := range tests {
@@ -750,3 +761,147 @@ func TestReadIntegrationType(t *testing.T) {
 		}
 	}
 }
+
+func TestShouldReturnBadRequestWhenVTypeIsInvalid(t *testing.T) {
+
+	reqData := ""
+
+	tests := []struct {
+		description        string
+		req                *http.Request
+		expectedStatusCode int
+		expectedStatus     string
+	}{
+		{
+			description:        "vtype parameter is missing",
+			req:                httptest.NewRequest("GET", "/event?t=vast&b=bidID", strings.NewReader(reqData)),
+			expectedStatusCode: 400,
+			expectedStatus:     "invalid request: parameter 'vtype' is required\n",
+		},
+		{
+			description:        "invalid vtype parameter",
+			req:                httptest.NewRequest("GET", "/event?t=vast&vtype=abc&b=bidID", strings.NewReader(reqData)),
+			expectedStatusCode: 400,
+			expectedStatus:     "invalid request: unknown vtype: 'abc'\n",
+		},
+		{
+			description:        "vtype is passed when event != vast",
+			req:                httptest.NewRequest("GET", "/event?t=win&vtype=startc&b=bidID", strings.NewReader(reqData)),
+			expectedStatusCode: 400,
+			expectedStatus:     "invalid request: parameter 'vtype' is only required for t=vast\n",
+		},
+	}
+
+	for _, test := range tests {
+		mockAccountsFetcher := &mockAccountsFetcher{}
+
+		mockAnalyticsModule := &eventsMockAnalyticsModule{
+			Fail: false,
+		}
+
+		cfg := &config.Configuration{
+			AccountDefaults: config.Account{},
+		}
+
+		recorder := httptest.NewRecorder()
+
+		e := NewEventEndpoint(cfg, mockAccountsFetcher, mockAnalyticsModule)
+		e(recorder, test.req, nil)
+
+		d, err := ioutil.ReadAll(recorder.Result().Body)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		assert.Equal(t, test.expectedStatusCode, recorder.Result().StatusCode, test.description)
+		assert.Equal(t, test.expectedStatus, string(d), test.description)
+
+	}
+}
+
+func TestReadVType(t *testing.T) {
+	type args struct {
+		er  *analytics.EventRequest
+		req *http.Request
+	}
+	tests := []struct {
+		name          string
+		args          args
+		expectedError error
+		expectedVType analytics.VastType
+	}{
+		{
+			name: "vtype = start",
+			args: args{
+				er: &analytics.EventRequest{
+					Type: analytics.Vast,
+				},
+				req: httptest.NewRequest("GET", "/event?t=vast&vtype=start&b=bidId&ts=0&a=accountId", strings.NewReader("")),
+			},
+			expectedError: nil,
+			expectedVType: analytics.Start,
+		},
+		{
+			name: "vtype = firstQuartile",
+			args: args{
+				er: &analytics.EventRequest{
+					Type: analytics.Vast,
+				},
+				req: httptest.NewRequest("GET", "/event?t=vast&vtype=firstQuartile&b=bidId&ts=0&a=accountId", strings.NewReader("")),
+			},
+			expectedError: nil,
+			expectedVType: analytics.FirstQuartile,
+		},
+		{
+			name: "vtype = midPoint",
+			args: args{
+				er: &analytics.EventRequest{
+					Type: analytics.Vast,
+				},
+				req: httptest.NewRequest("GET", "/event?t=vast&vtype=midPoint&b=bidId&ts=0&a=accountId", strings.NewReader("")),
+			},
+			expectedError: nil,
+			expectedVType: analytics.MidPoint,
+		},
+		{
+			name: "vtype = thirdQuartile",
+			args: args{
+				er: &analytics.EventRequest{
+					Type: analytics.Vast,
+				},
+				req: httptest.NewRequest("GET", "/event?t=vast&vtype=thirdQuartile&b=bidId&ts=0&a=accountId", strings.NewReader("")),
+			},
+			expectedError: nil,
+			expectedVType: analytics.ThirdQuartile,
+		},
+		{
+			name: "vtype = complete",
+			args: args{
+				er: &analytics.EventRequest{
+					Type: analytics.Vast,
+				},
+				req: httptest.NewRequest("GET", "/event?t=vast&vtype=complete&b=bidId&ts=0&a=accountId", strings.NewReader("")),
+			},
+			expectedError: nil,
+			expectedVType: analytics.Complete,
+		},
+		{
+			name: "unknown vtype",
+			args: args{
+				er: &analytics.EventRequest{
+					Type: analytics.Vast,
+				},
+				req: httptest.NewRequest("GET", "/event?t=vast&vtype=test&b=bidId&ts=0&a=accountId", strings.NewReader("")),
+			},
+			expectedError: &errortypes.BadInput{Message: "unknown vtype: 'test'"},
+			expectedVType: "",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := readVType(tt.args.er, tt.args.req)
+			assert.Equal(t, tt.expectedError, err, tt.name)
+			assert.Equal(t, tt.expectedVType, tt.args.er.VType, tt.name)
+		})
+	}
+}
diff --git a/firstpartydata/first_party_data_test.go b/firstpartydata/first_party_data_test.go
index 1e09bab1..1e0dc015 100644
--- a/firstpartydata/first_party_data_test.go
+++ b/firstpartydata/first_party_data_test.go
@@ -762,7 +762,7 @@ func TestResolveUser(t *testing.T) {
 	fpdConfigUser[yobKey] = []byte(`1980`)
 	fpdConfigUser[genderKey] = []byte(`"M"`)
 	fpdConfigUser[keywordsKey] = []byte(`"fpdConfigUserKeywords"`)
-	fpdConfigUser["data"] = []byte(`[{"id":"UserDataId1", "name":"UserDataName1"}, {"id":"UserDataId2", "name":"UserDataName2"}]`)
+	fpdConfigUser[dataKey] = []byte(`[{"id":"UserDataId1", "name":"UserDataName1"}, {"id":"UserDataId2", "name":"UserDataName2"}]`)
 	fpdConfigUser["ext"] = []byte(`{"data":{"fpdConfigUserExt": 123}}`)
 
 	bidRequestUser := &openrtb2.User{
@@ -789,6 +789,8 @@ func TestResolveUser(t *testing.T) {
 		Data: []openrtb2.Data{
 			{ID: "openRtbGlobalFPDUserDataId1", Name: "openRtbGlobalFPDUserDataName1"},
 			{ID: "openRtbGlobalFPDUserDataId2", Name: "openRtbGlobalFPDUserDataName2"},
+			{ID: "UserDataId1", Name: "UserDataName1"},
+			{ID: "UserDataId2", Name: "UserDataName2"},
 		},
 	}
 
@@ -801,10 +803,6 @@ func TestResolveUser(t *testing.T) {
 			description:       "bid request user.ext is nil",
 			bidRequestUserExt: nil,
 			expectedUserExt: `{"data":{
-									"data":[
-										{"id":"UserDataId1","name":"UserDataName1"},
-										{"id":"UserDataId2","name":"UserDataName2"}
-									],
 									"fpdConfigUserExt":123,
 									"globalFPDUserData":"globalFPDUserDataValue",
 									"id":"fpdConfigUserId"
@@ -815,10 +813,6 @@ func TestResolveUser(t *testing.T) {
 			description:       "bid request user.ext is not nil",
 			bidRequestUserExt: []byte(`{"bidRequestUserExt": 1234}`),
 			expectedUserExt: `{"data":{
-									"data":[
-										{"id":"UserDataId1","name":"UserDataName1"},
-										{"id":"UserDataId2","name":"UserDataName2"}
-									],
 									"fpdConfigUserExt":123,
 									"globalFPDUserData":"globalFPDUserDataValue",
 									"id":"fpdConfigUserId"
@@ -836,7 +830,7 @@ func TestResolveUser(t *testing.T) {
 		fpdConfigUser[yobKey] = []byte(`1980`)
 		fpdConfigUser[genderKey] = []byte(`"M"`)
 		fpdConfigUser[keywordsKey] = []byte(`"fpdConfigUserKeywords"`)
-		fpdConfigUser["data"] = []byte(`[{"id":"UserDataId1", "name":"UserDataName1"}, {"id":"UserDataId2", "name":"UserDataName2"}]`)
+		fpdConfigUser[dataKey] = []byte(`[{"id":"UserDataId1", "name":"UserDataName1"}, {"id":"UserDataId2", "name":"UserDataName2"}]`)
 		fpdConfigUser["ext"] = []byte(`{"data":{"fpdConfigUserExt": 123}}`)
 		fpdConfig := &openrtb_ext.ORTB2{User: fpdConfigUser}
 
@@ -885,7 +879,7 @@ func TestMergeUsers(t *testing.T) {
 	fpdConfigUser[yobKey] = []byte(`1980`)
 	fpdConfigUser[genderKey] = []byte(`"M"`)
 	fpdConfigUser[keywordsKey] = []byte(`"fpdConfigUserKeywords"`)
-	fpdConfigUser["data"] = []byte(`[{"id":"UserDataId1", "name":"UserDataName1"}, {"id":"UserDataId2", "name":"UserDataName2"}]`)
+	fpdConfigUser[dataKey] = []byte(`[{"id":"UserDataId1", "name":"UserDataName1"}, {"id":"UserDataId2", "name":"UserDataName2"}]`)
 	fpdConfigUser["ext"] = []byte(`{"data":{"fpdConfigUserExt": 123}}`)
 
 	resultUser, err := mergeUsers(originalUser, fpdConfigUser)
@@ -893,9 +887,6 @@ func TestMergeUsers(t *testing.T) {
 
 	expectedUserExt := `{"bidRequestUserExt":1234,
 						 "data":{
-							"data":[
-								{"id":"UserDataId1","name":"UserDataName1"},
-								{"id":"UserDataId2","name":"UserDataName2"}],
 							"fpdConfigUserExt":123,
 							"id":"fpdConfigUserId"}
 						 }`
@@ -910,6 +901,8 @@ func TestMergeUsers(t *testing.T) {
 		Data: []openrtb2.Data{
 			{ID: "openRtbGlobalFPDUserDataId1", Name: "openRtbGlobalFPDUserDataName1"},
 			{ID: "openRtbGlobalFPDUserDataId2", Name: "openRtbGlobalFPDUserDataName2"},
+			{ID: "UserDataId1", Name: "UserDataName1"},
+			{ID: "UserDataId2", Name: "UserDataName2"},
 		},
 	}
 	assert.Equal(t, expectedUser, resultUser, "Result user is incorrect")
@@ -959,7 +952,7 @@ func TestResolveSite(t *testing.T) {
 	fpdConfigSite[keywordsKey] = []byte(`"fpdConfigSiteKeywords"`)
 	fpdConfigSite[nameKey] = []byte(`"fpdConfigSiteName"`)
 	fpdConfigSite[pageKey] = []byte(`"fpdConfigSitePage"`)
-	fpdConfigSite["data"] = []byte(`[{"id":"SiteDataId1", "name":"SiteDataName1"}, {"id":"SiteDataId2", "name":"SiteDataName2"}]`)
+	fpdConfigSite[dataKey] = []byte(`[{"id":"SiteDataId1", "name":"SiteDataName1"}, {"id":"SiteDataId2", "name":"SiteDataName2"}]`)
 	fpdConfigSite["ext"] = []byte(`{"data":{"fpdConfigSiteExt": 123}}`)
 
 	bidRequestSite := &openrtb2.Site{
@@ -1070,7 +1063,7 @@ func TestResolveSite(t *testing.T) {
 		fpdConfigSite := make(map[string]json.RawMessage, 0)
 		fpdConfigSite["id"] = []byte(`"fpdConfigSiteId"`)
 		fpdConfigSite[keywordsKey] = []byte(`"fpdConfigSiteKeywords"`)
-		fpdConfigSite["data"] = []byte(`[{"id":"SiteDataId1", "name":"SiteDataName1"}, {"id":"SiteDataId2", "name":"SiteDataName2"}]`)
+		fpdConfigSite[dataKey] = []byte(`[{"id":"SiteDataId1", "name":"SiteDataName1"}, {"id":"SiteDataId2", "name":"SiteDataName2"}]`)
 		fpdConfigSite["ext"] = []byte(`{"data":{"fpdConfigSiteExt": 123}}`)
 		fpdConfig := &openrtb_ext.ORTB2{Site: fpdConfigSite}
 
@@ -1134,7 +1127,7 @@ func TestMergeSites(t *testing.T) {
 	fpdConfigSite[pageCatKey] = []byte(`["cars3", "auto3"]`)
 	fpdConfigSite[searchKey] = []byte(`"fpdConfigSiteSearch"`)
 	fpdConfigSite[refKey] = []byte(`"fpdConfigSiteRef"`)
-	fpdConfigSite["data"] = []byte(`[{"id":"SiteDataId1", "name":"SiteDataName1"}, {"id":"SiteDataId2", "name":"SiteDataName2"}]`)
+	fpdConfigSite[dataKey] = []byte(`[{"id":"SiteDataId1", "name":"SiteDataName1"}, {"id":"SiteDataId2", "name":"SiteDataName2"}]`)
 	fpdConfigSite["ext"] = []byte(`{"data":{"fpdConfigSiteExt": 123}}`)
 
 	resultSite, err := mergeSites(originalSite, fpdConfigSite, "appnexus")
@@ -1181,7 +1174,7 @@ func TestResolveApp(t *testing.T) {
 	fpdConfigApp[keywordsKey] = []byte(`"fpdConfigAppKeywords"`)
 	fpdConfigApp[nameKey] = []byte(`"fpdConfigAppName"`)
 	fpdConfigApp[bundleKey] = []byte(`"fpdConfigAppBundle"`)
-	fpdConfigApp["data"] = []byte(`[{"id":"AppDataId1", "name":"AppDataName1"}, {"id":"AppDataId2", "name":"AppDataName2"}]`)
+	fpdConfigApp[dataKey] = []byte(`[{"id":"AppDataId1", "name":"AppDataName1"}, {"id":"AppDataId2", "name":"AppDataName2"}]`)
 	fpdConfigApp["ext"] = []byte(`{"data":{"fpdConfigAppExt": 123}}`)
 
 	bidRequestApp := &openrtb2.App{
@@ -1292,7 +1285,7 @@ func TestResolveApp(t *testing.T) {
 		fpdConfigApp := make(map[string]json.RawMessage, 0)
 		fpdConfigApp["id"] = []byte(`"fpdConfigAppId"`)
 		fpdConfigApp[keywordsKey] = []byte(`"fpdConfigAppKeywords"`)
-		fpdConfigApp["data"] = []byte(`[{"id":"AppDataId1", "name":"AppDataName1"}, {"id":"AppDataId2", "name":"AppDataName2"}]`)
+		fpdConfigApp[dataKey] = []byte(`[{"id":"AppDataId1", "name":"AppDataName1"}, {"id":"AppDataId2", "name":"AppDataName2"}]`)
 		fpdConfigApp["ext"] = []byte(`{"data":{"fpdConfigAppExt": 123}}`)
 		fpdConfig := &openrtb_ext.ORTB2{App: fpdConfigApp}
 
@@ -1356,7 +1349,7 @@ func TestMergeApps(t *testing.T) {
 	fpdConfigApp[catKey] = []byte(`["cars1", "auto1"]`)
 	fpdConfigApp[sectionCatKey] = []byte(`["cars2", "auto2"]`)
 	fpdConfigApp[pageCatKey] = []byte(`["cars3", "auto3"]`)
-	fpdConfigApp["data"] = []byte(`[{"id":"AppDataId1", "name":"AppDataName1"}, {"id":"AppDataId2", "name":"AppDataName2"}]`)
+	fpdConfigApp[dataKey] = []byte(`[{"id":"AppDataId1", "name":"AppDataName1"}, {"id":"AppDataId2", "name":"AppDataName2"}]`)
 	fpdConfigApp["ext"] = []byte(`{"data":{"fpdConfigAppExt": 123}}`)
 
 	resultApp, err := mergeApps(originalApp, fpdConfigApp)
diff --git a/firstpartydata/tests/extractfpdforbidders/two-bidders-global-and-non-global-user.json b/firstpartydata/tests/extractfpdforbidders/two-bidders-global-and-non-global-user.json
index c04838c7..ed81b415 100644
--- a/firstpartydata/tests/extractfpdforbidders/two-bidders-global-and-non-global-user.json
+++ b/firstpartydata/tests/extractfpdforbidders/two-bidders-global-and-non-global-user.json
@@ -200,19 +200,19 @@
         "gender": "F",
         "yob": 2000,
         "keywords": "apnFpdUserKeyword",
+        "data": [
+          {
+            "id": "apnFpdUserContentDataId1",
+            "name": "apnFpdUserContentDataName1"
+          },
+          {
+            "id": "apnFpdUserContentDataId2",
+            "name": "apnFpdUserContentDataName2"
+          }
+        ],
         "ext": {
           "data": {
-            "id": "apnUserId",
-            "data": [
-              {
-                "id": "apnFpdUserContentDataId1",
-                "name": "apnFpdUserContentDataName1"
-              },
-              {
-                "id": "apnFpdUserContentDataId2",
-                "name": "apnFpdUserContentDataName2"
-              }
-            ]
+            "id": "apnUserId"
           }
         }
       }
diff --git a/usersync/syncer_test.go b/usersync/syncer_test.go
index 6ff23009..21a86932 100644
--- a/usersync/syncer_test.go
+++ b/usersync/syncer_test.go
@@ -26,7 +26,6 @@ func TestNewSyncer(t *testing.T) {
 	testCases := []struct {
 		description         string
 		givenKey            string
-		givenDefault        string
 		givenIFrameConfig   *config.SyncerEndpoint
 		givenRedirectConfig *config.SyncerEndpoint
 		givenExternalURL    string
@@ -38,7 +37,6 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "Missing Key",
 			givenKey:            "",
-			givenDefault:        "iframe",
 			givenIFrameConfig:   iframeConfig,
 			givenRedirectConfig: nil,
 			expectedError:       "key is required",
@@ -46,23 +44,13 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "Missing Endpoints",
 			givenKey:            "a",
-			givenDefault:        "",
 			givenIFrameConfig:   nil,
 			givenRedirectConfig: nil,
 			expectedError:       "at least one endpoint (iframe and/or redirect) is required",
 		},
-		{
-			description:         "Resolve Default Sync Type Error ",
-			givenKey:            "a",
-			givenDefault:        "",
-			givenIFrameConfig:   iframeConfig,
-			givenRedirectConfig: redirectConfig,
-			expectedError:       "default sync type is required when more then one sync endpoint is configured",
-		},
 		{
 			description:         "IFrame & Redirect Endpoints",
 			givenKey:            "a",
-			givenDefault:        "iframe",
 			givenIFrameConfig:   iframeConfig,
 			givenRedirectConfig: redirectConfig,
 			expectedDefault:     SyncTypeIFrame,
@@ -72,7 +60,6 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "IFrame - Parse Error",
 			givenKey:            "a",
-			givenDefault:        "iframe",
 			givenIFrameConfig:   errParseConfig,
 			givenRedirectConfig: nil,
 			expectedError:       "iframe template: a_usersync_url:1: function \"malformed\" not defined",
@@ -80,7 +67,6 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "IFrame - Validation Error",
 			givenKey:            "a",
-			givenDefault:        "iframe",
 			givenIFrameConfig:   errInvalidConfig,
 			givenRedirectConfig: nil,
 			expectedError:       "iframe composed url: \"notAURL:http%3A%2F%2Fhost.com%2Fhost\" is invalid",
@@ -88,7 +74,6 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "Redirect - Parse Error",
 			givenKey:            "a",
-			givenDefault:        "redirect",
 			givenIFrameConfig:   nil,
 			givenRedirectConfig: errParseConfig,
 			expectedError:       "redirect template: a_usersync_url:1: function \"malformed\" not defined",
@@ -96,7 +81,6 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "Redirect - Validation Error",
 			givenKey:            "a",
-			givenDefault:        "redirect",
 			givenIFrameConfig:   nil,
 			givenRedirectConfig: errInvalidConfig,
 			expectedError:       "redirect composed url: \"notAURL:http%3A%2F%2Fhost.com%2Fhost\" is invalid",
@@ -104,7 +88,6 @@ func TestNewSyncer(t *testing.T) {
 		{
 			description:         "Syncer Level External URL",
 			givenKey:            "a",
-			givenDefault:        "iframe",
 			givenExternalURL:    "http://syncer.com",
 			givenIFrameConfig:   iframeConfig,
 			givenRedirectConfig: redirectConfig,
@@ -118,7 +101,6 @@ func TestNewSyncer(t *testing.T) {
 		syncerConfig := config.Syncer{
 			Key:         test.givenKey,
 			SupportCORS: &supportCORS,
-			Default:     test.givenDefault,
 			IFrame:      test.givenIFrameConfig,
 			Redirect:    test.givenRedirectConfig,
 			ExternalURL: test.givenExternalURL,
@@ -166,73 +148,27 @@ func TestResolveDefaultSyncType(t *testing.T) {
 		description      string
 		givenConfig      config.Syncer
 		expectedSyncType SyncType
-		expectedError    string
 	}{
 		{
-			description:      "IFrame & Redirect - IFrame Default",
-			givenConfig:      config.Syncer{Default: "iframe", IFrame: anyEndpoint, Redirect: anyEndpoint},
-			expectedSyncType: SyncTypeIFrame,
-		},
-		{
-			description:      "IFrame & Redirect - Redirect Default",
-			givenConfig:      config.Syncer{Default: "redirect", IFrame: anyEndpoint, Redirect: anyEndpoint},
-			expectedSyncType: SyncTypeRedirect,
-		},
-		{
-			description:      "IFrame & Redirect - No Default",
-			givenConfig:      config.Syncer{Default: "", IFrame: anyEndpoint, Redirect: anyEndpoint},
-			expectedSyncType: SyncTypeUnknown,
-			expectedError:    "default sync type is required when more then one sync endpoint is configured",
-		},
-		{
-			description:      "IFrame & Redirect - Invalid Default",
-			givenConfig:      config.Syncer{Default: "invalid", IFrame: anyEndpoint, Redirect: anyEndpoint},
-			expectedSyncType: SyncTypeUnknown,
-			expectedError:    "invalid default sync type 'invalid'",
-		},
-		{
-			description:      "IFrame Only - IFrame Default",
-			givenConfig:      config.Syncer{Default: "iframe", IFrame: anyEndpoint, Redirect: nil},
+			description:      "IFrame & Redirect",
+			givenConfig:      config.Syncer{IFrame: anyEndpoint, Redirect: anyEndpoint},
 			expectedSyncType: SyncTypeIFrame,
 		},
 		{
-			description:      "IFrame Only - No Default",
-			givenConfig:      config.Syncer{Default: "", IFrame: anyEndpoint, Redirect: nil},
+			description:      "IFrame Only",
+			givenConfig:      config.Syncer{IFrame: anyEndpoint, Redirect: nil},
 			expectedSyncType: SyncTypeIFrame,
 		},
-		{
-			description:      "IFrame Only - Redirect Default",
-			givenConfig:      config.Syncer{Default: "redirect", IFrame: anyEndpoint, Redirect: nil},
-			expectedSyncType: SyncTypeUnknown,
-			expectedError:    "default is set to redirect but no redirect endpoint is configured",
-		},
 		{
 			description:      "Redirect Only - Redirect Default",
-			givenConfig:      config.Syncer{Default: "redirect", IFrame: nil, Redirect: anyEndpoint},
+			givenConfig:      config.Syncer{IFrame: nil, Redirect: anyEndpoint},
 			expectedSyncType: SyncTypeRedirect,
 		},
-		{
-			description:      "IFrame Only - No Default",
-			givenConfig:      config.Syncer{Default: "", IFrame: nil, Redirect: anyEndpoint},
-			expectedSyncType: SyncTypeRedirect,
-		},
-		{
-			description:      "IFrame Only - IFrame Default",
-			givenConfig:      config.Syncer{Default: "iframe", IFrame: nil, Redirect: anyEndpoint},
-			expectedSyncType: SyncTypeUnknown,
-			expectedError:    "default is set to iframe but no iframe endpoint is configured",
-		},
 	}
 
 	for _, test := range testCases {
-		result, err := resolveDefaultSyncType(test.givenConfig)
-
+		result := resolveDefaultSyncType(test.givenConfig)
 		assert.Equal(t, test.expectedSyncType, result, test.description+":result")
-		if test.expectedError == "" {
-			assert.NoError(t, err, test.description+":err")
-		} else {
-			assert.EqualError(t, err, test.expectedError, test.description+":err")
-		}
 	}
 }
 
diff --git a/usersync/syncersbuilder_test.go b/usersync/syncersbuilder_test.go
index e36b1c2e..79727be6 100644
--- a/usersync/syncersbuilder_test.go
+++ b/usersync/syncersbuilder_test.go
@@ -22,10 +22,11 @@ func TestBuildSyncers(t *testing.T) {
 	var (
 		hostConfig              = config.Configuration{ExternalURL: "http://host.com", UserSync: config.UserSync{RedirectURL: "{{.ExternalURL}}/{{.SyncerKey}}/host"}}
 		iframeConfig            = &config.SyncerEndpoint{URL: "https://bidder.com/iframe?redirect={{.RedirectURL}}"}
+		iframeConfigError       = &config.SyncerEndpoint{URL: "https://bidder.com/iframe?redirect={{xRedirectURL}}"} // Error caused by invalid macro
 		infoKeyAPopulated       = config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Key: "a", IFrame: iframeConfig}}
 		infoKeyADisabled        = config.BidderInfo{Enabled: false, Syncer: &config.Syncer{Key: "a", IFrame: iframeConfig}}
 		infoKeyAEmpty           = config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Key: "a"}}
-		infoKeyAError           = config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Key: "a", Default: "redirect", IFrame: iframeConfig}} // Error caused by invalid default sync type
+		infoKeyAError           = config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Key: "a", IFrame: iframeConfigError}}
 		infoKeyASupportsOnly    = config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Supports: []string{"iframe"}}}
 		infoKeyBPopulated       = config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Key: "b", IFrame: iframeConfig}}
 		infoKeyBEmpty           = config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Key: "b"}}
@@ -63,7 +64,7 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAError},
 			expectedErrors: []string{
-				"cannot create syncer for bidder bidder1 with key a: default is set to redirect but no redirect endpoint is configured",
+				"cannot create syncer for bidder bidder1 with key a: iframe template: a_usersync_url:1: function \"xRedirectURL\" not defined",
 			},
 		},
 		{
@@ -105,7 +106,7 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAEmpty, "bidder2": infoKeyAError},
 			expectedErrors: []string{
-				"cannot create syncer for bidder bidder2 with key a: default is set to redirect but no redirect endpoint is configured",
+				"cannot create syncer for bidder bidder2 with key a: iframe template: a_usersync_url:1: function \"xRedirectURL\" not defined",
 			},
 		},
 		{
@@ -137,7 +138,7 @@ func TestBuildSyncers(t *testing.T) {
 			givenConfig:      hostConfig,
 			givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAError, "bidder2": infoKeyBEmpty},
 			expectedErrors: []string{
-				"cannot create syncer for bidder bidder1 with key a: default is set to redirect but no redirect endpoint is configured",
+				"cannot create syncer for bidder bidder1 with key a: iframe template: a_usersync_url:1: function \"xRedirectURL\" not defined",
 				"cannot create syncer for bidder bidder2 with key b: at least one endpoint (iframe and/or redirect) is required",
 			},
 		},
@@ -199,7 +200,7 @@ func TestShouldCreateSyncer(t *testing.T) {
 		},
 		{
 			description: "Enabled, Syncer - Fully Loaded",
-			given:       config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Key: "anyKey", Default: "iframe", Supports: anySupports, IFrame: anyEndpoint, Redirect: anyEndpoint, SupportCORS: &anyCORS}},
+			given:       config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Key: "anyKey", Supports: anySupports, IFrame: anyEndpoint, Redirect: anyEndpoint, SupportCORS: &anyCORS}},
 			expected:    true,
 		},
 		{
@@ -207,11 +208,6 @@ func TestShouldCreateSyncer(t *testing.T) {
 			given:       config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Key: "anyKey"}},
 			expected:    true,
 		},
-		{
-			description: "Enabled, Syncer - Only Default",
-			given:       config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Default: "iframe"}},
-			expected:    true,
-		},
 		{
 			description: "Enabled, Syncer - Only Supports",
 			given:       config.BidderInfo{Enabled: true, Syncer: &config.Syncer{Supports: anySupports}},
@@ -244,7 +240,7 @@ func TestShouldCreateSyncer(t *testing.T) {
 		},
 		{
 			description: "Disabled, Syncer - Fully Loaded",
-			given:       config.BidderInfo{Enabled: false, Syncer: &config.Syncer{Key: "anyKey", Default: "iframe", Supports: anySupports, IFrame: anyEndpoint, Redirect: anyEndpoint, SupportCORS: &anyCORS}},
+			given:       config.BidderInfo{Enabled: false, Syncer: &config.Syncer{Key: "anyKey", Supports: anySupports, IFrame: anyEndpoint, Redirect: anyEndpoint, SupportCORS: &anyCORS}},
 			expected:    false,
 		},
 		{
@@ -252,11 +248,6 @@ func TestShouldCreateSyncer(t *testing.T) {
 			given:       config.BidderInfo{Enabled: false, Syncer: &config.Syncer{Key: "anyKey"}},
 			expected:    false,
 		},
-		{
-			description: "Disabled, Syncer - Only Default",
-			given:       config.BidderInfo{Enabled: false, Syncer: &config.Syncer{Default: "iframe"}},
-			expected:    false,
-		},
 		{
 			description: "Disabled, Syncer - Only Supports",
 			given:       config.BidderInfo{Enabled: false, Syncer: &config.Syncer{Supports: anySupports}},
diff --git a/usersync/synctype_test.go b/usersync/synctype_test.go
index 35a241f1..1f172f5f 100644
--- a/usersync/synctype_test.go
+++ b/usersync/synctype_test.go
@@ -50,49 +50,3 @@ func TestSyncTypeFilter(t *testing.T) {
 		assert.ElementsMatch(t, test.expectedSyncTypes, syncTypes, test.description)
 	}
 }
-
-func TestSyncTypeParse(t *testing.T) {
-	testCases := []struct {
-		description   string
-		given         string
-		expected      SyncType
-		expectedError string
-	}{
-		{
-			description: "IFrame",
-			given:       "iframe",
-			expected:    SyncTypeIFrame,
-		},
-		{
-			description: "IFrame - Case Insensitive",
-			given:       "iFrAmE",
-			expected:    SyncTypeIFrame,
-		},
-		{
-			description: "Redirect",
-			given:       "redirect",
-			expected:    SyncTypeRedirect,
-		},
-		{
-			description: "Redirect - Case Insensitive",
-			given:       "ReDiReCt",
-			expected:    SyncTypeRedirect,
-		},
-		{
-			description:   "Invalid",
-			given:         "invalid",
-			expectedError: "invalid sync type `invalid`",
-		},
-	}
-
-	for _, test := range testCases {
-		result, err := SyncTypeParse(test.given)
-
-		if test.expectedError == "" {
-			assert.NoError(t, err, test.description+":err")
-			assert.Equal(t, test.expected, result, test.description+":result")
-		} else {
-			assert.EqualError(t, err, test.expectedError, test.description+":err")
-		}
-	}
-}