Repository: prebid/prebid-server
Pull request #1089 (2019-10-25 12:42:19)

Add mapping of user.ext.eids[] for LiveIntent in Rubicon bidder
The Rubicon XAPI will initially expect the `liveintent` ID on `user.ext.tpid`, but will change at some point.

This is what will come in OpenRTB:
```
{
  "user": {
    "ext": {
      "eids": [​{
        "source": "liveintent.com",
        "uids": [{
          "id": "T7JiRRvsRAmh88"
        }],
        "ext": {
            "segments": ["999","888"]
        }
      }],
    }
  }
}
```

And this is what needs to be sent to Rubicon:
```
{
  "user": {
    "ext": {
      "eids": [​{
        "source": "liveintent.com",
        "uids": [{
          "id": "T7JiRRvsRAmh88"
        }],
        "ext": {
            "segments": ["999","888"]
        }
      }],
      "tpid": [
        {
           "source": "liveintent.com",
           "uid": "T7JiRRvsRAmh88"
        }
      ],
      "rp": {
          "target": {
              "LIseg": ["999","888"]
          }
      }
    }
  }
}
```

Two changes when `user.ext.eids.source`="liveintent.com"

1) add or create `user.ext.tpid` entry
2) add or create `user.ext.rp` entry


mansinahar: @rpanchyk just curious, why can't Rubicon XAPI just read these values directly from `user.ext.eids` ?
rpanchyk: Good question :) i think historically, Rubicon XAPI reads data from `user.ext.tpid`.
It will be changed in future i guess, but for now it uses as here.
mansinahar: Also, please consider using https://godoc.org/github.com/stretchr/testify/assert package for your tests. This has a lot of functions that help to do assertions easily and also helps with better formatting of failed test results, which in turn helps easy and quick understanding of what went wrong to make fixes. 

Doesn't have to be this PR itself as I understand it can just make this PR too big but if you have a chance then I'd request submitting another PR for this whenever possible
stale[bot]: This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

mansinahar: Hey @rpanchyk! Just wanted to check if you have had a chance to address the above comments?
rpanchyk: hi @mansinahar, yes, i plan to continue working on PR this week.

stale[bot]: This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.


Patch:
diff --git a/adapters/audienceNetwork/facebook.go b/adapters/audienceNetwork/facebook.go
index f8c993ba..a20a2393 100644
--- a/adapters/audienceNetwork/facebook.go
+++ b/adapters/audienceNetwork/facebook.go
@@ -23,7 +23,6 @@ type FacebookAdapter struct {
 	URI          string
 	nonSecureUri string
 	platformID   string
-	appID        string
 	appSecret    string
 }
 
@@ -75,7 +74,6 @@ func (this *FacebookAdapter) buildRequests(request *openrtb.BidRequest) ([]*adap
 
 	headers.Add("Content-Type", "application/json;charset=utf-8")
 	headers.Add("Accept", "application/json")
-	headers.Add("Auth-Token", this.appID+"|"+this.appSecret)
 	headers.Add("X-Fb-Pool-Routing-Token", request.User.BuyerUID)
 
 	for _, imp := range request.Imp {
@@ -406,7 +404,7 @@ func resolveImpType(imp *openrtb.Imp) (openrtb_ext.BidType, bool) {
 	return openrtb_ext.BidTypeBanner, false
 }
 
-func NewFacebookBidder(client *http.Client, platformID string, appID string, appSecret string) adapters.Bidder {
+func NewFacebookBidder(client *http.Client, platformID string, appSecret string) adapters.Bidder {
 	if platformID == "" {
 		glog.Errorf("No facebook partnerID specified. Calls to the Audience Network will fail. Did you set adapters.facebook.platform_id in the app config?")
 		return &adapters.MisconfiguredBidder{
@@ -415,14 +413,6 @@ func NewFacebookBidder(client *http.Client, platformID string, appID string, app
 		}
 	}
 
-	if appID == "" {
-		glog.Errorf("No facebook app ID specified. Calls to the Audience Network will fail. Did you set adapters.facebook.app_id in the app config?")
-		return &adapters.MisconfiguredBidder{
-			Name:  "audienceNetwork",
-			Error: errors.New("Audience Network is not configured properly on this Prebid Server deploy. If you believe this should work, contact the company hosting the service and tell them to check their configuration."),
-		}
-	}
-
 	if appSecret == "" {
 		glog.Errorf("No facebook app secret specified. Calls to the Audience Network will fail. Did you set adapters.facebook.app_secret in the app config?")
 		return &adapters.MisconfiguredBidder{
@@ -439,7 +429,6 @@ func NewFacebookBidder(client *http.Client, platformID string, appID string, app
 		//for AB test
 		nonSecureUri: "http://an.facebook.com/placementbid.ortb",
 		platformID:   platformID,
-		appID:        appID,
 		appSecret:    appSecret,
 	}
 }
diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go
index 86422db0..7f8de46b 100644
--- a/adapters/rubicon/rubicon.go
+++ b/adapters/rubicon/rubicon.go
@@ -213,6 +213,16 @@ var rubiSizeMap = map[rubiSize]int{
 	{w: 640, h: 320}:   156,
 }
 
+// defines the contract for bidrequest.user.ext.eids[i].ext
+type rubiconUserExtEidExt struct {
+	Segments []string `json:"segments,omitempty"`
+}
+
+// defines the contract for bidrequest.user.ext.eids[i].uids[j].ext
+type rubiconUserExtEidUidExt struct {
+	RtiPartner string `json:"rtiPartner,omitempty"`
+}
+
 //MAS algorithm
 func findPrimary(alt []int) (int, []int) {
 	min, pos, primary := 0, 0, 0
@@ -648,20 +658,12 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap
 
 				// set user.ext.tpid
 				if len(userExt.Eids) > 0 {
-					tpIds := make([]rubiconExtUserTpID, 0)
-					for _, eid := range userExt.Eids {
-						if eid.Source == "adserver.org" {
-							uids := eid.Uids
-							if len(uids) > 0 {
-								uid := uids[0]
-								if uid.Ext != nil && uid.Ext.RtiPartner == "TDID" {
-									tpIds = append(tpIds, rubiconExtUserTpID{Source: "tdid", UID: uid.ID})
-								}
-							}
-						}
-					}
-					if len(tpIds) > 0 {
-						userExtRP.TpID = tpIds
+					if tpIds, segments, errors := getTpIdsAndSegments(userExt.Eids); len(errors) > 0 {
+						errs = append(errs, errors...)
+						continue
+					} else if err := updateUserExtWithTpIdsAndSegments(&userExtRP, tpIds, segments); err != nil {
+						errs = append(errs, err)
+						continue
 					}
 				}
 			}
@@ -746,6 +748,82 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap
 	return requestData, errs
 }
 
+func getTpIdsAndSegments(eids []openrtb_ext.ExtUserEid) ([]rubiconExtUserTpID, []string, []error) {
+	tpIds := make([]rubiconExtUserTpID, 0)
+	segments := make([]string, 0)
+	errs := make([]error, 0)
+
+	for _, eid := range eids {
+		switch eid.Source {
+		case "adserver.org":
+			uids := eid.Uids
+			if len(uids) > 0 {
+				uid := uids[0]
+
+				if uid.Ext != nil {
+					var eidUidExt rubiconUserExtEidUidExt
+					if err := json.Unmarshal(uid.Ext, &eidUidExt); err != nil {
+						errs = append(errs, &errortypes.BadInput{
+							Message: err.Error(),
+						})
+						continue
+					}
+
+					if eidUidExt.RtiPartner == "TDID" {
+						tpIds = append(tpIds, rubiconExtUserTpID{Source: "tdid", UID: uid.ID})
+					}
+				}
+			}
+		case "liveintent.com":
+			uids := eid.Uids
+			if len(uids) > 0 {
+				uidId := uids[0].ID
+				if uidId != "" {
+					tpIds = append(tpIds, rubiconExtUserTpID{Source: "liveintent.com", UID: uidId})
+				}
+
+				if eid.Ext != nil {
+					var eidExt rubiconUserExtEidExt
+					if err := json.Unmarshal(eid.Ext, &eidExt); err != nil {
+						errs = append(errs, &errortypes.BadInput{
+							Message: err.Error(),
+						})
+						continue
+					}
+					segments = eidExt.Segments
+				}
+			}
+		}
+	}
+
+	return tpIds, segments, errs
+}
+
+func updateUserExtWithTpIdsAndSegments(userExtRP *rubiconUserExt, tpIds []rubiconExtUserTpID, segments []string) error {
+	if len(tpIds) > 0 {
+		userExtRP.TpID = tpIds
+
+		if segments != nil {
+			userExtRPTarget := make(map[string]interface{})
+
+			if userExtRP.RP.Target != nil {
+				if err := json.Unmarshal(userExtRP.RP.Target, &userExtRPTarget); err != nil {
+					return &errortypes.BadInput{Message: err.Error()}
+				}
+			}
+
+			userExtRPTarget["LIseg"] = segments
+
+			if target, err := json.Marshal(&userExtRPTarget); err != nil {
+				return &errortypes.BadInput{Message: err.Error()}
+			} else {
+				userExtRP.RP.Target = target
+			}
+		}
+	}
+	return nil
+}
+
 func isVideo(imp openrtb.Imp) bool {
 	video := imp.Video
 	if video != nil {
diff --git a/config/config.go b/config/config.go
index 8f1d5f3b..3df2eaf8 100644
--- a/config/config.go
+++ b/config/config.go
@@ -231,7 +231,6 @@ type Adapter struct {
 
 	// needed for Facebook
 	PlatformID string `mapstructure:"platform_id"`
-	AppID      string `mapstructure:"app_id"`
 	AppSecret  string `mapstructure:"app_secret"`
 }
 
@@ -474,28 +473,31 @@ func (cfg *Configuration) GetCachedAssetURL(uuid string) string {
 func (cfg *Configuration) setDerivedDefaults() {
 	externalURL := cfg.ExternalURL
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.Bidder33Across, "https://ic.tynt.com/r/d?m=xch&rt=html&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&ru="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3D33across%26uid%3D33XUSERID33X&id=zzz000000000002zzz")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdform, "https://cm.adform.net/cookie?redirect_url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadform%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdkernel, "https://sync.adkernel.com/user-sync?t=image&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadkernel%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BUID%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdkernelAdn, "https://tag.adkernel.com/syncr?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3DadkernelAdn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BUID%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdpone, "https://usersync.adpone.com/csync?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadpone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdtelligent, "https://sync.adtelligent.com/csync?t=p&ep=0&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D")
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdform, "https://cm.adform.net/cookie?redirect_url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadform%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdvangelists, "https://nep.advangelists.com/xp/user-sync?acctid={aid}&&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadvangelists%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAppnexus, "https://ib.adnxs.com/getuid?"+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadnxs%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBrightroll, "https://pr-bh.ybp.yahoo.com/sync/appnexusprebidserver/?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbrightroll%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeachfront, "https://sync.bfmio.com/sync_s2s?gdpr={{.GDPR}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeachfront%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bio_cid%5D")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBrightroll, "https://pr-bh.ybp.yahoo.com/sync/appnexusprebidserver/?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbrightroll%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConsumable, "https://e.serverbid.com/udb/9969/match?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconsumable%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/prebid/match?rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDatablocks, "https://sync.v5prebid.datablocks.net/s2ssync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D%24%7Bgdpr%7D%26gdpr_consent%3D%24%7Bgdpr_consent%7D%26uid%3D%24%7Buid%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEmxDigital, "https://cs.emxdgt.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Demx_digital%26uid%3D%24UID")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEngageBDR, "https://match.bnmla.com/usersync/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEPlanning, "https://ads.us.e-planning.net/uspd/1/?du=https%3A%2F%2Fads.us.e-planning.net%2Fgetuid%2F1%2F5a1ad71d2d53a0f5%3F"+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Deplanning%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	// openrtb_ext.BidderFacebook doesn't have a good default.
 	// openrtb_ext.BidderGamma doesn't have a good default.
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGrid, "https://grid.bidswitch.net/sp_sync?sp_id=prebid&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dgrid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGamoshi, "https://rtb.gamoshi.io/pix/0000/scm?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dgamoshi%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bgusr%5D")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGrid, "https://x.bidswitch.net/check_uuid/"+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dgrid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BBSW_UUID%7D?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGumGum, "https://rtb.gumgum.com/usync/prbds2s?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dgumgum%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderImprovedigital, "https://ad.360yield.com/server_match?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dimprovedigital%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BPUB_USER_ID%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderIx, "https://ssum.casalemedia.com/usermatchredir?s=184932&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dix%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLifestreet, "https://ads.lfstmedia.com/idsync/137062?synced=1&ttl=1s&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlifestreet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLockerDome, "https://lockerdome.com/usync/prebidserver?pid="+cfg.Adapters["lockerdome"].PlatformID+"&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlockerdome%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7Buid%7D%7D")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMgid, "https://cm.mgid.com/m?cdsp=363893&adu="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmgid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Bmuidn%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderOpenx, "https://rtb.openx.net/sync/prebid?r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dopenx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUID%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPubmatic, "https://ads.pubmatic.com/AdServer/js/user_sync.html?predirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPulsepoint, "https://bh.contextweb.com/rtset?pid=561205&ev=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpulsepoint%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25%25VGUID%25%25")
@@ -504,20 +506,16 @@ func (cfg *Configuration) setDerivedDefaults() {
 	// openrtb_ext.BidderRubicon doesn't have a good default.
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSharethrough, "https://match.sharethrough.com/FGMrCMMc/v1?redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsharethrough%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSomoaudience, "https://publisher-east.mobileadtrading.com/usersync?ru="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsomoaudience%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUID%7D")
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSovrn, "https://ap.lijit.com/pixel?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsovrn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSonobi, "https://sync.go.sonobi.com/us.gif?loc="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsonobi%26consent_string%3D{{.GDPR}}%26gdpr%3D{{.GDPRConsent}}%26uid%3D%5BUID%5D")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSovrn, "https://ap.lijit.com/pixel?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsovrn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSynacormedia, "https://sync.technoratimedia.com/services?srv=cs&pid=70&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsynacormedia%26uid%3D%5BUSER_ID%5D")
-	// openrtb_ext.BidderVrtcal doesn't have a good default.
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldmo, "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldmo%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGamoshi, "https://rtb.gamoshi.io/pix/0000/scm?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dgamoshi%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bgusr%5D")
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMgid, "https://cm.mgid.com/m?cdsp=363893&adu="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmgid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Bmuidn%7D")
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTripleliftNative, "https://eb2.3lift.com/sync?gpdr={{.GDPR}}&cmp_cs={{.GDPRConsent}}")
+	// openrtb_ext.BidderTappx doesn't have a good default.
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTriplelift, "https://eb2.3lift.com/getuid?gpdr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTripleliftNative, "https://eb2.3lift.com/sync?gpdr={{.GDPR}}&cmp_cs={{.GDPRConsent}}")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderUnruly, "https://usermatch.targeting.unrulymedia.com/pbsync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dunruly%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderVisx, "https://t.visx.net/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D")
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEngageBDR, "https://match.bnmla.com/usersync/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D")
-
-	// openrtb_ext.BidderTappx doesn't have a good default.
+	// openrtb_ext.BidderVrtcal doesn't have a good default.
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldmo, "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldmo%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 }
 
 func setDefaultUsersync(m map[string]Adapter, bidder openrtb_ext.BidderName, defaultValue string) {
@@ -651,54 +649,54 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.audiencenetwork.disabled", true)
 	v.SetDefault("adapters.rubicon.disabled", true)
 
-	v.SetDefault("adapters.adtelligent.endpoint", "http://hb.adtelligent.com/auction")
+	v.SetDefault("adapters.33across.endpoint", "http://ssc.33across.com/api/v1/hb")
+	v.SetDefault("adapters.33across.partner_id", "")
 	v.SetDefault("adapters.adform.endpoint", "http://adx.adform.net/adx")
+	v.SetDefault("adapters.adkernel.endpoint", "http://{{.Host}}/hb?zone={{.ZoneID}}")
+	v.SetDefault("adapters.adkerneladn.endpoint", "http://{{.Host}}/rtbpub?account={{.PublisherID}}")
+	v.SetDefault("adapters.adpone.endpoint", "http://rtb.adpone.com/bid-request?src=prebid_server")
+	v.SetDefault("adapters.adtelligent.endpoint", "http://hb.adtelligent.com/auction")
+	v.SetDefault("adapters.advangelists.endpoint", "http://nep.advangelists.com/xp/get?pubid={{.PublisherID}}")
 	v.SetDefault("adapters.appnexus.endpoint", "http://ib.adnxs.com/openrtb2") // Docs: https://wiki.appnexus.com/display/supply/Incoming+Bid+Request+from+SSPs
 	v.SetDefault("adapters.appnexus.platform_id", "5")
-	v.SetDefault("adapters.advangelists.endpoint", "http://nep.advangelists.com/xp/get?pubid={{.PublisherID}}")
 	v.SetDefault("adapters.beachfront.endpoint", "https://display.bfmio.com/prebid_display")
 	v.SetDefault("adapters.brightroll.endpoint", "http://east-bid.ybp.yahoo.com/bid/appnexuspbs")
 	v.SetDefault("adapters.consumable.endpoint", "https://e.serverbid.com/api/v2")
-	v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}")
-	v.SetDefault("adapters.engagebdr.endpoint", "http://dsp.bnmla.com/hb")
 	v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/s2s/header/24")
+	v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}")
 	v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com")
+	v.SetDefault("adapters.engagebdr.endpoint", "http://dsp.bnmla.com/hb")
 	v.SetDefault("adapters.eplanning.endpoint", "http://ads.us.e-planning.net/hb/1")
+	v.SetDefault("adapters.gamma.endpoint", "https://hb.gammaplatform.com/adx/request/")
+	v.SetDefault("adapters.gamoshi.endpoint", "https://rtb.gamoshi.io")
+	v.SetDefault("adapters.grid.endpoint", "http://grid.bidswitch.net/sp_bid?sp=prebid")
+	v.SetDefault("adapters.gumgum.endpoint", "https://g2.gumgum.com/providers/prbds2s/bid")
 	v.SetDefault("adapters.improvedigital.endpoint", "http://ad.360yield.com/pbs")
 	v.SetDefault("adapters.ix.endpoint", "http://appnexus-us-east.lb.indexww.com/transbidder?p=184932")
+	v.SetDefault("adapters.kubient.endpoint", "http://kbntx.ch/prebid")
 	v.SetDefault("adapters.lifestreet.endpoint", "https://prebid.s2s.lfstmedia.com/adrequest")
 	v.SetDefault("adapters.lockerdome.endpoint", "https://lockerdome.com/ladbid/prebidserver/openrtb2")
+	v.SetDefault("adapters.mgid.endpoint", "https://prebid.mgid.com/prebid/")
 	v.SetDefault("adapters.openx.endpoint", "http://rtb.openx.net/prebid")
 	v.SetDefault("adapters.pubmatic.endpoint", "http://hbopenbid.pubmatic.com/translator?source=prebid-server")
 	v.SetDefault("adapters.pulsepoint.endpoint", "http://bid.contextweb.com/header/s/ortb/prebid-s2s")
-	v.SetDefault("adapters.rubicon.endpoint", "http://exapi-us-east.rubiconproject.com/a/api/exchange.json")
+	v.SetDefault("adapters.rhythmone.endpoint", "http://tag.1rx.io/rmp")
 	v.SetDefault("adapters.rtbhouse.endpoint", "http://prebidserver-s2s-ams.creativecdn.com/bidder/prebidserver/bids")
+	v.SetDefault("adapters.rubicon.endpoint", "http://exapi-us-east.rubiconproject.com/a/api/exchange.json")
+	v.SetDefault("adapters.sharethrough.endpoint", "http://btlr.sharethrough.com/FGMrCMMc/v1")
 	v.SetDefault("adapters.somoaudience.endpoint", "http://publisher-east.mobileadtrading.com/rtb/bid")
+	v.SetDefault("adapters.sonobi.endpoint", "https://apex.go.sonobi.com/prebid?partnerid=71d9d3d8af")
 	v.SetDefault("adapters.sovrn.endpoint", "http://ap.lijit.com/rtb/bid?src=prebid_server")
+	v.SetDefault("adapters.synacormedia.endpoint", "http://{{.Host}}.technoratimedia.com/openrtb/bids/{{.Host}}")
+	v.SetDefault("adapters.tappx.endpoint", "https://{{.Host}}")
 	v.SetDefault("adapters.triplelift_native.disabled", true)
 	v.SetDefault("adapters.triplelift_native.extra_info", "{\"publisher_whitelist\":[]}")
-	v.SetDefault("adapters.synacormedia.endpoint", "http://{{.Host}}.technoratimedia.com/openrtb/bids/{{.Host}}")
 	v.SetDefault("adapters.triplelift.endpoint", "https://tlx.3lift.com/s2s/auction?supplier_id=20")
-	v.SetDefault("adapters.adkernel.endpoint", "http://{{.Host}}/hb?zone={{.ZoneID}}")
-	v.SetDefault("adapters.adkerneladn.endpoint", "http://{{.Host}}/rtbpub?account={{.PublisherID}}")
-	v.SetDefault("adapters.33across.partner_id", "")
-	v.SetDefault("adapters.33across.endpoint", "http://ssc.33across.com/api/v1/hb")
-	v.SetDefault("adapters.adpone.endpoint", "http://rtb.adpone.com/bid-request?src=prebid_server")
-	v.SetDefault("adapters.rhythmone.endpoint", "http://tag.1rx.io/rmp")
-	v.SetDefault("adapters.gumgum.endpoint", "https://g2.gumgum.com/providers/prbds2s/bid")
-	v.SetDefault("adapters.grid.endpoint", "http://grid.bidswitch.net/sp_bid?sp=prebid")
-	v.SetDefault("adapters.sharethrough.endpoint", "http://btlr.sharethrough.com/FGMrCMMc/v1")
-	v.SetDefault("adapters.sonobi.endpoint", "https://apex.go.sonobi.com/prebid?partnerid=71d9d3d8af")
 	v.SetDefault("adapters.unruly.endpoint", "http://targeting.unrulymedia.com/openrtb/2.2")
-	v.SetDefault("adapters.vrtcal.endpoint", "http://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804")
-	v.SetDefault("adapters.yieldmo.endpoint", "https://ads.yieldmo.com/exchange/prebid-server")
-	v.SetDefault("adapters.gamoshi.endpoint", "https://rtb.gamoshi.io")
-	v.SetDefault("adapters.gamma.endpoint", "https://hb.gammaplatform.com/adx/request/")
-	v.SetDefault("adapters.mgid.endpoint", "https://prebid.mgid.com/prebid/")
 	v.SetDefault("adapters.verizonmedia.disabled", true)
 	v.SetDefault("adapters.visx.endpoint", "https://t.visx.net/s2s_bid?wrapperType=s2s_prebid_standard")
-	v.SetDefault("adapters.tappx.endpoint", "https://{{.Host}}")
-	v.SetDefault("adapters.kubient.endpoint", "http://kbntx.ch/prebid")
+	v.SetDefault("adapters.vrtcal.endpoint", "http://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804")
+	v.SetDefault("adapters.yieldmo.endpoint", "https://ads.yieldmo.com/exchange/prebid-server")
 
 	v.SetDefault("max_request_size", 1024*256)
 	v.SetDefault("analytics.file.filename", "")
diff --git a/currencies/rates.go b/currencies/rates.go
index fd51570e..6a205213 100644
--- a/currencies/rates.go
+++ b/currencies/rates.go
@@ -62,9 +62,12 @@ func (r *Rates) GetRate(from string, to string) (float64, error) {
 	}
 	if r.Conversions != nil {
 		if conversion, present := r.Conversions[fromUnit.String()][toUnit.String()]; present {
+			// In case we have an entry FROM -> TO
 			return conversion, err
+		} else if conversion, present := r.Conversions[toUnit.String()][fromUnit.String()]; present {
+			// In case we have an entry TO -> FROM
+			return 1 / conversion, err
 		}
-
 		return 0, fmt.Errorf("Currency conversion rate not found: '%s' => '%s'", fromUnit.String(), toUnit.String())
 	}
 	return 0, errors.New("rates are nil")
diff --git a/endpoints/auction.go b/endpoints/auction.go
index d736dcf2..9a1d670d 100644
--- a/endpoints/auction.go
+++ b/endpoints/auction.go
@@ -78,33 +78,11 @@ func Auction(cfg *config.Configuration, syncers map[openrtb_ext.BidderName]users
 
 func (a *auction) auction(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
 	w.Header().Add("Content-Type", "application/json")
-	labels := pbsmetrics.Labels{
-		Source:        pbsmetrics.DemandUnknown,
-		RType:         pbsmetrics.ReqTypeLegacy,
-		PubID:         "",
-		Browser:       pbsmetrics.BrowserOther,
-		CookieFlag:    pbsmetrics.CookieFlagUnknown,
-		RequestStatus: pbsmetrics.RequestStatusOK,
-	}
-	if ua := user_agent.New(r.Header.Get("User-Agent")); ua != nil {
-		name, _ := ua.Browser()
-		if name == "Safari" {
-			labels.Browser = pbsmetrics.BrowserSafari
-		}
-	}
+	var labels = getDefaultLabels(r)
 	req, err := pbs.ParsePBSRequest(r, &a.cfg.AuctionTimeouts, a.dataCache, &(a.cfg.HostCookie))
-	// Defer here because we need req defined.
-	defer func() {
-		if req == nil {
-			a.metricsEngine.RecordRequest(labels)
-			a.metricsEngine.RecordLegacyImps(labels, 0)
-		} else {
-			// handles the case that ParsePBSRequest returns an error, so req.Start is not defined
-			a.metricsEngine.RecordRequest(labels)
-			a.metricsEngine.RecordLegacyImps(labels, len(req.AdUnits))
-			a.metricsEngine.RecordRequestTime(labels, time.Since(req.Start))
-		}
-	}()
+
+	defer a.recordMetrics(req, labels)
+
 	if err != nil {
 		if glog.V(2) {
 			glog.Infof("Failed to parse /auction request: %v", err)
@@ -114,17 +92,7 @@ func (a *auction) auction(w http.ResponseWriter, r *http.Request, _ httprouter.P
 		return
 	}
 	status := "OK"
-	if req.App != nil {
-		labels.Source = pbsmetrics.DemandApp
-	} else {
-		labels.Source = pbsmetrics.DemandWeb
-		if req.Cookie.LiveSyncCount() == 0 {
-			labels.CookieFlag = pbsmetrics.CookieFlagNo
-			status = "no_cookie"
-		} else {
-			labels.CookieFlag = pbsmetrics.CookieFlagYes
-		}
-	}
+	setLabelSource(&labels, req, &status)
 	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(req.TimeoutMillis))
 	defer cancel()
 	account, err := a.dataCache.Accounts().Get(req.AccountID)
@@ -150,46 +118,14 @@ func (a *auction) auction(w http.ResponseWriter, r *http.Request, _ httprouter.P
 			blabels := pbsmetrics.AdapterLabels{
 				Source:      labels.Source,
 				RType:       labels.RType,
-				Adapter:     openrtb_ext.BidderMap[bidder.BidderCode],
+				Adapter:     getAdapterValue(bidder),
 				PubID:       labels.PubID,
 				Browser:     labels.Browser,
 				CookieFlag:  labels.CookieFlag,
 				AdapterBids: pbsmetrics.AdapterBidPresent,
 			}
-			if blabels.Adapter == "" {
-				// "districtm" is legal, but not in BidderMap. Other values will log errors in the go_metrics code
-				blabels.Adapter = openrtb_ext.BidderName(bidder.BidderCode)
-			}
-			if req.App == nil {
-				// If exchanges[bidderCode] exists, then a.syncers[bidderCode] exists *except for districtm*.
-				// OpenRTB handles aliases differently, so this hack will keep legacy code working. For all other
-				// bidderCodes, a.syncers[bidderCode] will exist if exchanges[bidderCode] also does.
-				// This is guaranteed by the following unit tests, which compare these maps to the (source of truth) openrtb_ext.BidderMap:
-				//   1. TestSyncers inside usersync/usersync_test.go
-				//   2. TestExchangeMap inside pbs_light_test.go
-				syncerCode := bidder.BidderCode
-				if syncerCode == "districtm" {
-					syncerCode = "appnexus"
-				}
-				syncer := a.syncers[openrtb_ext.BidderName(syncerCode)]
-				uid, _, _ := req.Cookie.GetUID(syncer.FamilyName())
-				if uid == "" {
-					bidder.NoCookie = true
-					gdprApplies := req.ParseGDPR()
-					consent := req.ParseConsent()
-					if a.shouldUsersync(ctx, openrtb_ext.BidderName(syncerCode), gdprApplies, consent) {
-						syncInfo, err := syncer.GetUsersyncInfo(gdprApplies, consent)
-						if err == nil {
-							bidder.UsersyncInfo = syncInfo
-						} else {
-							glog.Errorf("Failed to get usersync info for %s: %v", syncerCode, err)
-						}
-					}
-					blabels.CookieFlag = pbsmetrics.CookieFlagNo
-					if ex.SkipNoCookies() {
-						continue
-					}
-				}
+			if skip := a.processUserSync(req, bidder, blabels, ex, &ctx); skip == true {
+				continue
 			}
 			sentBids++
 			bidderRunner := a.recoverSafely(func(bidder *pbs.PBSBidder, aLabels pbsmetrics.AdapterLabels) {
@@ -198,44 +134,7 @@ func (a *auction) auction(w http.ResponseWriter, r *http.Request, _ httprouter.P
 				bidList, err := ex.Call(ctx, req, bidder)
 				a.metricsEngine.RecordAdapterTime(aLabels, time.Since(start))
 				bidder.ResponseTime = int(time.Since(start) / time.Millisecond)
-				if err != nil {
-					var s struct{}
-					switch err {
-					case context.DeadlineExceeded:
-						aLabels.AdapterErrors = map[pbsmetrics.AdapterError]struct{}{pbsmetrics.AdapterErrorTimeout: s}
-						bidder.Error = "Timed out"
-					case context.Canceled:
-						fallthrough
-					default:
-						bidder.Error = err.Error()
-						switch err.(type) {
-						case *errortypes.BadInput:
-							aLabels.AdapterErrors = map[pbsmetrics.AdapterError]struct{}{pbsmetrics.AdapterErrorBadInput: s}
-						case *errortypes.BadServerResponse:
-							aLabels.AdapterErrors = map[pbsmetrics.AdapterError]struct{}{pbsmetrics.AdapterErrorBadServerResponse: s}
-						default:
-							glog.Warningf("Error from bidder %v. Ignoring all bids: %v", bidder.BidderCode, err)
-							aLabels.AdapterErrors = map[pbsmetrics.AdapterError]struct{}{pbsmetrics.AdapterErrorUnknown: s}
-						}
-					}
-				} else if bidList != nil {
-					bidList = checkForValidBidSize(bidList, bidder)
-					bidder.NumBids = len(bidList)
-					for _, bid := range bidList {
-						var cpm = float64(bid.Price * 1000)
-						a.metricsEngine.RecordAdapterPrice(aLabels, cpm)
-						switch bid.CreativeMediaType {
-						case "banner":
-							a.metricsEngine.RecordAdapterBidReceived(aLabels, openrtb_ext.BidTypeBanner, bid.Adm != "")
-						case "video":
-							a.metricsEngine.RecordAdapterBidReceived(aLabels, openrtb_ext.BidTypeVideo, bid.Adm != "")
-						}
-						bid.ResponseTime = bidder.ResponseTime
-					}
-				} else {
-					bidder.NoBid = true
-					aLabels.AdapterBids = pbsmetrics.AdapterBidNone
-				}
+				processBidResult(bidList, bidder, &aLabels, a.metricsEngine, err)
 
 				ch <- bidResult{
 					bidder:  bidder,
@@ -257,40 +156,10 @@ func (a *auction) auction(w http.ResponseWriter, r *http.Request, _ httprouter.P
 			resp.Bids = append(resp.Bids, bid)
 		}
 	}
-	if req.CacheMarkup == 1 {
-		cobjs := make([]*pbc.CacheObject, len(resp.Bids))
-		for i, bid := range resp.Bids {
-			if bid.CreativeMediaType == "video" {
-				cobjs[i] = &pbc.CacheObject{
-					Value:   bid.Adm,
-					IsVideo: true,
-				}
-			} else {
-				cobjs[i] = &pbc.CacheObject{
-					Value: &pbc.BidCache{
-						Adm:    bid.Adm,
-						NURL:   bid.NURL,
-						Width:  bid.Width,
-						Height: bid.Height,
-					},
-					IsVideo: false,
-				}
-			}
-		}
-		if err := pbc.Put(ctx, cobjs); err != nil {
-			writeAuctionError(w, "Prebid cache failed", err)
-			labels.RequestStatus = pbsmetrics.RequestStatusErr
-			return
-		}
-		for i, bid := range resp.Bids {
-			bid.CacheID = cobjs[i].UUID
-			bid.CacheURL = a.cfg.GetCachedAssetURL(bid.CacheID)
-			bid.NURL = ""
-			bid.Adm = ""
-		}
-	}
-	if req.CacheMarkup == 2 {
-		cacheVideoOnly(resp.Bids, ctx, w, a, &labels)
+	if err := cacheAccordingToMarkup(req, &resp, ctx, a, &labels); err != nil {
+		writeAuctionError(w, "Prebid cache failed", err)
+		labels.RequestStatus = pbsmetrics.RequestStatusErr
+		return
 	}
 	if req.SortBids == 1 {
 		sortBidsAddKeywordsMobile(resp.Bids, req, account.PriceGranularity)
@@ -338,7 +207,7 @@ func (a *auction) shouldUsersync(ctx context.Context, bidder openrtb_ext.BidderN
 }
 
 // cache video bids only for Web
-func cacheVideoOnly(bids pbs.PBSBidSlice, ctx context.Context, w http.ResponseWriter, deps *auction, labels *pbsmetrics.Labels) {
+func cacheVideoOnly(bids pbs.PBSBidSlice, ctx context.Context, deps *auction, labels *pbsmetrics.Labels) error {
 	var cobjs []*pbc.CacheObject
 	for _, bid := range bids {
 		if bid.CreativeMediaType == "video" {
@@ -350,9 +219,7 @@ func cacheVideoOnly(bids pbs.PBSBidSlice, ctx context.Context, w http.ResponseWr
 	}
 	err := pbc.Put(ctx, cobjs)
 	if err != nil {
-		writeAuctionError(w, "Prebid cache failed", err)
-		labels.RequestStatus = pbsmetrics.RequestStatusErr
-		return
+		return err
 	}
 	videoIndex := 0
 	for _, bid := range bids {
@@ -364,6 +231,7 @@ func cacheVideoOnly(bids pbs.PBSBidSlice, ctx context.Context, w http.ResponseWr
 			videoIndex++
 		}
 	}
+	return nil
 }
 
 // checkForValidBidSize goes through list of bids & find those which are banner mediaType and with height or width not defined
@@ -376,16 +244,9 @@ func checkForValidBidSize(bids pbs.PBSBidSlice, bidder *pbs.PBSBidder) pbs.PBSBi
 	finalBidCounter := 0
 bidLoop:
 	for _, bid := range bids {
-		if bid.CreativeMediaType == "banner" && (bid.Height == 0 || bid.Width == 0) {
+		if isUndimensionedBanner(bid) {
 			for _, adunit := range bidder.AdUnits {
-				if adunit.BidID == bid.BidID && adunit.Code == bid.AdUnitCode {
-					if len(adunit.Sizes) == 1 {
-						bid.Width, bid.Height = adunit.Sizes[0].W, adunit.Sizes[0].H
-						finalValidBids[finalBidCounter] = bid
-						finalBidCounter = finalBidCounter + 1
-					} else if len(adunit.Sizes) > 1 {
-						glog.Warningf("Bid was rejected for bidder %s because no size was defined", bid.BidderCode)
-					}
+				if copyBannerDimensions(&adunit, bid, finalValidBids, &finalBidCounter) {
 					continue bidLoop
 				}
 			}
@@ -397,6 +258,27 @@ bidLoop:
 	return finalValidBids[:finalBidCounter]
 }
 
+func isUndimensionedBanner(bid *pbs.PBSBid) bool {
+	return bid.CreativeMediaType == "banner" && (bid.Height == 0 || bid.Width == 0)
+}
+
+func copyBannerDimensions(adunit *pbs.PBSAdUnit, bid *pbs.PBSBid, finalValidBids []*pbs.PBSBid, finalBidCounter *int) bool {
+	var bidIDEqualsCode bool = false
+
+	if adunit.BidID == bid.BidID && adunit.Code == bid.AdUnitCode && adunit.Sizes != nil {
+		if len(adunit.Sizes) == 1 {
+			bid.Width, bid.Height = adunit.Sizes[0].W, adunit.Sizes[0].H
+			finalValidBids[*finalBidCounter] = bid
+			*finalBidCounter += 1
+		} else if len(adunit.Sizes) > 1 {
+			glog.Warningf("Bid was rejected for bidder %s because no size was defined", bid.BidderCode)
+		}
+		bidIDEqualsCode = true
+	}
+
+	return bidIDEqualsCode
+}
+
 // sortBidsAddKeywordsMobile sorts the bids and adds ad server targeting keywords to each bid.
 // The bids are sorted by cpm to find the highest bid.
 // The ad server targeting keywords are added to all bids, with specific keywords for the highest bid.
@@ -482,3 +364,164 @@ func sortBidsAddKeywordsMobile(bids pbs.PBSBidSlice, pbs_req *pbs.PBSRequest, pr
 		}
 	}
 }
+
+func getDefaultLabels(r *http.Request) pbsmetrics.Labels {
+	rlabels := pbsmetrics.Labels{
+		Source:        pbsmetrics.DemandUnknown,
+		RType:         pbsmetrics.ReqTypeLegacy,
+		PubID:         "",
+		Browser:       pbsmetrics.BrowserOther,
+		CookieFlag:    pbsmetrics.CookieFlagUnknown,
+		RequestStatus: pbsmetrics.RequestStatusOK,
+	}
+
+	if ua := user_agent.New(r.Header.Get("User-Agent")); ua != nil {
+		name, _ := ua.Browser()
+		if name == "Safari" {
+			rlabels.Browser = pbsmetrics.BrowserSafari
+		}
+	}
+	return rlabels
+}
+
+func setLabelSource(labels *pbsmetrics.Labels, req *pbs.PBSRequest, status *string) {
+	if req.App != nil {
+		labels.Source = pbsmetrics.DemandApp
+	} else {
+		labels.Source = pbsmetrics.DemandWeb
+		if req.Cookie.LiveSyncCount() == 0 {
+			labels.CookieFlag = pbsmetrics.CookieFlagNo
+			*status = "no_cookie"
+		} else {
+			labels.CookieFlag = pbsmetrics.CookieFlagYes
+		}
+	}
+}
+
+func getAdapterValue(bidder *pbs.PBSBidder) openrtb_ext.BidderName {
+	adapterLabelName, ok := openrtb_ext.BidderMap[bidder.BidderCode]
+	if ok && adapterLabelName != "" {
+		return adapterLabelName
+	} else {
+		return openrtb_ext.BidderName(bidder.BidderCode)
+	}
+}
+
+func cacheAccordingToMarkup(req *pbs.PBSRequest, resp *pbs.PBSResponse, ctx context.Context, a *auction, labels *pbsmetrics.Labels) error {
+	if req.CacheMarkup == 1 {
+		cobjs := make([]*pbc.CacheObject, len(resp.Bids))
+		for i, bid := range resp.Bids {
+			if bid.CreativeMediaType == "video" {
+				cobjs[i] = &pbc.CacheObject{
+					Value:   bid.Adm,
+					IsVideo: true,
+				}
+			} else {
+				cobjs[i] = &pbc.CacheObject{
+					Value: &pbc.BidCache{
+						Adm:    bid.Adm,
+						NURL:   bid.NURL,
+						Width:  bid.Width,
+						Height: bid.Height,
+					},
+					IsVideo: false,
+				}
+			}
+		}
+		if err := pbc.Put(ctx, cobjs); err != nil {
+			return err
+		}
+		for i, bid := range resp.Bids {
+			bid.CacheID = cobjs[i].UUID
+			bid.CacheURL = a.cfg.GetCachedAssetURL(bid.CacheID)
+			bid.NURL = ""
+			bid.Adm = ""
+		}
+	} else if req.CacheMarkup == 2 {
+		return cacheVideoOnly(resp.Bids, ctx, a, labels)
+	}
+	return nil
+}
+
+func processBidResult(bidList pbs.PBSBidSlice, bidder *pbs.PBSBidder, aLabels *pbsmetrics.AdapterLabels, metrics pbsmetrics.MetricsEngine, err error) {
+	if err != nil {
+		var s struct{}
+		if err == context.DeadlineExceeded {
+			aLabels.AdapterErrors = map[pbsmetrics.AdapterError]struct{}{pbsmetrics.AdapterErrorTimeout: s}
+			bidder.Error = "Timed out"
+		} else if err != context.Canceled {
+			bidder.Error = err.Error()
+			switch err.(type) {
+			case *errortypes.BadInput:
+				aLabels.AdapterErrors = map[pbsmetrics.AdapterError]struct{}{pbsmetrics.AdapterErrorBadInput: s}
+			case *errortypes.BadServerResponse:
+				aLabels.AdapterErrors = map[pbsmetrics.AdapterError]struct{}{pbsmetrics.AdapterErrorBadServerResponse: s}
+			default:
+				glog.Warningf("Error from bidder %v. Ignoring all bids: %v", bidder.BidderCode, err)
+				aLabels.AdapterErrors = map[pbsmetrics.AdapterError]struct{}{pbsmetrics.AdapterErrorUnknown: s}
+			}
+		}
+	} else if bidList != nil {
+		bidList = checkForValidBidSize(bidList, bidder)
+		bidder.NumBids = len(bidList)
+		for _, bid := range bidList {
+			var cpm = float64(bid.Price * 1000)
+			metrics.RecordAdapterPrice(*aLabels, cpm)
+			switch bid.CreativeMediaType {
+			case "banner":
+				metrics.RecordAdapterBidReceived(*aLabels, openrtb_ext.BidTypeBanner, bid.Adm != "")
+			case "video":
+				metrics.RecordAdapterBidReceived(*aLabels, openrtb_ext.BidTypeVideo, bid.Adm != "")
+			}
+			bid.ResponseTime = bidder.ResponseTime
+		}
+	} else {
+		bidder.NoBid = true
+		aLabels.AdapterBids = pbsmetrics.AdapterBidNone
+	}
+}
+
+func (a *auction) recordMetrics(req *pbs.PBSRequest, labels pbsmetrics.Labels) {
+	a.metricsEngine.RecordRequest(labels)
+	if req == nil {
+		a.metricsEngine.RecordLegacyImps(labels, 0)
+		return
+	}
+	a.metricsEngine.RecordLegacyImps(labels, len(req.AdUnits))
+	a.metricsEngine.RecordRequestTime(labels, time.Since(req.Start))
+}
+
+func (a *auction) processUserSync(req *pbs.PBSRequest, bidder *pbs.PBSBidder, blabels pbsmetrics.AdapterLabels, ex adapters.Adapter, ctx *context.Context) bool {
+	var skip bool = false
+	if req.App != nil {
+		return skip
+	}
+	// If exchanges[bidderCode] exists, then a.syncers[bidderCode] exists *except for districtm*.
+	// OpenRTB handles aliases differently, so this hack will keep legacy code working. For all other
+	// bidderCodes, a.syncers[bidderCode] will exist if exchanges[bidderCode] also does.
+	// This is guaranteed by the TestSyncers unit test inside usersync/usersync_test.go, which compares these maps to the (source of truth) openrtb_ext.BidderMap:
+	syncerCode := bidder.BidderCode
+	if syncerCode == "districtm" {
+		syncerCode = "appnexus"
+	}
+	syncer := a.syncers[openrtb_ext.BidderName(syncerCode)]
+	uid, _, _ := req.Cookie.GetUID(syncer.FamilyName())
+	if uid == "" {
+		bidder.NoCookie = true
+		gdprApplies := req.ParseGDPR()
+		consent := req.ParseConsent()
+		if a.shouldUsersync(*ctx, openrtb_ext.BidderName(syncerCode), gdprApplies, consent) {
+			syncInfo, err := syncer.GetUsersyncInfo(gdprApplies, consent)
+			if err == nil {
+				bidder.UsersyncInfo = syncInfo
+			} else {
+				glog.Errorf("Failed to get usersync info for %s: %v", syncerCode, err)
+			}
+		}
+		blabels.CookieFlag = pbsmetrics.CookieFlagNo
+		if ex.SkipNoCookies() {
+			skip = true
+		}
+	}
+	return skip
+}
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 8fa21eca..7328d3c1 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -22,6 +22,9 @@ import (
 	"github.com/prebid/prebid-server/exchange"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/pbsmetrics"
+	"github.com/prebid/prebid-server/privacy"
+	"github.com/prebid/prebid-server/privacy/ccpa"
+	"github.com/prebid/prebid-server/privacy/gdpr"
 	"github.com/prebid/prebid-server/stored_requests"
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
 	"github.com/prebid/prebid-server/usersync"
@@ -379,22 +382,19 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope
 		req.Imp[0].TagID = slot
 	}
 
-	//In the AMP endpoint the consent string found in the http.Request query overrides that of the prebid query
-	queryConsentString := httpRequest.FormValue("gdpr_consent")
-	if queryConsentString != "" {
-		jsonMsg := json.RawMessage(`{"consent":"` + queryConsentString + `"}`)
-		// If nil, initialize
-		if req.User == nil {
-			req.User = &openrtb.User{Ext: jsonMsg}
-		} else if req.User.Ext == nil {
-			req.User.Ext = jsonMsg
-		} else { // req.User.Ext != nil, keep whatever is in there and only substitute the consent string
-			var parserErr error
-			req.User.Ext, parserErr = jsonparser.Set(req.User.Ext, []byte(`"`+queryConsentString+`"`), "consent")
-			if parserErr != nil {
-				return parserErr
-			}
-		}
+	gdprConsent := getQueryParam(httpRequest, "gdpr_consent")
+	ccpaValue := getQueryParam(httpRequest, "us_privacy")
+	privacyPolicies := privacy.Policies{
+		GDPR: gdpr.Policy{
+			Consent: gdprConsent,
+		},
+		CCPA: ccpa.Policy{
+			Value: ccpaValue,
+		},
+	}
+
+	if err := privacyPolicies.Write(req); err != nil {
+		return err
 	}
 
 	if timeout, err := strconv.ParseInt(httpRequest.FormValue("timeout"), 10, 64); err == nil {
@@ -404,6 +404,17 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope
 	return nil
 }
 
+func getQueryParam(httpRequest *http.Request, name string) string {
+	values, ok := httpRequest.URL.Query()[name]
+
+	if !ok || len(values) == 0 {
+		return ""
+	}
+
+	// return first value of the query param, matching the behavior of httpRequest.FormValue
+	return values[0]
+}
+
 func makeFormatReplacement(overrideWidth uint64, overrideHeight uint64, width uint64, height uint64, multisize string) []openrtb.Format {
 	if overrideWidth != 0 && overrideHeight != 0 {
 		return []openrtb.Format{{
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index fd180359..61ea929e 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -444,6 +444,10 @@ func fillAndValidateNative(n *openrtb.Native, impIndex int) error {
 }
 
 func validateNativeContextTypes(cType native.ContextType, cSubtype native.ContextSubType, impIndex int) error {
+	if cType == 0 {
+		// Context is only recommended, so none is a valid type.
+		return nil
+	}
 	if cType < native.ContextTypeContent || cType > native.ContextTypeProduct {
 		return fmt.Errorf("request.imp[%d].native.request.context is invalid. See https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=39", impIndex)
 	}
@@ -480,6 +484,10 @@ func validateNativeContextTypes(cType native.ContextType, cSubtype native.Contex
 }
 
 func validateNativePlacementType(pt native.PlacementType, impIndex int) error {
+	if pt == 0 {
+		// Placement Type is only reccomended, not required.
+		return nil
+	}
 	if pt < native.PlacementTypeFeed || pt > native.PlacementTypeRecommendationWidget {
 		return fmt.Errorf("request.imp[%d].native.request.plcmttype is invalid. See https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=40", impIndex)
 	}
@@ -491,23 +499,37 @@ func fillAndValidateNativeAssets(assets []nativeRequests.Asset, impIndex int) er
 		return fmt.Errorf("request.imp[%d].native.request.assets must be an array containing at least one object", impIndex)
 	}
 
+	assetIDs := make(map[int64]struct{}, len(assets))
+
+	// If none of the asset IDs are defined by the caller, then prebid server should assign its own unique IDs. But
+	// if the caller did assign its own asset IDs, then prebid server will respect those IDs
+	assignAssetIDs := true
+	for i := 0; i < len(assets); i++ {
+		assignAssetIDs = assignAssetIDs && (assets[i].ID == 0)
+	}
+
 	for i := 0; i < len(assets); i++ {
-		// Per the OpenRTB spec docs, this is a "unique asset ID, assigned by exchange. Typically a counter for the array"
-		// To avoid conflict with the Request, we'll return a 400 if the Request _did_ define this ID,
-		// and then populate it as the spec suggests.
 		if err := validateNativeAsset(assets[i], impIndex, i); err != nil {
 			return err
 		}
-		assets[i].ID = int64(i)
+
+		if assignAssetIDs {
+			assets[i].ID = int64(i)
+			continue
+		}
+
+		// Each asset should have a unique ID thats assigned by the caller
+		if _, ok := assetIDs[assets[i].ID]; ok {
+			return fmt.Errorf("request.imp[%d].native.request.assets[%d].id is already being used by another asset. Each asset ID must be unique.", impIndex, i)
+		}
+
+		assetIDs[assets[i].ID] = struct{}{}
 	}
+
 	return nil
 }
 
 func validateNativeAsset(asset nativeRequests.Asset, impIndex int, assetIndex int) error {
-	if asset.ID != 0 {
-		return fmt.Errorf(`request.imp[%d].native.request.assets[%d].id must not be defined. Prebid Server will set this automatically, using the index of the asset in the array as the ID`, impIndex, assetIndex)
-	}
-
 	assetErr := "request.imp[%d].native.request.assets[%d] must define exactly one of {title, img, video, data}"
 	foundType := false
 
@@ -523,9 +545,7 @@ func validateNativeAsset(asset nativeRequests.Asset, impIndex int, assetIndex in
 			return fmt.Errorf(assetErr, impIndex, assetIndex)
 		}
 		foundType = true
-		if err := validateNativeAssetImg(asset.Img, impIndex, assetIndex); err != nil {
-			return err
-		}
+		// It is technically valid to have neither w/h nor wmin/hmin, so no check
 	}
 
 	if asset.Video != nil {
@@ -587,19 +607,6 @@ func validateNativeEventTracker(tracker nativeRequests.EventTracker, impIndex in
 	return nil
 }
 
-func validateNativeAssetImg(image *nativeRequests.Image, impIndex int, assetIndex int) error {
-	// Note that w, wmin, h, and hmin cannot be negative because these variables use unsigned ints.
-	// Those fail during the standard json.Unmarshal() call.
-	if image.W == 0 && image.WMin == 0 {
-		return fmt.Errorf(`request.imp[%d].native.request.assets[%d].img must contain at least one of "w" or "wmin"`, impIndex, assetIndex)
-	}
-	if image.H == 0 && image.HMin == 0 {
-		return fmt.Errorf(`request.imp[%d].native.request.assets[%d].img must contain at least one of "h" or "hmin"`, impIndex, assetIndex)
-	}
-
-	return nil
-}
-
 func validateNativeAssetVideo(video *nativeRequests.Video, impIndex int, assetIndex int) error {
 	if len(video.MIMEs) < 1 {
 		return fmt.Errorf("request.imp[%d].native.request.assets[%d].video.mimes must be an array with at least one MIME type", impIndex, assetIndex)
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/assets-with-dup-ids.json b/endpoints/openrtb2/sample-requests/invalid-native/assets-with-dup-ids.json
new file mode 100644
index 00000000..dfece8cf
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/invalid-native/assets-with-dup-ids.json
@@ -0,0 +1,18 @@
+{
+    "context": 1,
+    "plcmttype": 1,
+    "assets": [
+        {
+            "id": 1,
+            "img": {
+                "wmin": 30
+            }
+        },
+        {
+            "id": 1,
+            "title": {
+                "len": 20
+            }
+        }
+    ]
+}
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/assets-with-partial-ids.json b/endpoints/openrtb2/sample-requests/invalid-native/assets-with-partial-ids.json
new file mode 100644
index 00000000..291ae8d7
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/invalid-native/assets-with-partial-ids.json
@@ -0,0 +1,22 @@
+{
+    "context": 1,
+    "plcmttype": 1,
+    "assets": [
+        {
+            "id": 1,
+            "img": {
+                "wmin": 30
+            }
+        },
+        {
+            "title": {
+                "len": 20
+            }
+        },
+        {
+            "img": {
+                "wmin": 50
+            }
+        }
+    ]
+}
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/asset-img-no-hmin.json b/endpoints/openrtb2/sample-requests/valid-native/asset-img-no-hmin.json
similarity index 100%
rename from endpoints/openrtb2/sample-requests/invalid-native/asset-img-no-hmin.json
rename to endpoints/openrtb2/sample-requests/valid-native/asset-img-no-hmin.json
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/asset-img-no-wmin.json b/endpoints/openrtb2/sample-requests/valid-native/asset-img-no-wmin.json
similarity index 100%
rename from endpoints/openrtb2/sample-requests/invalid-native/asset-img-no-wmin.json
rename to endpoints/openrtb2/sample-requests/valid-native/asset-img-no-wmin.json
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/asset-with-id.json b/endpoints/openrtb2/sample-requests/valid-native/asset-with-id.json
similarity index 60%
rename from endpoints/openrtb2/sample-requests/invalid-native/asset-with-id.json
rename to endpoints/openrtb2/sample-requests/valid-native/asset-with-id.json
index 6f2eee53..7c55888b 100644
--- a/endpoints/openrtb2/sample-requests/invalid-native/asset-with-id.json
+++ b/endpoints/openrtb2/sample-requests/valid-native/asset-with-id.json
@@ -3,11 +3,10 @@
     "plcmttype": 1,
     "assets": [
         {
-            "id": 2,
+            "id": 1,
             "img": {
-                "hmin": 30,
-                "wmin": 20
+                "wmin": 30
             }
         }
     ]
-}
\ No newline at end of file
+}
diff --git a/endpoints/openrtb2/sample-requests/valid-native/asset-with-no-id.json b/endpoints/openrtb2/sample-requests/valid-native/asset-with-no-id.json
new file mode 100644
index 00000000..a9bc57ea
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/valid-native/asset-with-no-id.json
@@ -0,0 +1,11 @@
+{
+    "context": 1,
+    "plcmttype": 1,
+    "assets": [
+        {
+            "img": {
+                "wmin": 30
+            }
+        }
+    ]
+}
diff --git a/endpoints/openrtb2/sample-requests/valid-native/assets-with-unique-ids.json b/endpoints/openrtb2/sample-requests/valid-native/assets-with-unique-ids.json
new file mode 100644
index 00000000..10692b9a
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/valid-native/assets-with-unique-ids.json
@@ -0,0 +1,18 @@
+{
+    "context": 1,
+    "plcmttype": 1,
+    "assets": [
+        {
+            "id": 1,
+            "img": {
+                "wmin": 30
+            }
+        },
+        {
+            "id": 2,
+            "title": {
+                "len": 20
+            }
+        }
+    ]
+}
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/request-no-context.json b/endpoints/openrtb2/sample-requests/valid-native/request-no-context.json
similarity index 100%
rename from endpoints/openrtb2/sample-requests/invalid-native/request-no-context.json
rename to endpoints/openrtb2/sample-requests/valid-native/request-no-context.json
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/request-no-plcmttype.json b/endpoints/openrtb2/sample-requests/valid-native/request-plcmttype-empty.json
similarity index 100%
rename from endpoints/openrtb2/sample-requests/invalid-native/request-no-plcmttype.json
rename to endpoints/openrtb2/sample-requests/valid-native/request-plcmttype-empty.json
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index 61f4f3e7..1f234b85 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -91,7 +91,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	}
 	requestJson, err := ioutil.ReadAll(lr)
 	if err != nil {
-		handleError(labels, w, []error{err}, ao)
+		handleError(&labels, w, []error{err}, &ao)
 		return
 	}
 
@@ -102,27 +102,27 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 
 	if err != nil {
 		if deps.cfg.VideoStoredRequestRequired {
-			handleError(labels, w, []error{err}, ao)
+			handleError(&labels, w, []error{err}, &ao)
 			return
 		}
 	} else {
 		storedRequest, errs := deps.loadStoredVideoRequest(context.Background(), storedRequestId)
 		if len(errs) > 0 {
-			handleError(labels, w, errs, ao)
+			handleError(&labels, w, errs, &ao)
 			return
 		}
 
 		//merge incoming req with stored video req
 		resolvedRequest, err = jsonpatch.MergePatch(storedRequest, requestJson)
 		if err != nil {
-			handleError(labels, w, []error{err}, ao)
+			handleError(&labels, w, []error{err}, &ao)
 			return
 		}
 	}
 	//unmarshal and validate combined result
 	videoBidReq, errL, podErrors := deps.parseVideoRequest(resolvedRequest)
 	if len(errL) > 0 {
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 
@@ -130,7 +130,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	if deps.defaultRequest {
 		if err := json.Unmarshal(deps.defReqJSON, bidReq); err != nil {
 			err = fmt.Errorf("Invalid JSON in Default Request Settings: %s", err)
-			handleError(labels, w, []error{err}, ao)
+			handleError(&labels, w, []error{err}, &ao)
 			return
 		}
 	}
@@ -154,7 +154,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		}
 		err := errors.New(fmt.Sprintf("all pods are incorrect: %s", strings.Join(resPodErr, "; ")))
 		errL = append(errL, err)
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 
@@ -166,7 +166,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 
 	errL = deps.validateRequest(bidReq)
 	if len(errL) > 0 {
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 
@@ -194,7 +194,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 
 	if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil {
 		errL = append(errL, acctIdErr)
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 	//execute auction logic
@@ -203,7 +203,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	ao.Response = response
 	if err != nil {
 		errL := []error{err}
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 
@@ -211,7 +211,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	bidResp, err := buildVideoResponse(response, podErrors)
 	if err != nil {
 		errL := []error{err}
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 	if bidReq.Test == 1 {
@@ -222,7 +222,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 	//resp, err := json.Marshal(response)
 	if err != nil {
 		errL := []error{err}
-		handleError(labels, w, errL, ao)
+		handleError(&labels, w, errL, &ao)
 		return
 	}
 
@@ -238,7 +238,7 @@ func cleanupVideoBidRequest(videoReq *openrtb_ext.BidRequestVideo, podErrors []P
 	return videoReq
 }
 
-func handleError(labels pbsmetrics.Labels, w http.ResponseWriter, errL []error, ao analytics.AuctionObject) {
+func handleError(labels *pbsmetrics.Labels, w http.ResponseWriter, errL []error, ao *analytics.AuctionObject) {
 	labels.RequestStatus = pbsmetrics.RequestStatusErr
 	var errors string
 	var status int = http.StatusInternalServerError
diff --git a/endpoints/setuid.go b/endpoints/setuid.go
index 21c95979..53d8f43b 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"errors"
 	"net/http"
+	"net/url"
 	"strconv"
 	"strings"
 	"time"
@@ -25,8 +26,14 @@ const (
 	chromeiOSStrLen = len(chromeiOSStr)
 )
 
-func NewSetUIDEndpoint(cfg config.HostCookie, perms gdpr.Permissions, pbsanalytics analytics.PBSAnalyticsModule, metrics pbsmetrics.MetricsEngine) httprouter.Handle {
+func NewSetUIDEndpoint(cfg config.HostCookie, syncers map[openrtb_ext.BidderName]usersync.Usersyncer, perms gdpr.Permissions, pbsanalytics analytics.PBSAnalyticsModule, metrics pbsmetrics.MetricsEngine) httprouter.Handle {
 	cookieTTL := time.Duration(cfg.TTL) * 24 * time.Hour
+
+	validFamilyNameMap := make(map[string]struct{})
+	for _, s := range syncers {
+		validFamilyNameMap[s.FamilyName()] = struct{}{}
+	}
+
 	return httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
 		so := analytics.SetUIDObject{
 			Status: http.StatusOK,
@@ -38,50 +45,51 @@ func NewSetUIDEndpoint(cfg config.HostCookie, perms gdpr.Permissions, pbsanalyti
 		pc := usersync.ParsePBSCookieFromRequest(r, &cfg)
 		if !pc.AllowSyncs() {
 			w.WriteHeader(http.StatusUnauthorized)
-			metrics.RecordUserIDSet(pbsmetrics.UserLabels{Action: pbsmetrics.RequestActionOptOut})
+			metrics.RecordUserIDSet(pbsmetrics.UserLabels{
+				Action: pbsmetrics.RequestActionOptOut,
+			})
 			so.Status = http.StatusUnauthorized
 			return
 		}
 
 		query := r.URL.Query()
-		bidder := query.Get("bidder")
-		if shouldReturn, status, body := preventSyncsGDPR(query.Get("gdpr"), query.Get("gdpr_consent"), perms); shouldReturn {
-			w.WriteHeader(status)
-			w.Write([]byte(body))
-			metrics.RecordUserIDSet(pbsmetrics.UserLabels{
-				Action: pbsmetrics.RequestActionGDPR,
-				Bidder: openrtb_ext.BidderName(bidder),
-			})
-			so.Status = status
-			return
-		}
 
-		if err := validateBidder(bidder); err != nil {
+		familyName, err := getFamilyName(query, validFamilyNameMap)
+		if err != nil {
 			w.WriteHeader(http.StatusBadRequest)
 			w.Write([]byte(err.Error()))
 			metrics.RecordUserIDSet(pbsmetrics.UserLabels{
 				Action: pbsmetrics.RequestActionErr,
-				Bidder: openrtb_ext.BidderName(bidder),
 			})
 			so.Status = http.StatusBadRequest
 			return
 		}
-		so.Bidder = bidder
+		so.Bidder = familyName
+
+		if shouldReturn, status, body := preventSyncsGDPR(query.Get("gdpr"), query.Get("gdpr_consent"), perms); shouldReturn {
+			w.WriteHeader(status)
+			w.Write([]byte(body))
+			metrics.RecordUserIDSet(pbsmetrics.UserLabels{
+				Action: pbsmetrics.RequestActionGDPR,
+				Bidder: openrtb_ext.BidderName(familyName),
+			})
+			so.Status = status
+			return
+		}
 
 		uid := query.Get("uid")
 		so.UID = uid
 
-		var err error
 		if uid == "" {
-			pc.Unsync(bidder)
+			pc.Unsync(familyName)
 		} else {
-			err = pc.TrySync(bidder, uid)
+			err = pc.TrySync(familyName, uid)
 		}
 
 		if err == nil {
 			labels := pbsmetrics.UserLabels{
 				Action: pbsmetrics.RequestActionSet,
-				Bidder: openrtb_ext.BidderName(bidder),
+				Bidder: openrtb_ext.BidderName(familyName),
 			}
 			metrics.RecordUserIDSet(labels)
 			so.Success = true
@@ -92,16 +100,19 @@ func NewSetUIDEndpoint(cfg config.HostCookie, perms gdpr.Permissions, pbsanalyti
 	})
 }
 
-func validateBidder(bidderName string) error {
-	if bidderName == "" {
-		return errors.New(`"bidder" query param is required`)
+func getFamilyName(query url.Values, validFamilyNameMap map[string]struct{}) (string, error) {
+	// The family name is bound to the 'bidder' query param. In most cases, these values are the same.
+	familyName := query.Get("bidder")
+
+	if familyName == "" {
+		return "", errors.New(`"bidder" query param is required`)
 	}
 
-	// Fixes #1054
-	if _, ok := openrtb_ext.BidderMap[bidderName]; !ok {
-		return errors.New("The bidder name provided is not supported by Prebid Server")
+	if _, ok := validFamilyNameMap[familyName]; !ok {
+		return "", errors.New("The bidder name provided is not supported by Prebid Server")
 	}
-	return nil
+
+	return familyName, nil
 }
 
 // siteCookieCheck scans the input User Agent string to check if browser is Chrome and browser version is greater than the minimum version for adding the SameSite cookie attribute
diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go
index 395b4a5d..1135a0e0 100644
--- a/exchange/adapter_map.go
+++ b/exchange/adapter_map.go
@@ -2,10 +2,11 @@ package exchange
 
 import (
 	"fmt"
-	"github.com/prebid/prebid-server/adapters/kubient"
 	"net/http"
 	"strings"
 
+	"github.com/prebid/prebid-server/adapters/kubient"
+
 	"github.com/prebid/prebid-server/adapters"
 	ttx "github.com/prebid/prebid-server/adapters/33across"
 	"github.com/prebid/prebid-server/adapters/adform"
@@ -61,6 +62,7 @@ import (
 
 func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapters.BidderInfos) map[openrtb_ext.BidderName]adaptedBidder {
 	ortbBidders := map[openrtb_ext.BidderName]adapters.Bidder{
+		openrtb_ext.Bidder33Across:     ttx.New33AcrossBidder(cfg.Adapters[string(openrtb_ext.Bidder33Across)].Endpoint),
 		openrtb_ext.BidderAdform:       adform.NewAdformBidder(client, cfg.Adapters[string(openrtb_ext.BidderAdform)].Endpoint),
 		openrtb_ext.BidderAdkernel:     adkernel.NewAdkernelAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernel))].Endpoint),
 		openrtb_ext.BidderAdkernelAdn:  adkernelAdn.NewAdkernelAdnAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernelAdn))].Endpoint),
@@ -73,46 +75,45 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter
 		openrtb_ext.BidderBrightroll: brightroll.NewBrightrollBidder(cfg.Adapters[string(openrtb_ext.BidderBrightroll)].Endpoint),
 		openrtb_ext.BidderConsumable: consumable.NewConsumableBidder(cfg.Adapters[string(openrtb_ext.BidderConsumable)].Endpoint),
 		openrtb_ext.BidderDatablocks: datablocks.NewDatablocksBidder(cfg.Adapters[string(openrtb_ext.BidderDatablocks)].Endpoint),
-		openrtb_ext.BidderEngageBDR:  engagebdr.NewEngageBDRBidder(client, cfg.Adapters[string(openrtb_ext.BidderEngageBDR)].Endpoint),
 		openrtb_ext.BidderEmxDigital: emx_digital.NewEmxDigitalBidder(cfg.Adapters[string(openrtb_ext.BidderEmxDigital)].Endpoint),
+		openrtb_ext.BidderEngageBDR:  engagebdr.NewEngageBDRBidder(client, cfg.Adapters[string(openrtb_ext.BidderEngageBDR)].Endpoint),
 		openrtb_ext.BidderEPlanning:  eplanning.NewEPlanningBidder(client, cfg.Adapters[string(openrtb_ext.BidderEPlanning)].Endpoint),
-		openrtb_ext.BidderGumGum:     gumgum.NewGumGumBidder(cfg.Adapters[string(openrtb_ext.BidderGumGum)].Endpoint),
-		openrtb_ext.BidderLockerDome: lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint),
-		openrtb_ext.BidderOpenx:      openx.NewOpenxBidder(cfg.Adapters[string(openrtb_ext.BidderOpenx)].Endpoint),
-		openrtb_ext.BidderPubmatic:   pubmatic.NewPubmaticBidder(client, cfg.Adapters[string(openrtb_ext.BidderPubmatic)].Endpoint),
-		openrtb_ext.BidderRhythmone:  rhythmone.NewRhythmoneBidder(cfg.Adapters[string(openrtb_ext.BidderRhythmone)].Endpoint),
-		openrtb_ext.BidderRTBHouse:   rtbhouse.NewRTBHouseBidder(cfg.Adapters[string(openrtb_ext.BidderRTBHouse)].Endpoint),
+		openrtb_ext.BidderFacebook: audienceNetwork.NewFacebookBidder(
+			client,
+			cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].PlatformID,
+			cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].AppSecret),
+		openrtb_ext.BidderGamma:          gamma.NewGammaBidder(cfg.Adapters[string(openrtb_ext.BidderGamma)].Endpoint),
+		openrtb_ext.BidderGamoshi:        gamoshi.NewGamoshiBidder(cfg.Adapters[string(openrtb_ext.BidderGamoshi)].Endpoint),
+		openrtb_ext.BidderGrid:           grid.NewGridBidder(cfg.Adapters[string(openrtb_ext.BidderGrid)].Endpoint),
+		openrtb_ext.BidderGumGum:         gumgum.NewGumGumBidder(cfg.Adapters[string(openrtb_ext.BidderGumGum)].Endpoint),
+		openrtb_ext.BidderImprovedigital: improvedigital.NewImprovedigitalBidder(cfg.Adapters[string(openrtb_ext.BidderImprovedigital)].Endpoint),
+		openrtb_ext.BidderKubient:        kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint),
+		openrtb_ext.BidderLockerDome:     lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint),
+		openrtb_ext.BidderMgid:           mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint),
+		openrtb_ext.BidderOpenx:          openx.NewOpenxBidder(cfg.Adapters[string(openrtb_ext.BidderOpenx)].Endpoint),
+		openrtb_ext.BidderPubmatic:       pubmatic.NewPubmaticBidder(client, cfg.Adapters[string(openrtb_ext.BidderPubmatic)].Endpoint),
+		openrtb_ext.BidderRhythmone:      rhythmone.NewRhythmoneBidder(cfg.Adapters[string(openrtb_ext.BidderRhythmone)].Endpoint),
+		openrtb_ext.BidderRTBHouse:       rtbhouse.NewRTBHouseBidder(cfg.Adapters[string(openrtb_ext.BidderRTBHouse)].Endpoint),
 		openrtb_ext.BidderRubicon: rubicon.NewRubiconBidder(
 			client,
 			cfg.Adapters[string(openrtb_ext.BidderRubicon)].Endpoint,
 			cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Username,
 			cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Password,
 			cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Tracker),
+
 		openrtb_ext.BidderSharethrough:     sharethrough.NewSharethroughBidder(cfg.Adapters[string(openrtb_ext.BidderSharethrough)].Endpoint),
 		openrtb_ext.BidderSomoaudience:     somoaudience.NewSomoaudienceBidder(cfg.Adapters[string(openrtb_ext.BidderSomoaudience)].Endpoint),
-		openrtb_ext.BidderSovrn:            sovrn.NewSovrnBidder(client, cfg.Adapters[string(openrtb_ext.BidderSovrn)].Endpoint),
-		openrtb_ext.Bidder33Across:         ttx.New33AcrossBidder(cfg.Adapters[string(openrtb_ext.Bidder33Across)].Endpoint),
-		openrtb_ext.BidderGrid:             grid.NewGridBidder(cfg.Adapters[string(openrtb_ext.BidderGrid)].Endpoint),
 		openrtb_ext.BidderSonobi:           sonobi.NewSonobiBidder(client, cfg.Adapters[string(openrtb_ext.BidderSonobi)].Endpoint),
+		openrtb_ext.BidderSovrn:            sovrn.NewSovrnBidder(client, cfg.Adapters[string(openrtb_ext.BidderSovrn)].Endpoint),
 		openrtb_ext.BidderSynacormedia:     synacormedia.NewSynacorMediaBidder(cfg.Adapters[string(openrtb_ext.BidderSynacormedia)].Endpoint),
+		openrtb_ext.BidderTappx:            tappx.NewTappxBidder(client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderTappx))].Endpoint),
 		openrtb_ext.BidderTriplelift:       triplelift.NewTripleliftBidder(client, cfg.Adapters[string(openrtb_ext.BidderTriplelift)].Endpoint),
 		openrtb_ext.BidderTripleliftNative: triplelift_native.NewTripleliftNativeBidder(client, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].ExtraAdapterInfo),
 		openrtb_ext.BidderUnruly:           unruly.NewUnrulyBidder(client, cfg.Adapters[string(openrtb_ext.BidderUnruly)].Endpoint),
+		openrtb_ext.BidderVerizonMedia:     verizonmedia.NewVerizonMediaBidder(client, cfg.Adapters[string(openrtb_ext.BidderVerizonMedia)].Endpoint),
+		openrtb_ext.BidderVisx:             visx.NewVisxBidder(cfg.Adapters[string(openrtb_ext.BidderVisx)].Endpoint),
 		openrtb_ext.BidderVrtcal:           vrtcal.NewVrtcalBidder(cfg.Adapters[string(openrtb_ext.BidderVrtcal)].Endpoint),
 		openrtb_ext.BidderYieldmo:          yieldmo.NewYieldmoBidder(cfg.Adapters[string(openrtb_ext.BidderYieldmo)].Endpoint),
-		openrtb_ext.BidderGamma:            gamma.NewGammaBidder(cfg.Adapters[string(openrtb_ext.BidderGamma)].Endpoint),
-		openrtb_ext.BidderVisx:             visx.NewVisxBidder(cfg.Adapters[string(openrtb_ext.BidderVisx)].Endpoint),
-		openrtb_ext.BidderGamoshi:          gamoshi.NewGamoshiBidder(cfg.Adapters[string(openrtb_ext.BidderGamoshi)].Endpoint),
-		openrtb_ext.BidderMgid:             mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint),
-		openrtb_ext.BidderImprovedigital:   improvedigital.NewImprovedigitalBidder(cfg.Adapters[string(openrtb_ext.BidderImprovedigital)].Endpoint),
-		openrtb_ext.BidderTappx:            tappx.NewTappxBidder(client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderTappx))].Endpoint),
-		openrtb_ext.BidderVerizonMedia:     verizonmedia.NewVerizonMediaBidder(client, cfg.Adapters[string(openrtb_ext.BidderVerizonMedia)].Endpoint),
-		openrtb_ext.BidderKubient:          kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint),
-		openrtb_ext.BidderFacebook: audienceNetwork.NewFacebookBidder(
-			client,
-			cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].PlatformID,
-			cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].AppID,
-			cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].AppSecret),
 	}
 
 	legacyBidders := map[openrtb_ext.BidderName]adapters.Adapter{
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 76a4d302..e92e813f 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -615,11 +615,11 @@ func (e *exchange) getBidCacheInfo(bid *pbsOrtbBid, auc *auction) (openrtb_ext.E
 		if cacheUUID, found = auc.cacheIds[bid.bid]; found {
 			cacheInfo.CacheId = cacheUUID
 			extCacheHost, extCachePath = e.cache.GetExtCacheData()
-			cacheInfo.Url = extCacheHost + "/" + extCachePath + "?uuid=" + cacheUUID
+			cacheInfo.Url = extCacheHost + extCachePath + "?uuid=" + cacheUUID
 		} else if cacheUUID, found = auc.vastCacheIds[bid.bid]; found {
 			cacheInfo.CacheId = cacheUUID
 			extCacheHost, extCachePath = e.cache.GetExtCacheData()
-			cacheInfo.Url = extCacheHost + "/" + extCachePath + "?uuid=" + cacheUUID
+			cacheInfo.Url = extCacheHost + extCachePath + "?uuid=" + cacheUUID
 		}
 	}
 	return cacheInfo, found
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index f7f54976..ce84480d 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -21,11 +21,11 @@ type BidderName string
 // Please keep these (and the BidderMap) alphabetized to minimize merge conflicts among adapter submissions.
 const (
 	Bidder33Across         BidderName = "33across"
+	BidderAdform           BidderName = "adform"
 	BidderAdkernel         BidderName = "adkernel"
 	BidderAdkernelAdn      BidderName = "adkernelAdn"
 	BidderAdpone           BidderName = "adpone"
 	BidderAdtelligent      BidderName = "adtelligent"
-	BidderAdform           BidderName = "adform"
 	BidderAdvangelists     BidderName = "advangelists"
 	BidderAppnexus         BidderName = "appnexus"
 	BidderBeachfront       BidderName = "beachfront"
@@ -34,6 +34,7 @@ const (
 	BidderConversant       BidderName = "conversant"
 	BidderDatablocks       BidderName = "datablocks"
 	BidderEmxDigital       BidderName = "emx_digital"
+	BidderEngageBDR        BidderName = "engagebdr"
 	BidderEPlanning        BidderName = "eplanning"
 	BidderFacebook         BidderName = "audienceNetwork"
 	BidderGamma            BidderName = "gamma"
@@ -54,48 +55,49 @@ const (
 	BidderRubicon          BidderName = "rubicon"
 	BidderSharethrough     BidderName = "sharethrough"
 	BidderSomoaudience     BidderName = "somoaudience"
-	BidderSovrn            BidderName = "sovrn"
 	BidderSonobi           BidderName = "sonobi"
+	BidderSovrn            BidderName = "sovrn"
 	BidderSynacormedia     BidderName = "synacormedia"
+	BidderTappx            BidderName = "tappx"
 	BidderTriplelift       BidderName = "triplelift"
 	BidderTripleliftNative BidderName = "triplelift_native"
 	BidderUnruly           BidderName = "unruly"
 	BidderVerizonMedia     BidderName = "verizonmedia"
+	BidderVisx             BidderName = "visx"
 	BidderVrtcal           BidderName = "vrtcal"
 	BidderYieldmo          BidderName = "yieldmo"
-	BidderVisx             BidderName = "visx"
-	BidderTappx            BidderName = "tappx"
-	BidderEngageBDR        BidderName = "engagebdr"
 )
 
 // BidderMap stores all the valid OpenRTB 2.x Bidders in the project. This map *must not* be mutated.
 var BidderMap = map[string]BidderName{
 	"33across":          Bidder33Across,
+	"adform":            BidderAdform,
 	"adkernel":          BidderAdkernel,
 	"adkernelAdn":       BidderAdkernelAdn,
 	"adpone":            BidderAdpone,
 	"adtelligent":       BidderAdtelligent,
-	"adform":            BidderAdform,
 	"advangelists":      BidderAdvangelists,
 	"appnexus":          BidderAppnexus,
 	"beachfront":        BidderBeachfront,
-	"audienceNetwork":   BidderFacebook,
 	"brightroll":        BidderBrightroll,
 	"consumable":        BidderConsumable,
 	"conversant":        BidderConversant,
 	"datablocks":        BidderDatablocks,
 	"emx_digital":       BidderEmxDigital,
+	"engagebdr":         BidderEngageBDR,
 	"eplanning":         BidderEPlanning,
+	"audienceNetwork":   BidderFacebook,
 	"gamma":             BidderGamma,
 	"gamoshi":           BidderGamoshi,
 	"grid":              BidderGrid,
 	"gumgum":            BidderGumGum,
 	"improvedigital":    BidderImprovedigital,
 	"ix":                BidderIx,
+	"kubient":           BidderKubient,
 	"lifestreet":        BidderLifestreet,
 	"lockerdome":        BidderLockerDome,
-	"openx":             BidderOpenx,
 	"mgid":              BidderMgid,
+	"openx":             BidderOpenx,
 	"pubmatic":          BidderPubmatic,
 	"pulsepoint":        BidderPulsepoint,
 	"rhythmone":         BidderRhythmone,
@@ -103,19 +105,17 @@ var BidderMap = map[string]BidderName{
 	"rubicon":           BidderRubicon,
 	"sharethrough":      BidderSharethrough,
 	"somoaudience":      BidderSomoaudience,
-	"sovrn":             BidderSovrn,
 	"sonobi":            BidderSonobi,
+	"sovrn":             BidderSovrn,
 	"synacormedia":      BidderSynacormedia,
+	"tappx":             BidderTappx,
 	"triplelift":        BidderTriplelift,
 	"triplelift_native": BidderTripleliftNative,
 	"unruly":            BidderUnruly,
 	"verizonmedia":      BidderVerizonMedia,
+	"visx":              BidderVisx,
 	"vrtcal":            BidderVrtcal,
 	"yieldmo":           BidderYieldmo,
-	"visx":              BidderVisx,
-	"tappx":             BidderTappx,
-	"engagebdr":         BidderEngageBDR,
-	"kubient":           BidderKubient,
 }
 
 // BidderList returns the values of the BidderMap
@@ -134,9 +134,9 @@ func (name BidderName) MarshalJSON() ([]byte, error) {
 func (name *BidderName) String() string {
 	if name == nil {
 		return ""
-	} else {
-		return string(*name)
 	}
+
+	return string(*name)
 }
 
 // The BidderParamValidator is used to enforce bidrequest.imp[i].ext.{anyBidder} values.
diff --git a/openrtb_ext/regs.go b/openrtb_ext/regs.go
index 4f33846f..56a5179d 100644
--- a/openrtb_ext/regs.go
+++ b/openrtb_ext/regs.go
@@ -6,4 +6,7 @@ type ExtRegs struct {
 	// GDPR should be "1" if the caller believes the user is subject to GDPR laws, "0" if not, and undefined
 	// if it's unknown. For more info on this parameter, see: https://iabtechlab.com/wp-content/uploads/2018/02/OpenRTB_Advisory_GDPR_2018-02.pdf
 	GDPR *int8 `json:"gdpr,omitempty"`
+
+	// USPrivacy should be a four character string, see: https://iabtechlab.com/wp-content/uploads/2019/11/OpenRTB-Extension-U.S.-Privacy-IAB-Tech-Lab.pdf
+	USPrivacy string `json:"us_privacy,omitempty"`
 }
diff --git a/openrtb_ext/user.go b/openrtb_ext/user.go
index 308823a4..520d73a6 100644
--- a/openrtb_ext/user.go
+++ b/openrtb_ext/user.go
@@ -1,5 +1,7 @@
 package openrtb_ext
 
+import "encoding/json"
+
 // ExtUser defines the contract for bidrequest.user.ext
 type ExtUser struct {
 	// Consent is a GDPR consent string. See "Advised Extensions" of
@@ -36,15 +38,11 @@ type ExtUserEid struct {
 	Source string          `json:"source"`
 	ID     string          `json:"id,omitempty"`
 	Uids   []ExtUserEidUid `json:"uids,omitempty"`
+	Ext    json.RawMessage `json:"ext,omitempty"`
 }
 
 // ExtUserEidUid defines the contract for bidrequest.user.ext.eids[i].uids[j]
 type ExtUserEidUid struct {
-	ID  string            `json:"id"`
-	Ext *ExtUserEidUidExt `json:"ext,omitempty"`
-}
-
-// ExtUserEidUidExt defines the contract for bidrequest.user.ext.eids[i].uids[j].ext
-type ExtUserEidUidExt struct {
-	RtiPartner string `json:"rtiPartner,omitempty"`
+	ID  string          `json:"id"`
+	Ext json.RawMessage `json:"ext,omitempty"`
 }
diff --git a/prebid_cache_client/client.go b/prebid_cache_client/client.go
index 88da5c3a..6da69f68 100644
--- a/prebid_cache_client/client.go
+++ b/prebid_cache_client/client.go
@@ -72,9 +72,11 @@ type clientImpl struct {
 func (c *clientImpl) GetExtCacheData() (string, string) {
 	path := c.externalCachePath
 	if path == "/" {
+		// Only the slash for the path, remove it to empty
 		path = ""
-	} else if strings.Index(path, "/") == 0 {
-		path = strings.TrimLeft(path, "/")
+	} else if len(path) > 0 && !strings.HasPrefix(path, "/") {
+		// Path defined but does not start with "/", prepend it
+		path = "/" + path
 	}
 
 	return c.externalCacheHost, path
diff --git a/privacy/ccpa/ccpa.go b/privacy/ccpa/ccpa.go
new file mode 100644
index 00000000..612c1073
--- /dev/null
+++ b/privacy/ccpa/ccpa.go
@@ -0,0 +1,33 @@
+package ccpa
+
+import (
+	"encoding/json"
+
+	"github.com/buger/jsonparser"
+	"github.com/mxmCherry/openrtb"
+)
+
+// Policy represents the CCPA regulation for an OpenRTB bid request.
+type Policy struct {
+	Value string
+}
+
+// Write mutates an OpenRTB bid request with the context of the CCPA policy.
+func (p Policy) Write(req *openrtb.BidRequest) error {
+	if p.Value == "" {
+		return nil
+	}
+
+	if req.Regs == nil {
+		req.Regs = &openrtb.Regs{}
+	}
+
+	if req.Regs.Ext == nil {
+		req.Regs.Ext = json.RawMessage(`{"us_privacy":"` + p.Value + `"}`)
+		return nil
+	}
+
+	var err error
+	req.Regs.Ext, err = jsonparser.Set(req.Regs.Ext, []byte(`"`+p.Value+`"`), "us_privacy")
+	return err
+}
diff --git a/privacy/gdpr/gdpr.go b/privacy/gdpr/gdpr.go
new file mode 100644
index 00000000..60d6fe8e
--- /dev/null
+++ b/privacy/gdpr/gdpr.go
@@ -0,0 +1,33 @@
+package gdpr
+
+import (
+	"encoding/json"
+
+	"github.com/buger/jsonparser"
+	"github.com/mxmCherry/openrtb"
+)
+
+// Policy represents the GDPR regulation of an OpenRTB bid request.
+type Policy struct {
+	Consent string
+}
+
+// Write mutates an OpenRTB bid request with the context of the GDPR policy.
+func (p Policy) Write(req *openrtb.BidRequest) error {
+	if p.Consent == "" {
+		return nil
+	}
+
+	if req.User == nil {
+		req.User = &openrtb.User{}
+	}
+
+	if req.User.Ext == nil {
+		req.User.Ext = json.RawMessage(`{"consent":"` + p.Consent + `"}`)
+		return nil
+	}
+
+	var err error
+	req.User.Ext, err = jsonparser.Set(req.User.Ext, []byte(`"`+p.Consent+`"`), "consent")
+	return err
+}
diff --git a/privacy/policies.go b/privacy/policies.go
new file mode 100644
index 00000000..ebe34ef5
--- /dev/null
+++ b/privacy/policies.go
@@ -0,0 +1,35 @@
+package privacy
+
+import (
+	"github.com/mxmCherry/openrtb"
+
+	"github.com/prebid/prebid-server/privacy/ccpa"
+	"github.com/prebid/prebid-server/privacy/gdpr"
+)
+
+// Policies represents the privacy regulations for an OpenRTB bid request.
+type Policies struct {
+	GDPR gdpr.Policy
+	CCPA ccpa.Policy
+}
+
+type policyWriter interface {
+	Write(req *openrtb.BidRequest) error
+}
+
+// Write mutates an OpenRTB bid request with the policies applied.
+func (p Policies) Write(req *openrtb.BidRequest) error {
+	return writePolicies(req, []policyWriter{
+		p.GDPR, p.CCPA,
+	})
+}
+
+func writePolicies(req *openrtb.BidRequest, writers []policyWriter) error {
+	for _, writer := range writers {
+		if err := writer.Write(req); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
diff --git a/router/router.go b/router/router.go
index 06a842c6..19946391 100644
--- a/router/router.go
+++ b/router/router.go
@@ -265,7 +265,7 @@ func New(cfg *config.Configuration, rateConvertor *currencies.RateConverter) (r
 		PBSAnalytics:     pbsAnalytics,
 	}
 
-	r.GET("/setuid", endpoints.NewSetUIDEndpoint(cfg.HostCookie, gdprPerms, pbsAnalytics, r.MetricsEngine))
+	r.GET("/setuid", endpoints.NewSetUIDEndpoint(cfg.HostCookie, syncers, gdprPerms, pbsAnalytics, r.MetricsEngine))
 	r.GET("/getuids", endpoints.NewGetUIDsEndpoint(cfg.HostCookie))
 	r.POST("/optout", userSyncDeps.OptOut)
 	r.GET("/optout", userSyncDeps.OptOut)
diff --git a/stored_requests/events/http/http.go b/stored_requests/events/http/http.go
index 94d8de65..4f141dac 100644
--- a/stored_requests/events/http/http.go
+++ b/stored_requests/events/http/http.go
@@ -6,6 +6,7 @@ import (
 	"encoding/json"
 	"io/ioutil"
 	httpCore "net/http"
+	"net/url"
 	"time"
 
 	"golang.org/x/net/context/ctxhttp"
@@ -94,10 +95,33 @@ func (e *HTTPEvents) refresh(ticker <-chan time.Time) {
 		select {
 		case thisTime := <-ticker:
 			thisTimeInUTC := thisTime.UTC()
-			thisEndpoint := e.Endpoint + "?last-modified=" + e.lastUpdate.Format(time.RFC3339)
+
+			// Parse the endpoint url defined
+			endpointUrl, urlErr := url.Parse(e.Endpoint)
+
+			// Error with url parsing
+			if urlErr != nil {
+				glog.Errorf("Disabling refresh HTTP cache from GET '%s': %v", e.Endpoint, urlErr)
+				return
+			}
+
+			// Parse the url query string
+			urlQuery := endpointUrl.Query()
+
+			// See the last-modified query param
+			urlQuery.Set("last-modified", e.lastUpdate.Format(time.RFC3339))
+
+			// Rebuild
+			endpointUrl.RawQuery = urlQuery.Encode()
+
+			// Convert to string
+			endpoint := endpointUrl.String()
+
+			glog.Infof("Refreshing HTTP cache from GET '%s'", endpoint)
+
 			ctx, cancel := e.ctxProducer()
-			resp, err := ctxhttp.Get(ctx, e.client, thisEndpoint)
-			if respObj, ok := e.parse(thisEndpoint, resp, err); ok {
+			resp, err := ctxhttp.Get(ctx, e.client, endpoint)
+			if respObj, ok := e.parse(endpoint, resp, err); ok {
 				invalidations := events.Invalidation{
 					Requests: extractInvalidations(respObj.StoredRequests),
 					Imps:     extractInvalidations(respObj.StoredImps),
diff --git a/usersync/cookie.go b/usersync/cookie.go
index 13aa7f38..461fbba8 100644
--- a/usersync/cookie.go
+++ b/usersync/cookie.go
@@ -100,6 +100,15 @@ func NewPBSCookie() *PBSCookie {
 	}
 }
 
+// NewPBSCookie returns an empty PBSCookie with optOut enabled
+func NewPBSCookieWithOptOut() *PBSCookie {
+	return &PBSCookie{
+		uids:     make(map[string]uidWithExpiry),
+		optOut:   true,
+		birthday: timestamp(),
+	}
+}
+
 // AllowSyncs is true if the user lets bidders sync cookies, and false otherwise.
 func (cookie *PBSCookie) AllowSyncs() bool {
 	return cookie != nil && !cookie.optOut
diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go
index 7a43fe83..2db827cb 100644
--- a/usersync/usersyncers/syncer.go
+++ b/usersync/usersyncers/syncer.go
@@ -74,16 +74,18 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderConversant, conversant.NewConversantSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderDatablocks, datablocks.NewDatablocksSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderEmxDigital, emx_digital.NewEMXDigitalSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderEngageBDR, engagebdr.NewEngageBDRSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderEPlanning, eplanning.NewEPlanningSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderFacebook, audienceNetwork.NewFacebookSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderGamma, gamma.NewGammaSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderGamoshi, gamoshi.NewGamoshiSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderGrid, grid.NewGridSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderGumGum, gumgum.NewGumGumSyncer)
-	insertIntoMap(cfg, syncers, openrtb_ext.BidderEngageBDR, engagebdr.NewEngageBDRSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderImprovedigital, improvedigital.NewImprovedigitalSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderIx, ix.NewIxSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderLifestreet, lifestreet.NewLifestreetSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderLockerDome, lockerdome.NewLockerDomeSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderMgid, mgid.NewMgidSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderOpenx, openx.NewOpenxSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderPubmatic, pubmatic.NewPubmaticSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderPulsepoint, pulsepoint.NewPulsepointSyncer)
@@ -92,18 +94,16 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderRubicon, rubicon.NewRubiconSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderSharethrough, sharethrough.NewSharethroughSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderSomoaudience, somoaudience.NewSomoaudienceSyncer)
-	insertIntoMap(cfg, syncers, openrtb_ext.BidderSovrn, sovrn.NewSovrnSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderSonobi, sonobi.NewSonobiSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderSovrn, sovrn.NewSovrnSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderSynacormedia, synacormedia.NewSynacorMediaSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderTriplelift, triplelift.NewTripleliftSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderTripleliftNative, triplelift_native.NewTripleliftSyncer)
-	insertIntoMap(cfg, syncers, openrtb_ext.BidderVrtcal, vrtcal.NewVrtcalSyncer)
-	insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldmo, yieldmo.NewYieldmoSyncer)
-	insertIntoMap(cfg, syncers, openrtb_ext.BidderVisx, visx.NewVisxSyncer)
-	insertIntoMap(cfg, syncers, openrtb_ext.BidderGamoshi, gamoshi.NewGamoshiSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderUnruly, unruly.NewUnrulySyncer)
-	insertIntoMap(cfg, syncers, openrtb_ext.BidderMgid, mgid.NewMgidSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderVerizonMedia, verizonmedia.NewVerizonMediaSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderVisx, visx.NewVisxSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderVrtcal, vrtcal.NewVrtcalSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldmo, yieldmo.NewYieldmoSyncer)
 
 	return syncers
 }


Test patch:
diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/banner.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/banner.json
index d0a85aa6..632629b5 100644
--- a/adapters/audienceNetwork/audienceNetworktest/exemplary/banner.json
+++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/banner.json
@@ -43,9 +43,6 @@
                     "Accept": [
                         "application/json"
                     ],
-                    "Auth-Token": [
-                        "test-app-id|test-app-secret"
-                    ],
                     "Content-Type": [
                         "application/json;charset=utf-8"
                     ],
diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json
index d31714ec..630e26d3 100644
--- a/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json
+++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json
@@ -44,9 +44,6 @@
                     "Accept": [
                         "application/json"
                     ],
-                    "Auth-Token": [
-                        "test-app-id|test-app-secret"
-                    ],
                     "Content-Type": [
                         "application/json;charset=utf-8"
                     ],
diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json
index 2223422c..288c7c14 100644
--- a/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json
+++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json
@@ -37,9 +37,6 @@
                     "Accept": [
                         "application/json"
                     ],
-                    "Auth-Token": [
-                        "test-app-id|test-app-secret"
-                    ],
                     "Content-Type": [
                         "application/json;charset=utf-8"
                     ],
diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json
index 070bcabf..15563c2a 100644
--- a/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json
+++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json
@@ -42,9 +42,6 @@
                     "Accept": [
                         "application/json"
                     ],
-                    "Auth-Token": [
-                        "test-app-id|test-app-secret"
-                    ],
                     "Content-Type": [
                         "application/json;charset=utf-8"
                     ],
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json
index 4fd1eb33..0fe836af 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json
@@ -62,9 +62,6 @@
                     "Accept": [
                         "application/json"
                     ],
-                    "Auth-Token": [
-                        "test-app-id|test-app-secret"
-                    ],
                     "Content-Type": [
                         "application/json;charset=utf-8"
                     ],
@@ -142,9 +139,6 @@
                     "Accept": [
                         "application/json"
                     ],
-                    "Auth-Token": [
-                        "test-app-id|test-app-secret"
-                    ],
                     "Content-Type": [
                         "application/json;charset=utf-8"
                     ],
diff --git a/adapters/audienceNetwork/facebook_test.go b/adapters/audienceNetwork/facebook_test.go
index 2f685234..2ce0ef3b 100644
--- a/adapters/audienceNetwork/facebook_test.go
+++ b/adapters/audienceNetwork/facebook_test.go
@@ -38,5 +38,5 @@ type FacebookExt struct {
 }
 
 func TestJsonSamples(t *testing.T) {
-	adapterstest.RunJSONBidderTest(t, "audienceNetworktest", NewFacebookBidder(nil, "test-platform-id", "test-app-id", "test-app-secret"))
+	adapterstest.RunJSONBidderTest(t, "audienceNetworktest", NewFacebookBidder(nil, "test-platform-id", "test-app-secret"))
 }
diff --git a/adapters/rubicon/rubicon_test.go b/adapters/rubicon/rubicon_test.go
index c59e1574..bc3d2e8a 100644
--- a/adapters/rubicon/rubicon_test.go
+++ b/adapters/rubicon/rubicon_test.go
@@ -7,7 +7,6 @@ import (
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
-	"reflect"
 	"testing"
 	"time"
 
@@ -24,6 +23,8 @@ import (
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
+
+	"github.com/stretchr/testify/assert"
 )
 
 type rubiAppendTrackerUrlTestScenario struct {
@@ -64,7 +65,14 @@ type rubiBidInfo struct {
 var rubidata rubiBidInfo
 
 func DummyRubiconServer(w http.ResponseWriter, r *http.Request) {
-	defer r.Body.Close()
+	defer func() {
+		err := r.Body.Close()
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+	}()
+
 	body, err := ioutil.ReadAll(r.Body)
 	if err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -268,37 +276,28 @@ func TestRubiconBasicResponse(t *testing.T) {
 	an, ctx, pbReq := CreatePrebidRequest(server, t)
 
 	bids, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
-	if err != nil {
-		t.Fatalf("Should not have gotten an error: %v", err)
-	}
-	if len(bids) != 3 {
-		t.Fatalf("Received %d bids instead of 3", len(bids))
-	}
+	assert.Nil(t, err, "Should not have gotten an error: %v", err)
+	assert.Equal(t, 3, len(bids), "Received %d bids instead of 3", len(bids))
+
 	for _, bid := range bids {
 		matched := false
 		for _, tag := range rubidata.tags {
 			if bid.AdUnitCode == tag.code {
 				matched = true
-				if bid.BidderCode != "rubicon" {
-					t.Errorf("Incorrect BidderCode '%s'", bid.BidderCode)
-				}
-				if bid.Price != tag.bid {
-					t.Errorf("Incorrect bid price '%.2f' expected '%.2f'", bid.Price, tag.bid)
-				}
-				if bid.Adm != tag.content {
-					t.Errorf("Incorrect bid markup '%s' expected '%s'", bid.Adm, tag.content)
-				}
-				if !reflect.DeepEqual(bid.AdServerTargeting, tag.adServerTargeting) {
-					t.Errorf("Incorrect targeting '%+v' expected '%+v'", bid.AdServerTargeting, tag.adServerTargeting)
-				}
-				if bid.CreativeMediaType != tag.mediaType {
-					t.Errorf("Incorrect media type '%s' expected '%s'", bid.CreativeMediaType, tag.mediaType)
-				}
+
+				assert.Equal(t, "rubicon", bid.BidderCode, "Incorrect BidderCode '%s'", bid.BidderCode)
+
+				assert.Equal(t, tag.bid, bid.Price, "Incorrect bid price '%.2f' expected '%.2f'", bid.Price, tag.bid)
+
+				assert.Equal(t, tag.content, bid.Adm, "Incorrect bid markup '%s' expected '%s'", bid.Adm, tag.content)
+
+				assert.Equal(t, bid.AdServerTargeting, tag.adServerTargeting,
+					"Incorrect targeting '%+v' expected '%+v'", bid.AdServerTargeting, tag.adServerTargeting)
+
+				assert.Equal(t, tag.mediaType, bid.CreativeMediaType, "Incorrect media type '%s' expected '%s'", bid.CreativeMediaType, tag.mediaType)
 			}
 		}
-		if !matched {
-			t.Errorf("Received bid for unknown ad unit '%s'", bid.AdUnitCode)
-		}
+		assert.True(t, matched, "Received bid for unknown ad unit '%s'", bid.AdUnitCode)
 	}
 
 	// same test but with request timing out
@@ -307,23 +306,15 @@ func TestRubiconBasicResponse(t *testing.T) {
 	defer cancel()
 
 	bids, err = an.Call(ctx, pbReq, pbReq.Bidders[0])
-	if err == nil {
-		t.Fatalf("Should have gotten a timeout error: %v", err)
-	}
+	assert.NotNil(t, err, "Should have gotten a timeout error: %v", err)
 }
 
 func TestRubiconUserSyncInfo(t *testing.T) {
 	an := NewRubiconAdapter(adapters.DefaultHTTPAdapterConfig, "uri", "xuser", "xpass", "pbs-test-tracker")
 
-	adapterName := an.Name()
-	if adapterName != "rubicon" {
-		t.Errorf("Name '%s' != 'rubicon'", adapterName)
-	}
+	assert.Equal(t, "rubicon", an.Name(), "Name '%s' != 'rubicon'", an.Name())
 
-	skipNoCookies := an.SkipNoCookies()
-	if skipNoCookies != false {
-		t.Errorf("SkipNoCookies should be false")
-	}
+	assert.False(t, an.SkipNoCookies(), "SkipNoCookies should be false")
 }
 
 func getTestSizes() map[int]openrtb.Format {
@@ -339,7 +330,6 @@ func getTestSizes() map[int]openrtb.Format {
 }
 
 func TestParseSizes(t *testing.T) {
-
 	SIZE_ID := getTestSizes()
 
 	sizes := []openrtb.Format{
@@ -347,18 +337,10 @@ func TestParseSizes(t *testing.T) {
 		SIZE_ID[15],
 	}
 	primary, alt, err := parseRubiconSizes(sizes)
-	if err != nil {
-		t.Errorf("Parsing error: %v", err)
-	}
-	if primary != 15 {
-		t.Errorf("Primary %d != 15", primary)
-	}
-	if len(alt) != 1 {
-		t.Fatalf("Alt not len 1")
-	}
-	if alt[0] != 10 {
-		t.Errorf("Alt not 10: %d", alt[0])
-	}
+	assert.Nil(t, err, "Parsing error: %v", err)
+	assert.Equal(t, 15, primary, "Primary %d != 15", primary)
+	assert.Equal(t, 1, len(alt), "Alt not len 1")
+	assert.Equal(t, 10, alt[0], "Alt not 10: %d", alt[0])
 
 	sizes = []openrtb.Format{
 		{
@@ -368,31 +350,17 @@ func TestParseSizes(t *testing.T) {
 		SIZE_ID[15],
 	}
 	primary, alt, err = parseRubiconSizes(sizes)
-
-	if err != nil {
-		t.Errorf("Shouldn't have thrown error for invalid size 1111x1111 since we still have a valid one")
-	}
-	if primary != 15 {
-		t.Errorf("Primary %d != 15", primary)
-	}
-	if len(alt) != 0 {
-		t.Fatalf("Alt len %d != 0", len(alt))
-	}
+	assert.Nil(t, err, "Shouldn't have thrown error for invalid size 1111x1111 since we still have a valid one")
+	assert.Equal(t, 15, primary, "Primary %d != 15", primary)
+	assert.Equal(t, 0, len(alt), "Alt len %d != 0", len(alt))
 
 	sizes = []openrtb.Format{
 		SIZE_ID[15],
 	}
 	primary, alt, err = parseRubiconSizes(sizes)
-
-	if err != nil {
-		t.Errorf("Parsing error: %v", err)
-	}
-	if primary != 15 {
-		t.Errorf("Primary %d != 15", primary)
-	}
-	if len(alt) != 0 {
-		t.Fatalf("Alt len %d != 0", len(alt))
-	}
+	assert.Nil(t, err, "Parsing error: %v", err)
+	assert.Equal(t, 15, primary, "Primary %d != 15", primary)
+	assert.Equal(t, 0, len(alt), "Alt len %d != 0", len(alt))
 
 	sizes = []openrtb.Format{
 		{
@@ -401,16 +369,9 @@ func TestParseSizes(t *testing.T) {
 		},
 	}
 	primary, alt, err = parseRubiconSizes(sizes)
-
-	if err == nil {
-		t.Errorf("Parsing error: %v", err)
-	}
-	if primary != 0 {
-		t.Errorf("Primary %d != 0", primary)
-	}
-	if len(alt) != 0 {
-		t.Errorf("Alt len %d != 0", len(alt))
-	}
+	assert.NotNil(t, err, "Parsing error: %v", err)
+	assert.Equal(t, 0, primary, "Primary %d != 15", primary)
+	assert.Equal(t, 0, len(alt), "Alt len %d != 0", len(alt))
 }
 
 func TestMASAlgorithm(t *testing.T) {
@@ -526,17 +487,19 @@ func TestMASAlgorithm(t *testing.T) {
 
 	for _, test := range testStubs {
 		prim, alt, err := parseRubiconSizes(test.input)
-		if prim != test.output.primary {
-			t.Fatalf("Error in parsing rubicon sizes: MAS algorithm fail at primary: testcase %v", test.input)
-		}
-		if len(alt) != len(test.output.alt) {
-			t.Fatalf("Error in parsing rubicon sizes: MAS Algorithm fail at alt: testcase %v", test.input)
-		}
-		if (err != nil && !test.output.ok) || (err == nil && test.output.ok) {
-			t.Fatalf("Error in parsing rubicon sizes: MAS Algorithm fail at throwing error: testcase %v", test.input)
-		}
-	}
 
+		assert.Equal(t, test.output.primary, prim,
+			"Error in parsing rubicon sizes: MAS algorithm fail at primary: testcase %v", test.input)
+
+		assert.Equal(t, len(test.output.alt), len(alt),
+			"Error in parsing rubicon sizes: MAS Algorithm fail at alt: testcase %v", test.input)
+
+		assert.False(t, err != nil && !test.output.ok,
+			"Error in parsing rubicon sizes: MAS Algorithm fail at throwing error: testcase %v", test.input)
+
+		assert.False(t, err == nil && test.output.ok,
+			"Error in parsing rubicon sizes: MAS Algorithm fail at throwing error: testcase %v", test.input)
+	}
 }
 
 func TestAppendTracker(t *testing.T) {
@@ -555,14 +518,11 @@ func TestAppendTracker(t *testing.T) {
 
 	for _, scenario := range testScenarios {
 		res := appendTrackerToUrl(scenario.source, scenario.tracker)
-		if res != scenario.expected {
-			t.Fatalf("Failed to convert '%s' to '%s'", res, scenario.expected)
-		}
+		assert.Equal(t, scenario.expected, res, "Failed to convert '%s' to '%s'", res, scenario.expected)
 	}
 }
 
 func TestNoContentResponse(t *testing.T) {
-
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		w.WriteHeader(http.StatusNoContent)
 	}))
@@ -571,22 +531,15 @@ func TestNoContentResponse(t *testing.T) {
 	an, ctx, pbReq := CreatePrebidRequest(server, t)
 	bids, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
 
-	if len(bids) != 0 {
-		t.Fatalf("Length of bids should be 0 instead of: %v", len(bids))
-	}
+	assert.Empty(t, bids, "Length of bids should be 0 instead of: %v", len(bids))
 
-	if pbReq.Bidders[0].Debug[0].StatusCode != 204 {
-		t.Fatalf("StatusCode should be 204 instead of: %v", pbReq.Bidders[0].Debug[0].StatusCode)
-	}
-
-	if err != nil {
-		t.Fatalf("Should not have gotten an error: %v", err)
-	}
+	assert.Equal(t, 204, pbReq.Bidders[0].Debug[0].StatusCode,
+		"StatusCode should be 204 instead of: %v", pbReq.Bidders[0].Debug[0].StatusCode)
 
+	assert.Nil(t, err, "Should not have gotten an error: %v", err)
 }
 
 func TestNotFoundResponse(t *testing.T) {
-
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		w.WriteHeader(http.StatusNotFound)
 	}))
@@ -595,22 +548,16 @@ func TestNotFoundResponse(t *testing.T) {
 	an, ctx, pbReq := CreatePrebidRequest(server, t)
 	_, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
 
-	if pbReq.Bidders[0].Debug[0].StatusCode != 404 {
-		t.Fatalf("StatusCode should be 404 instead of: %v", pbReq.Bidders[0].Debug[0].StatusCode)
-	}
-
-	if err == nil {
-		t.Fatalf("Should have gotten an error: %v", err)
-	}
+	assert.Equal(t, 404, pbReq.Bidders[0].Debug[0].StatusCode,
+		"StatusCode should be 404 instead of: %v", pbReq.Bidders[0].Debug[0].StatusCode)
 
-	if !strings.HasPrefix(err.Error(), "HTTP status 404") {
-		t.Fatalf("Should start with 'HTTP status' instead of: %v", err.Error())
-	}
+	assert.NotNil(t, err, "Should have gotten an error: %v", err)
 
+	assert.True(t, strings.HasPrefix(err.Error(), "HTTP status 404"),
+		"Should start with 'HTTP status' instead of: %v", err.Error())
 }
 
 func TestWrongFormatResponse(t *testing.T) {
-
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		w.Write([]byte("This is text."))
 	}))
@@ -619,22 +566,16 @@ func TestWrongFormatResponse(t *testing.T) {
 	an, ctx, pbReq := CreatePrebidRequest(server, t)
 	_, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
 
-	if pbReq.Bidders[0].Debug[0].StatusCode != 200 {
-		t.Fatalf("StatusCode should be 200 instead of: %v", pbReq.Bidders[0].Debug[0].StatusCode)
-	}
-
-	if err == nil {
-		t.Fatalf("Should have gotten an error: %v", err)
-	}
+	assert.Equal(t, 200, pbReq.Bidders[0].Debug[0].StatusCode,
+		"StatusCode should be 200 instead of: %v", pbReq.Bidders[0].Debug[0].StatusCode)
 
-	if !strings.HasPrefix(err.Error(), "invalid character") {
-		t.Fatalf("Should start with 'invalid character' instead of: %v", err)
-	}
+	assert.NotNil(t, err, "Should have gotten an error: %v", err)
 
+	assert.True(t, strings.HasPrefix(err.Error(), "invalid character"),
+		"Should start with 'invalid character' instead of: %v", err)
 }
 
 func TestZeroSeatBidResponse(t *testing.T) {
-
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		resp := openrtb.BidResponse{
 			ID:      "test-response-id",
@@ -650,18 +591,12 @@ func TestZeroSeatBidResponse(t *testing.T) {
 	an, ctx, pbReq := CreatePrebidRequest(server, t)
 	bids, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
 
-	if len(bids) != 0 {
-		t.Fatalf("Length of bids should be 0 instead of: %v", len(bids))
-	}
-
-	if err != nil {
-		t.Fatalf("Should not have gotten an error: %v", err)
-	}
+	assert.Empty(t, bids, "Length of bids should be 0 instead of: %v", len(bids))
 
+	assert.Nil(t, err, "Should not have gotten an error: %v", err)
 }
 
 func TestEmptyBidResponse(t *testing.T) {
-
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		resp := openrtb.BidResponse{
 			ID:    "test-response-id",
@@ -682,18 +617,12 @@ func TestEmptyBidResponse(t *testing.T) {
 	an, ctx, pbReq := CreatePrebidRequest(server, t)
 	bids, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
 
-	if len(bids) != 0 {
-		t.Fatalf("Length of bids should be 0 instead of: %v", len(bids))
-	}
-
-	if err != nil {
-		t.Fatalf("Should not have gotten an error: %v", err)
-	}
+	assert.Empty(t, bids, "Length of bids should be 0 instead of: %v", len(bids))
 
+	assert.Nil(t, err, "Should not have gotten an error: %v", err)
 }
 
 func TestWrongBidIdResponse(t *testing.T) {
-
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		resp := openrtb.BidResponse{
 			ID:    "test-response-id",
@@ -721,18 +650,12 @@ func TestWrongBidIdResponse(t *testing.T) {
 	an, ctx, pbReq := CreatePrebidRequest(server, t)
 	bids, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
 
-	if len(bids) != 0 {
-		t.Fatalf("Length of bids should be 0 instead of: %v", len(bids))
-	}
+	assert.Empty(t, bids, "Length of bids should be 0 instead of: %v", len(bids))
 
-	if err == nil {
-		t.Fatalf("Should not have gotten an error: %v", err)
-	}
-
-	if !strings.HasPrefix(err.Error(), "Unknown ad unit code") {
-		t.Fatalf("Should start with 'Unknown ad unit code' instead of: %v", err)
-	}
+	assert.NotNil(t, err, "Should not have gotten an error: %v", err)
 
+	assert.True(t, strings.HasPrefix(err.Error(), "Unknown ad unit code"),
+		"Should start with 'Unknown ad unit code' instead of: %v", err)
 }
 
 func TestZeroPriceBidResponse(t *testing.T) {
@@ -764,10 +687,7 @@ func TestZeroPriceBidResponse(t *testing.T) {
 	an, ctx, pbReq := CreatePrebidRequest(server, t)
 	b, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
 
-	if b != nil {
-		t.Fatalf("\n\n\n0 price bids are being included %d, err : %v", len(b), err)
-	}
-
+	assert.Nil(t, b, "\n\n\n0 price bids are being included %d, err : %v", len(b), err)
 }
 
 func TestDifferentRequest(t *testing.T) {
@@ -782,10 +702,9 @@ func TestDifferentRequest(t *testing.T) {
 		ID:   "com.test",
 		Name: "testApp",
 	}
+
 	_, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
-	if err == nil {
-		t.Fatalf("Should have gotten an error: %v", err)
-	}
+	assert.NotNil(t, err, "Should have gotten an error: %v", err)
 
 	// set app back to normal
 	pbReq.App = nil
@@ -793,9 +712,7 @@ func TestDifferentRequest(t *testing.T) {
 	// test video media type
 	pbReq.Bidders[0].AdUnits[0].MediaTypes = []pbs.MediaType{pbs.MEDIA_TYPE_VIDEO}
 	_, err = an.Call(ctx, pbReq, pbReq.Bidders[0])
-	if err == nil {
-		t.Fatalf("Should have gotten an error: %v", err)
-	}
+	assert.NotNil(t, err, "Should have gotten an error: %v", err)
 
 	// set media back to normal
 	pbReq.Bidders[0].AdUnits[0].MediaTypes = []pbs.MediaType{pbs.MEDIA_TYPE_BANNER}
@@ -803,9 +720,7 @@ func TestDifferentRequest(t *testing.T) {
 	// test wrong params
 	pbReq.Bidders[0].AdUnits[0].Params = json.RawMessage(fmt.Sprintf("{\"zoneId\": %s, \"siteId\": %d, \"visitor\": %s, \"inventory\": %s}", "zma", rubidata.siteID, rubidata.visitorTargeting, rubidata.inventoryTargeting))
 	_, err = an.Call(ctx, pbReq, pbReq.Bidders[0])
-	if err == nil {
-		t.Fatalf("Should have gotten an error: %v", err)
-	}
+	assert.NotNil(t, err, "Should have gotten an error: %v", err)
 
 	// set params back to normal
 	pbReq.Bidders[0].AdUnits[0].Params = json.RawMessage(fmt.Sprintf("{\"zoneId\": %d, \"siteId\": %d, \"accountId\": %d, \"visitor\": %s, \"inventory\": %s}", 8394, rubidata.siteID, rubidata.accountID, rubidata.visitorTargeting, rubidata.inventoryTargeting))
@@ -829,9 +744,7 @@ func TestDifferentRequest(t *testing.T) {
 	}
 	pbReq.Bidders[0].AdUnits = pbReq.Bidders[0].AdUnits[:len(pbReq.Bidders[0].AdUnits)-1]
 	b, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
-	if err == nil || len(b) != 0 {
-		t.Fatalf("Filtering bids based on ad unit sizes failed. Got %d bids instead of 0", len(b))
-	}
+	assert.NotNil(t, err, "Should have gotten an error: %v", err)
 
 	pbReq.Bidders[0].AdUnits[1].Sizes = []openrtb.Format{
 		{
@@ -842,9 +755,10 @@ func TestDifferentRequest(t *testing.T) {
 		SIZE_ID[15],
 	}
 	b, err = an.Call(ctx, pbReq, pbReq.Bidders[0])
-	if err != nil || len(b) != 1 {
-		t.Fatalf("Filtering bids based on ad unit sizes failed. Got %d bids instead of 1, error = %v", len(b), err)
-	}
+	assert.Nil(t, err, "Should have not gotten an error: %v", err)
+
+	assert.Equal(t, 1, len(b),
+		"Filtering bids based on ad unit sizes failed. Got %d bids instead of 1, error = %v", len(b), err)
 }
 
 func CreatePrebidRequest(server *httptest.Server, t *testing.T) (an *RubiconAdapter, ctx context.Context, pbReq *pbs.PBSRequest) {
@@ -957,18 +871,16 @@ func CreatePrebidRequest(server *httptest.Server, t *testing.T) (an *RubiconAdap
 		Max:     2000,
 	}, cacheClient, &hcc)
 	pbReq.IsDebug = true
-	if err != nil {
-		t.Fatalf("ParsePBSRequest failed: %v", err)
-	}
-	if len(pbReq.Bidders) != 1 {
-		t.Fatalf("ParsePBSRequest returned %d bidders instead of 1", len(pbReq.Bidders))
-	}
-	if pbReq.Bidders[0].BidderCode != "rubicon" {
-		t.Fatalf("ParsePBSRequest returned invalid bidder")
-	}
 
-	ctx = context.TODO()
+	assert.Nil(t, err, "ParsePBSRequest failed: %v", err)
 
+	assert.Equal(t, 1, len(pbReq.Bidders),
+		"ParsePBSRequest returned %d bidders instead of 1", len(pbReq.Bidders))
+
+	assert.Equal(t, "rubicon", pbReq.Bidders[0].BidderCode,
+		"ParsePBSRequest returned invalid bidder")
+
+	ctx = context.TODO()
 	return
 }
 
@@ -1037,15 +949,6 @@ func TestOpenRTBRequest(t *testing.T) {
                     "pref": 0
                 },
 				"eids": [{
-                    "source": "adserver.org",
-                    "uids": [{
-                        "id": "3d50a262-bd8e-4be3-90b8-246291523907",
-                        "ext": {
-                            "rtiPartner": "TDID"
-                        }
-                    }]
-                },
-                {
                     "source": "pubcid",
                     "id": "2402fc76-7b39-4f0e-bfc2-060ef7693648"
 				}]
@@ -1056,36 +959,22 @@ func TestOpenRTBRequest(t *testing.T) {
 
 	reqs, errs := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{})
 
-	if len(errs) > 0 {
-		t.Errorf("Got unexpected errors while building HTTP requests: %v", errs)
-	}
-	if len(reqs) != 2 {
-		t.Fatalf("Unexpected number of HTTP requests. Got %d. Expected %d", len(reqs), 2)
-	}
+	assert.Empty(t, errs, "Got unexpected errors while building HTTP requests: %v", errs)
+	assert.Equal(t, 2, len(reqs), "Unexpected number of HTTP requests. Got %d. Expected %d", len(reqs), 2)
 
 	for i := 0; i < len(reqs); i++ {
 		httpReq := reqs[i]
-		if httpReq.Method != "POST" {
-			t.Errorf("Expected a POST message. Got %s", httpReq.Method)
-		}
+		assert.Equal(t, "POST", httpReq.Method, "Expected a POST message. Got %s", httpReq.Method)
 
 		var rpRequest openrtb.BidRequest
 		if err := json.Unmarshal(httpReq.Body, &rpRequest); err != nil {
 			t.Fatalf("Failed to unmarshal HTTP request: %v", rpRequest)
 		}
 
-		if rpRequest.ID != request.ID {
-			t.Errorf("Bad Request ID. Expected %s, Got %s", request.ID, rpRequest.ID)
-		}
-		if len(rpRequest.Imp) != len(request.Imp) {
-			t.Fatalf("Wrong len(request.Imp). Expected %d, Got %d", len(request.Imp), len(rpRequest.Imp))
-		}
-		if rpRequest.Cur != nil {
-			t.Fatalf("Wrong request.Cur. Expected nil, Got %s", rpRequest.Cur)
-		}
-		if request.Ext != nil {
-			t.Fatalf("Wrong request.ext. Expected nil, Got %v", request.Ext)
-		}
+		assert.Equal(t, request.ID, rpRequest.ID, "Bad Request ID. Expected %s, Got %s", request.ID, rpRequest.ID)
+		assert.Equal(t, len(request.Imp), len(rpRequest.Imp), "Wrong len(request.Imp). Expected %d, Got %d", len(request.Imp), len(rpRequest.Imp))
+		assert.Nil(t, rpRequest.Cur, "Wrong request.Cur. Expected nil, Got %s", rpRequest.Cur)
+		assert.Nil(t, request.Ext, "Wrong request.ext. Expected nil, Got %v", request.Ext)
 
 		if rpRequest.Imp[0].ID == "test-imp-banner-id" {
 			var rpExt rubiconBannerExt
@@ -1093,58 +982,52 @@ func TestOpenRTBRequest(t *testing.T) {
 				t.Fatal("Error unmarshalling request from the outgoing request.")
 			}
 
-			if rpRequest.Imp[0].Banner.Format[0].W != 300 {
-				t.Fatalf("Banner width does not match. Expected %d, Got %d", 300, rpRequest.Imp[0].Banner.Format[0].W)
-			}
-			if rpRequest.Imp[0].Banner.Format[0].H != 250 {
-				t.Fatalf("Banner height does not match. Expected %d, Got %d", 250, rpRequest.Imp[0].Banner.Format[0].H)
-			}
-			if rpRequest.Imp[0].Banner.Format[1].W != 300 {
-				t.Fatalf("Banner width does not match. Expected %d, Got %d", 300, rpRequest.Imp[0].Banner.Format[1].W)
-			}
-			if rpRequest.Imp[0].Banner.Format[1].H != 600 {
-				t.Fatalf("Banner height does not match. Expected %d, Got %d", 600, rpRequest.Imp[0].Banner.Format[1].H)
-			}
+			assert.Equal(t, uint64(300), rpRequest.Imp[0].Banner.Format[0].W,
+				"Banner width does not match. Expected %d, Got %d", 300, rpRequest.Imp[0].Banner.Format[0].W)
+
+			assert.Equal(t, uint64(250), rpRequest.Imp[0].Banner.Format[0].H,
+				"Banner height does not match. Expected %d, Got %d", 250, rpRequest.Imp[0].Banner.Format[0].H)
+
+			assert.Equal(t, uint64(300), rpRequest.Imp[0].Banner.Format[1].W,
+				"Banner width does not match. Expected %d, Got %d", 300, rpRequest.Imp[0].Banner.Format[1].W)
+
+			assert.Equal(t, uint64(600), rpRequest.Imp[0].Banner.Format[1].H,
+				"Banner height does not match. Expected %d, Got %d", 600, rpRequest.Imp[0].Banner.Format[1].H)
 		} else if rpRequest.Imp[0].ID == "test-imp-video-id" {
 			var rpExt rubiconVideoExt
 			if err := json.Unmarshal(rpRequest.Imp[0].Ext, &rpExt); err != nil {
 				t.Fatal("Error unmarshalling request from the outgoing request.")
 			}
 
-			if rpRequest.Imp[0].Video.W != 640 {
-				t.Fatalf("Video width does not match. Expected %d, Got %d", 640, rpRequest.Imp[0].Video.W)
-			}
-			if rpRequest.Imp[0].Video.H != 360 {
-				t.Fatalf("Video height does not match. Expected %d, Got %d", 360, rpRequest.Imp[0].Video.H)
-			}
-			if rpRequest.Imp[0].Video.MIMEs[0] != "video/mp4" {
-				t.Fatalf("Video MIMEs do not match. Expected %s, Got %s", "video/mp4", rpRequest.Imp[0].Video.MIMEs[0])
-			}
-			if rpRequest.Imp[0].Video.MinDuration != 15 {
-				t.Fatalf("Video min duration does not match. Expected %d, Got %d", 15, rpRequest.Imp[0].Video.MinDuration)
-			}
-			if rpRequest.Imp[0].Video.MaxDuration != 30 {
-				t.Fatalf("Video max duration does not match. Expected %d, Got %d", 30, rpRequest.Imp[0].Video.MaxDuration)
-			}
+			assert.Equal(t, uint64(640), rpRequest.Imp[0].Video.W,
+				"Video width does not match. Expected %d, Got %d", 640, rpRequest.Imp[0].Video.W)
+
+			assert.Equal(t, uint64(360), rpRequest.Imp[0].Video.H,
+				"Video height does not match. Expected %d, Got %d", 360, rpRequest.Imp[0].Video.H)
+
+			assert.Equal(t, "video/mp4", rpRequest.Imp[0].Video.MIMEs[0], "Video MIMEs do not match. Expected %s, Got %s", "video/mp4", rpRequest.Imp[0].Video.MIMEs[0])
+
+			assert.Equal(t, int64(15), rpRequest.Imp[0].Video.MinDuration,
+				"Video min duration does not match. Expected %d, Got %d", 15, rpRequest.Imp[0].Video.MinDuration)
+
+			assert.Equal(t, int64(30), rpRequest.Imp[0].Video.MaxDuration,
+				"Video max duration does not match. Expected %d, Got %d", 30, rpRequest.Imp[0].Video.MaxDuration)
 		}
 
-		if rpRequest.User.Ext != nil {
-			var userExt rubiconUserExt
-			if err := json.Unmarshal(rpRequest.User.Ext, &userExt); err != nil {
-				t.Fatal("Error unmarshalling request.user.ext object.")
-			}
-			if userExt.DigiTrust.ID != "some-digitrust-id" || userExt.DigiTrust.KeyV != 1 || userExt.DigiTrust.Pref != 0 {
-				t.Fatal("DigiTrust values are not as expected!")
-			}
-			if userExt.Eids == nil || len(userExt.Eids) != 2 {
-				t.Fatal("Eids values are not as expected!")
-			}
-			if userExt.TpID == nil || len(userExt.TpID) != 1 {
-				t.Fatal("TpID values are not as expected!")
-			}
-		} else {
-			t.Fatalf("User.Ext object should not be nil since it contains a valid digitrust object.")
+		assert.NotNil(t, rpRequest.User.Ext, "User.Ext object should not be nil.")
+
+		var userExt rubiconUserExt
+		if err := json.Unmarshal(rpRequest.User.Ext, &userExt); err != nil {
+			t.Fatal("Error unmarshalling request.user.ext object.")
 		}
+
+		assert.Equal(t, "some-digitrust-id", userExt.DigiTrust.ID, "DigiTrust ID id not as expected!")
+		assert.Equal(t, 1, userExt.DigiTrust.KeyV, "DigiTrust KeyV id not as expected!")
+		assert.Equal(t, 0, userExt.DigiTrust.Pref, "DigiTrust Pref id not as expected!")
+
+		assert.NotNil(t, userExt.Eids)
+		assert.Equal(t, 1, len(userExt.Eids), "Eids values are not as expected!")
+		assert.Contains(t, userExt.Eids, openrtb_ext.ExtUserEid{Source: "pubcid", ID: "2402fc76-7b39-4f0e-bfc2-060ef7693648"})
 	}
 }
 
@@ -1179,29 +1062,20 @@ func TestOpenRTBRequestWithBannerImpEvenIfImpHasVideo(t *testing.T) {
 
 	reqs, errs := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{})
 
-	if len(errs) > 0 {
-		t.Errorf("Got unexpected errors while building HTTP requests: %v", errs)
-	}
-	if len(reqs) != 1 {
-		t.Fatalf("Unexpected number of HTTP requests. Got %d. Expected %d", len(reqs), 1)
-	}
+	assert.Empty(t, errs, "Got unexpected errors while building HTTP requests: %v", errs)
+
+	assert.Equal(t, 1, len(reqs), "Unexpected number of HTTP requests. Got %d. Expected %d", len(reqs), 1)
 
 	rubiconReq := &openrtb.BidRequest{}
 	if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil {
-		t.Errorf("Unexpected error while decoding request: %s", err)
+		t.Fatalf("Unexpected error while decoding request: %s", err)
 	}
 
-	if len(rubiconReq.Imp) != 1 {
-		t.Fatalf("Unexpected number of request impressions. Got %d. Expected %d", len(rubiconReq.Imp), 1)
-	}
+	assert.Equal(t, 1, len(rubiconReq.Imp), "Unexpected number of request impressions. Got %d. Expected %d", len(rubiconReq.Imp), 1)
 
-	if rubiconReq.Imp[0].Video != nil {
-		t.Error("Unexpected video object in request impression")
-	}
+	assert.Nil(t, rubiconReq.Imp[0].Video, "Unexpected video object in request impression")
 
-	if rubiconReq.Imp[0].Banner == nil {
-		t.Error("Banner object must be in request impression")
-	}
+	assert.NotNil(t, rubiconReq.Imp[0].Banner, "Banner object must be in request impression")
 }
 
 func TestOpenRTBRequestWithImpAndAdSlotIncluded(t *testing.T) {
@@ -1239,12 +1113,11 @@ func TestOpenRTBRequestWithImpAndAdSlotIncluded(t *testing.T) {
 
 	rubiconReq := &openrtb.BidRequest{}
 	if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil {
-		t.Errorf("Unexpected error while decoding request: %s", err)
+		t.Fatalf("Unexpected error while decoding request: %s", err)
 	}
 
-	if len(rubiconReq.Imp) != 1 {
-		t.Fatalf("Unexpected number of request impressions. Got %d. Expected %d", len(rubiconReq.Imp), 1)
-	}
+	assert.Equal(t, 1, len(rubiconReq.Imp),
+		"Unexpected number of request impressions. Got %d. Expected %d", len(rubiconReq.Imp), 1)
 
 	var rpImpExt rubiconImpExt
 	if err := json.Unmarshal(rubiconReq.Imp[0].Ext, &rpImpExt); err != nil {
@@ -1256,9 +1129,92 @@ func TestOpenRTBRequestWithImpAndAdSlotIncluded(t *testing.T) {
 		t.Fatal("Error unmarshalling imp.ext.rp.target")
 	}
 
-	if rubiconExtInventory["dfp_ad_unit_code"] != "test-adslot" {
-		t.Fatalf("Unexpected dfp_ad_unit_code: %s", rubiconExtInventory["dfp_ad_unit_code"])
+	assert.Equal(t, "test-adslot", rubiconExtInventory["dfp_ad_unit_code"],
+		"Unexpected dfp_ad_unit_code: %s", rubiconExtInventory["dfp_ad_unit_code"])
+}
+
+func TestOpenRTBRequestWithSpecificExtUserEids(t *testing.T) {
+	SIZE_ID := getTestSizes()
+	bidder := new(RubiconAdapter)
+
+	request := &openrtb.BidRequest{
+		ID: "test-request-id",
+		Imp: []openrtb.Imp{{
+			ID: "test-imp-id",
+			Banner: &openrtb.Banner{
+				Format: []openrtb.Format{
+					SIZE_ID[15],
+					SIZE_ID[10],
+				},
+			},
+			Ext: json.RawMessage(`{"bidder": {
+				"zoneId": 8394,
+				"siteId": 283282,
+				"accountId": 7891
+			}}`),
+		}},
+		User: &openrtb.User{
+			Ext: json.RawMessage(`{"eids": [
+			{
+				"source": "pubcid",
+				"id": "2402fc76-7b39-4f0e-bfc2-060ef7693648"
+			},
+			{
+				"source": "adserver.org",
+				"uids": [{
+					"id": "3d50a262-bd8e-4be3-90b8-246291523907",
+					"ext": {
+						"rtiPartner": "TDID"
+					}
+				}]
+			},
+			{
+				"source": "liveintent.com",
+				"uids": [{
+					"id": "T7JiRRvsRAmh88"
+				}],
+				"ext": {
+					"segments": ["999","888"]
+				}
+			}
+			]}`),
+		},
+	}
+
+	reqs, _ := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{})
+
+	rubiconReq := &openrtb.BidRequest{}
+	if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil {
+		t.Fatalf("Unexpected error while decoding request: %s", err)
 	}
+
+	assert.NotNil(t, rubiconReq.User.Ext, "User.Ext object should not be nil.")
+
+	var userExt rubiconUserExt
+	if err := json.Unmarshal(rubiconReq.User.Ext, &userExt); err != nil {
+		t.Fatal("Error unmarshalling request.user.ext object.")
+	}
+
+	assert.NotNil(t, userExt.Eids)
+	assert.Equal(t, 3, len(userExt.Eids), "Eids values are not as expected!")
+
+	assert.NotNil(t, userExt.TpID)
+	assert.Equal(t, 2, len(userExt.TpID), "TpID values are not as expected!")
+
+	// adserver.org
+	assert.Equal(t, "tdid", userExt.TpID[0].Source, "TpID source value is not as expected!")
+
+	// liveintent.com
+	assert.Equal(t, "liveintent.com", userExt.TpID[1].Source, "TpID source value is not as expected!")
+
+	userExtRPTarget := make(map[string]interface{})
+	if err := json.Unmarshal(userExt.RP.Target, &userExtRPTarget); err != nil {
+		t.Fatal("Error unmarshalling request.user.ext.rp.target object.")
+	}
+
+	assert.Contains(t, userExtRPTarget, "LIseg", "request.user.ext.rp.target value is not as expected!")
+	assert.Contains(t, userExtRPTarget["LIseg"], "888", "No segment with 888 as expected!")
+	assert.Contains(t, userExtRPTarget["LIseg"], "999", "No segment with 999 as expected!")
 }
 
 func TestOpenRTBRequestWithVideoImpEvenIfImpHasBannerButAllRequiredVideoFields(t *testing.T) {
@@ -1296,29 +1252,21 @@ func TestOpenRTBRequestWithVideoImpEvenIfImpHasBannerButAllRequiredVideoFields(t
 
 	reqs, errs := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{})
 
-	if len(errs) > 0 {
-		t.Errorf("Got unexpected errors while building HTTP requests: %v", errs)
-	}
-	if len(reqs) != 1 {
-		t.Fatalf("Unexpected number of HTTP requests. Got %d. Expected %d", len(reqs), 1)
-	}
+	assert.Empty(t, errs, "Got unexpected errors while building HTTP requests: %v", errs)
+
+	assert.Equal(t, 1, len(reqs), "Unexpected number of HTTP requests. Got %d. Expected %d", len(reqs), 1)
 
 	rubiconReq := &openrtb.BidRequest{}
 	if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil {
-		t.Errorf("Unexpected error while decoding request: %s", err)
+		t.Fatalf("Unexpected error while decoding request: %s", err)
 	}
 
-	if len(rubiconReq.Imp) != 1 {
-		t.Fatalf("Unexpected number of request impressions. Got %d. Expected %d", len(rubiconReq.Imp), 1)
-	}
+	assert.Equal(t, 1, len(rubiconReq.Imp),
+		"Unexpected number of request impressions. Got %d. Expected %d", len(rubiconReq.Imp), 1)
 
-	if rubiconReq.Imp[0].Banner != nil {
-		t.Error("Unexpected banner object in request impression")
-	}
+	assert.Nil(t, rubiconReq.Imp[0].Banner, "Unexpected banner object in request impression")
 
-	if rubiconReq.Imp[0].Video == nil {
-		t.Error("Video object must be in request impression")
-	}
+	assert.NotNil(t, rubiconReq.Imp[0].Video, "Video object must be in request impression")
 }
 
 func TestOpenRTBEmptyResponse(t *testing.T) {
@@ -1327,12 +1275,9 @@ func TestOpenRTBEmptyResponse(t *testing.T) {
 	}
 	bidder := new(RubiconAdapter)
 	bidResponse, errs := bidder.MakeBids(nil, nil, httpResp)
-	if bidResponse != nil && len(bidResponse.Bids) != 0 {
-		t.Errorf("Expected 0 bids. Got %d", len(bidResponse.Bids))
-	}
-	if len(errs) != 0 {
-		t.Errorf("Expected 0 errors. Got %d", len(errs))
-	}
+
+	assert.Nil(t, bidResponse, "Expected empty response")
+	assert.Empty(t, errs, "Expected 0 errors. Got %d", len(errs))
 }
 
 func TestOpenRTBSurpriseResponse(t *testing.T) {
@@ -1341,12 +1286,10 @@ func TestOpenRTBSurpriseResponse(t *testing.T) {
 	}
 	bidder := new(RubiconAdapter)
 	bidResponse, errs := bidder.MakeBids(nil, nil, httpResp)
-	if bidResponse != nil && len(bidResponse.Bids) != 0 {
-		t.Errorf("Expected 0 bids. Got %d", len(bidResponse.Bids))
-	}
-	if len(errs) != 1 {
-		t.Errorf("Expected 1 error. Got %d", len(errs))
-	}
+
+	assert.Nil(t, bidResponse, "Expected empty response")
+
+	assert.Equal(t, 1, len(errs), "Expected 1 error. Got %d", len(errs))
 }
 
 func TestOpenRTBStandardResponse(t *testing.T) {
@@ -1384,19 +1327,16 @@ func TestOpenRTBStandardResponse(t *testing.T) {
 	bidder := new(RubiconAdapter)
 	bidResponse, errs := bidder.MakeBids(request, reqData, httpResp)
 
-	if bidResponse != nil && len(bidResponse.Bids) != 1 {
-		t.Fatalf("Expected 1 bid. Got %d", len(bidResponse.Bids))
-	}
-	if len(errs) != 0 {
-		t.Errorf("Expected 0 errors. Got %d", len(errs))
-	}
-	if bidResponse.Bids[0].BidType != openrtb_ext.BidTypeBanner {
-		t.Errorf("Expected a banner bid. Got: %s", bidResponse.Bids[0].BidType)
-	}
+	assert.NotNil(t, bidResponse, "Expected not empty response")
+	assert.Equal(t, 1, len(bidResponse.Bids), "Expected 1 bid. Got %d", len(bidResponse.Bids))
+
+	assert.Empty(t, errs, "Expected 0 errors. Got %d", len(errs))
+
+	assert.Equal(t, openrtb_ext.BidTypeBanner, bidResponse.Bids[0].BidType,
+		"Expected a banner bid. Got: %s", bidResponse.Bids[0].BidType)
+
 	theBid := bidResponse.Bids[0].Bid
-	if theBid.ID != "1234567890" {
-		t.Errorf("Bad bid ID. Expected %s, got %s", "1234567890", theBid.ID)
-	}
+	assert.Equal(t, "1234567890", theBid.ID, "Bad bid ID. Expected %s, got %s", "1234567890", theBid.ID)
 }
 
 func TestOpenRTBCopyBidIdFromResponseIfZero(t *testing.T) {
@@ -1417,9 +1357,7 @@ func TestOpenRTBCopyBidIdFromResponseIfZero(t *testing.T) {
 	bidResponse, _ := bidder.MakeBids(request, reqData, httpResp)
 
 	theBid := bidResponse.Bids[0].Bid
-	if theBid.ID != "1234567890" {
-		t.Errorf("Bad bid ID. Expected %s, got %s", "1234567890", theBid.ID)
-	}
+	assert.Equal(t, "1234567890", theBid.ID, "Bad bid ID. Expected %s, got %s", "1234567890", theBid.ID)
 }
 
 func TestJsonSamples(t *testing.T) {
diff --git a/currencies/rates_test.go b/currencies/rates_test.go
index a8e043a0..915b817d 100644
--- a/currencies/rates_test.go
+++ b/currencies/rates_test.go
@@ -163,6 +163,60 @@ func TestGetRate(t *testing.T) {
 	}
 }
 
+func TestGetRate_ReverseConversion(t *testing.T) {
+
+	// Setup:
+	rates := currencies.NewRates(time.Now(), map[string]map[string]float64{
+		"USD": {
+			"GBP": 0.77208,
+		},
+		"EUR": {
+			"USD": 0.88723,
+		},
+	})
+
+	testCases := []struct {
+		from         string
+		to           string
+		expectedRate float64
+		description  string
+	}{
+		{
+			from:         "USD",
+			to:           "GBP",
+			expectedRate: 0.77208,
+			description:  "case 1 - Rate is present directly and will be returned as is",
+		},
+		{
+			from:         "EUR",
+			to:           "USD",
+			expectedRate: 0.88723,
+			description:  "case 2 - Rate is present directly and will be returned as is (2)",
+		},
+		{
+			from:         "GBP",
+			to:           "USD",
+			expectedRate: 1 / 0.77208,
+			description:  "case 3 - Rate is not present but the reverse one is, will return the computed rate from the reverse entry",
+		},
+		{
+			from:         "USD",
+			to:           "EUR",
+			expectedRate: 1 / 0.88723,
+			description:  "case 4 - Rate is not present but the reverse one is, will return the computed rate from the reverse entry (2)",
+		},
+	}
+
+	for _, tc := range testCases {
+		// Execute:
+		rate, err := rates.GetRate(tc.from, tc.to)
+
+		// Verify:
+		assert.Nil(t, err, "err should be nil: "+tc.description)
+		assert.Equal(t, tc.expectedRate, rate, "rate doesn't match the expected one: "+tc.description)
+	}
+}
+
 func TestGetRate_EmptyRates(t *testing.T) {
 
 	// Setup:
diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go
index 2abee138..e968003a 100644
--- a/endpoints/auction_test.go
+++ b/endpoints/auction_test.go
@@ -341,7 +341,6 @@ func TestCacheVideoOnly(t *testing.T) {
 	bids = append(bids, &rbVideoBid2)
 
 	ctx := context.TODO()
-	w := httptest.NewRecorder()
 	v := viper.New()
 	config.SetupViper(v, "")
 	cfg, err := config.New(v)
@@ -353,7 +352,11 @@ func TestCacheVideoOnly(t *testing.T) {
 		HostVendorID: 0,
 	}, nil, nil)
 	prebid_cache_client.InitPrebidCache(server.URL)
-	cacheVideoOnly(bids, ctx, w, &auction{cfg: cfg, syncers: syncers, gdprPerms: gdprPerms, metricsEngine: &metricsConf.DummyMetricsEngine{}}, &pbsmetrics.Labels{})
+	var labels = &pbsmetrics.Labels{}
+	if err := cacheVideoOnly(bids, ctx, &auction{cfg: cfg, syncers: syncers, gdprPerms: gdprPerms, metricsEngine: &metricsConf.DummyMetricsEngine{}}, labels); err != nil {
+		t.Errorf("Prebid cache failed: %v \n", err)
+		return
+	}
 	if bids[0].CacheID != "UUID-1" {
 		t.Errorf("UUID was '%s', should have been 'UUID-1'", bids[0].CacheID)
 	}
diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go
index 49276f03..0eafaa05 100644
--- a/endpoints/openrtb2/amp_auction_test.go
+++ b/endpoints/openrtb2/amp_auction_test.go
@@ -739,6 +739,102 @@ func TestWidthOnly(t *testing.T) {
 	}.execute(t)
 }
 
+func TestCCPAPresent(t *testing.T) {
+	req, err := getTestBidRequest(false, false, "", "digitrustId")
+	if err != nil {
+		t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err)
+	}
+
+	reqStored := map[string]json.RawMessage{
+		"1": json.RawMessage(req),
+	}
+
+	theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{})
+
+	exchange := &mockAmpExchange{}
+
+	endpoint, _ := NewAmpEndpoint(
+		exchange,
+		newParamsValidator(t),
+		&mockAmpStoredReqFetcher{reqStored},
+		empty_fetcher.EmptyFetcher{},
+		&config.Configuration{MaxRequestSize: maxSize},
+		theMetrics,
+		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		map[string]string{},
+		[]byte{},
+		openrtb_ext.BidderMap,
+	)
+
+	usPrivacy := "1YYN"
+	httpReq := httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1&us_privacy="+usPrivacy, nil)
+	httpRecorder := httptest.NewRecorder()
+	endpoint(httpRecorder, httpReq, nil)
+
+	// Assert our bidRequest was valid
+	if !assert.NotNil(t, exchange.lastRequest, "Endpoint responded with %d: %s", httpRecorder.Code, httpRecorder.Body.String()) {
+		return
+	}
+	// Assert our bidRequest had a valid "Regs" field
+	if !assert.NotNil(t, exchange.lastRequest.Regs) {
+		return
+	}
+	// Assert our bidRequest had a valid "Regs.Ext" field
+	if !assert.NotNil(t, exchange.lastRequest.Regs.Ext) {
+		return
+	}
+
+	var regs openrtb_ext.ExtRegs
+	err = json.Unmarshal(exchange.lastRequest.Regs.Ext, &regs)
+	assert.NoError(t, err)
+	assert.Equal(t, usPrivacy, regs.USPrivacy)
+}
+
+func TestCCPANotPresent(t *testing.T) {
+	req, err := getTestBidRequest(false, false, "", "digitrustId")
+	if err != nil {
+		t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err)
+	}
+
+	reqStored := map[string]json.RawMessage{
+		"1": json.RawMessage(req),
+	}
+
+	theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{})
+
+	exchange := &mockAmpExchange{}
+
+	endpoint, _ := NewAmpEndpoint(
+		exchange,
+		newParamsValidator(t),
+		&mockAmpStoredReqFetcher{reqStored},
+		empty_fetcher.EmptyFetcher{},
+		&config.Configuration{MaxRequestSize: maxSize},
+		theMetrics,
+		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		map[string]string{},
+		[]byte{},
+		openrtb_ext.BidderMap,
+	)
+
+	httpReq := httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1", nil)
+	httpRecorder := httptest.NewRecorder()
+	endpoint(httpRecorder, httpReq, nil)
+
+	// Assert our bidRequest was valid
+	if !assert.NotNil(t, exchange.lastRequest, "Endpoint responded with %d: %s", httpRecorder.Code, httpRecorder.Body.String()) {
+		return
+	}
+
+	// Assert CCPA Signal Not Found
+	if exchange.lastRequest.Regs != nil && exchange.lastRequest.Regs.Ext != nil {
+		var regs openrtb_ext.ExtRegs
+		err = json.Unmarshal(exchange.lastRequest.Regs.Ext, &regs)
+		assert.NoError(t, err)
+		assert.Empty(t, regs.USPrivacy)
+	}
+}
+
 type formatOverrideSpec struct {
 	width          uint64
 	height         uint64
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index 4818c556..7f3d3d4c 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -282,7 +282,6 @@ func TestRejectAccountRequired(t *testing.T) {
 func (gr *getResponseFromDirectory) assert(t *testing.T) {
 	//t *testing.T, dir string, payloadGetter func(*testing.T, []byte) []byte, messageGetter func(*testing.T, []byte) []byte, expectedCode int, aliased bool) {
 	t.Helper()
-	var filename string
 	var filesToAssert []string
 	if gr.file == "" {
 		// Append every file found in `gr.dir` to the `filesToAssert` array and test them all
@@ -300,14 +299,14 @@ func (gr *getResponseFromDirectory) assert(t *testing.T) {
 		fileData = readFile(t, testFile)
 		code, msg := gr.doRequest(t, gr.payloadGetter(t, fileData))
 		fmt.Printf("Processing %s\n", testFile)
-		assertResponseCode(t, filename, code, gr.expectedCode, msg)
+		assertResponseCode(t, testFile, code, gr.expectedCode, msg)
 
 		expectMsg := gr.messageGetter(t, fileData)
 		if gr.description != "" {
 			if len(expectMsg) > 0 {
-				assert.Equal(t, string(expectMsg), msg, "Test failed. %s. Filename: \n", gr.description, filename)
+				assert.Equal(t, string(expectMsg), msg, "Test failed. %s. Filename: \n", gr.description, testFile)
 			} else {
-				assert.Equal(t, string(expectMsg), msg, "file %s had bad response body", filename)
+				assert.Equal(t, string(expectMsg), msg, "file %s had bad response body", testFile)
 			}
 		}
 	}
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index 85285a30..6a8e913a 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -3,12 +3,14 @@ package openrtb2
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"io/ioutil"
 	"net/http/httptest"
 	"strings"
 	"testing"
 
 	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/analytics"
 	analyticsConf "github.com/prebid/prebid-server/analytics/config"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/exchange"
@@ -641,6 +643,91 @@ func TestMergeOpenRTBToVideoRequest(t *testing.T) {
 	assert.Equal(t, videoReq.Site.Page, bidReq.Site.Page, "Device.Site.Page is incorrect")
 }
 
+func TestHandleError(t *testing.T) {
+	ao := analytics.AuctionObject{
+		Status: 200,
+		Errors: make([]error, 0),
+	}
+
+	labels := pbsmetrics.Labels{
+		Source:        pbsmetrics.DemandUnknown,
+		RType:         pbsmetrics.ReqTypeVideo,
+		PubID:         pbsmetrics.PublisherUnknown,
+		Browser:       "test browser",
+		CookieFlag:    pbsmetrics.CookieFlagUnknown,
+		RequestStatus: pbsmetrics.RequestStatusOK,
+	}
+
+	recorder := httptest.NewRecorder()
+	err1 := errors.New("Error for testing handleError 1")
+	err2 := errors.New("Error for testing handleError 2")
+	handleError(&labels, recorder, []error{err1, err2}, &ao)
+
+	assert.Equal(t, pbsmetrics.RequestStatusErr, labels.RequestStatus, "labels.RequestStatus should indicate an error")
+	assert.Equal(t, 500, recorder.Code, "Error status should be written to writer")
+	assert.Equal(t, 500, ao.Status, "AnalyticsObject should have error status")
+	assert.Equal(t, 2, len(ao.Errors), "New errors should be appended to AnalyticsObject Errors")
+	assert.Equal(t, "Error for testing handleError 1", ao.Errors[0].Error(), "Error in AnalyticsObject should have test error message for first error")
+	assert.Equal(t, "Error for testing handleError 2", ao.Errors[1].Error(), "Error in AnalyticsObject should have test error message for second error")
+}
+
+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))
+	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
+	recorder := httptest.NewRecorder()
+
+	deps, met, mod := mockDepsWithMetrics(t, ex)
+	deps.VideoAuctionEndpoint(recorder, req, nil)
+
+	assert.Equal(t, int64(0), met.RequestStatuses[pbsmetrics.ReqTypeVideo][pbsmetrics.RequestStatusOK].Count(), "OK requests count should be 0")
+	assert.Equal(t, int64(1), met.RequestStatuses[pbsmetrics.ReqTypeVideo][pbsmetrics.RequestStatusErr].Count(), "Error requests count should be 1")
+	assert.Equal(t, 1, len(mod.auctionObjects), "Mock AnalyticsModule should have 1 AuctionObject")
+	assert.Equal(t, 500, mod.auctionObjects[0].Status, "AnalyticsObject should have 500 status")
+	assert.Equal(t, 2, len(mod.auctionObjects[0].Errors), "AnalyticsObject should have Errors length of 2")
+	assert.Equal(t, "request missing required field: PodConfig.DurationRangeSec", mod.auctionObjects[0].Errors[0].Error(), "First error in AnalyticsObject should have message regarding DurationRangeSec")
+	assert.Equal(t, "request missing required field: PodConfig.Pods", mod.auctionObjects[0].Errors[1].Error(), "Second error in AnalyticsObject should have message regarding Pods")
+}
+
+func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *pbsmetrics.Metrics, *mockAnalyticsModule) {
+	theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{})
+	mockModule := &mockAnalyticsModule{}
+	edep := &endpointDeps{
+		ex,
+		newParamsValidator(t),
+		&mockVideoStoredReqFetcher{},
+		&mockVideoStoredReqFetcher{},
+		empty_fetcher.EmptyFetcher{},
+		&config.Configuration{MaxRequestSize: maxSize},
+		theMetrics,
+		mockModule,
+		map[string]string{},
+		false,
+		[]byte{},
+		openrtb_ext.BidderMap,
+	}
+
+	return edep, theMetrics, mockModule
+}
+
+type mockAnalyticsModule struct {
+	auctionObjects []*analytics.AuctionObject
+}
+
+func (m *mockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject) {
+	m.auctionObjects = append(m.auctionObjects, ao)
+}
+
+func (m *mockAnalyticsModule) LogCookieSyncObject(cso *analytics.CookieSyncObject) { return }
+
+func (m *mockAnalyticsModule) LogSetUIDObject(so *analytics.SetUIDObject) { return }
+
+func (m *mockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject) { return }
+
 func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 	theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{})
 	edep := &endpointDeps{
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 78ad0257..cccc6659 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -5,23 +5,26 @@ import (
 	"errors"
 	"net/http"
 	"net/http/httptest"
+	"net/url"
 	"regexp"
 	"testing"
 	"time"
 
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/pbsmetrics"
 	"github.com/prebid/prebid-server/usersync"
 	"github.com/stretchr/testify/assert"
 
 	"github.com/prebid/prebid-server/openrtb_ext"
 
 	analyticsConf "github.com/prebid/prebid-server/analytics/config"
-	"github.com/prebid/prebid-server/config"
 	metricsConf "github.com/prebid/prebid-server/pbsmetrics/config"
 )
 
 func TestSetUIDEndpoint(t *testing.T) {
 	testCases := []struct {
 		uri                   string
+		validFamilyNames      []string
 		existingSyncs         map[string]string
 		gdprAllowsHostCookies bool
 		gdprReturnsError      bool
@@ -32,6 +35,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 	}{
 		{
 			uri:                   "/setuid?bidder=pubmatic&uid=123",
+			validFamilyNames:      []string{"pubmatic"},
 			existingSyncs:         nil,
 			gdprAllowsHostCookies: true,
 			expectedSyncs:         map[string]string{"pubmatic": "123"},
@@ -40,6 +44,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 		{
 			uri:                   "/setuid?bidder=unsupported-bidder&uid=123",
+			validFamilyNames:      []string{},
 			existingSyncs:         nil,
 			gdprAllowsHostCookies: true,
 			expectedSyncs:         nil,
@@ -48,6 +53,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 		{
 			uri:                   "/setuid?bidder=&uid=123",
+			validFamilyNames:      []string{"pubmatic"},
 			existingSyncs:         nil,
 			gdprAllowsHostCookies: true,
 			expectedSyncs:         nil,
@@ -56,6 +62,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 		{
 			uri:                   "/setuid?bidder=unsupported-bidder&uid=123",
+			validFamilyNames:      []string{},
 			existingSyncs:         map[string]string{"pubmatic": "1234"},
 			gdprAllowsHostCookies: true,
 			expectedSyncs:         nil,
@@ -65,6 +72,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 		{
 			uri:                   "/setuid?bidder=&uid=123",
+			validFamilyNames:      []string{"pubmatic"},
 			existingSyncs:         map[string]string{"pubmatic": "1234"},
 			gdprAllowsHostCookies: true,
 			expectedSyncs:         nil,
@@ -74,6 +82,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 		{
 			uri:                   "/setuid?bidder=pubmatic",
+			validFamilyNames:      []string{"pubmatic"},
 			existingSyncs:         map[string]string{"pubmatic": "1234"},
 			gdprAllowsHostCookies: true,
 			expectedSyncs:         map[string]string{},
@@ -82,6 +91,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 		{
 			uri:                   "/setuid?bidder=pubmatic&uid=123",
+			validFamilyNames:      []string{"pubmatic"},
 			existingSyncs:         map[string]string{"rubicon": "def"},
 			gdprAllowsHostCookies: true,
 			expectedSyncs:         map[string]string{"pubmatic": "123", "rubicon": "def"},
@@ -90,6 +100,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 		{
 			uri:                  "/setuid?bidder=pubmatic&uid=123&gdpr=0",
+			validFamilyNames:     []string{"pubmatic"},
 			existingSyncs:        nil,
 			expectedSyncs:        map[string]string{"pubmatic": "123"},
 			expectedResponseCode: http.StatusOK,
@@ -97,6 +108,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 		{
 			uri:                  "/setuid?bidder=pubmatic&uid=123",
+			validFamilyNames:     []string{"pubmatic"},
 			existingSyncs:        nil,
 			expectedSyncs:        nil,
 			expectedResponseCode: http.StatusOK,
@@ -105,6 +117,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 		{
 			uri:                   "/setuid?uid=123",
+			validFamilyNames:      []string{"appnexus"},
 			existingSyncs:         nil,
 			expectedSyncs:         nil,
 			gdprAllowsHostCookies: true,
@@ -114,6 +127,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 		{
 			uri:                   "/setuid?bidder=appnexus&uid=123&gdpr=2",
+			validFamilyNames:      []string{"appnexus"},
 			existingSyncs:         nil,
 			expectedSyncs:         nil,
 			gdprAllowsHostCookies: true,
@@ -123,6 +137,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 		{
 			uri:                   "/setuid?bidder=appnexus&uid=123&gdpr=1",
+			validFamilyNames:      []string{"appnexus"},
 			existingSyncs:         nil,
 			expectedSyncs:         nil,
 			gdprAllowsHostCookies: true,
@@ -133,6 +148,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		{
 			uri: "/setuid?bidder=pubmatic&uid=123&gdpr_consent=" +
 				"BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw",
+			validFamilyNames:     []string{"pubmatic"},
 			existingSyncs:        nil,
 			expectedSyncs:        nil,
 			gdprReturnsError:     true,
@@ -144,6 +160,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		{
 			uri: "/setuid?bidder=pubmatic&uid=123&gdpr=1&gdpr_consent=" +
 				"BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw",
+			validFamilyNames:     []string{"pubmatic"},
 			existingSyncs:        nil,
 			expectedSyncs:        nil,
 			expectedResponseCode: http.StatusOK,
@@ -153,6 +170,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 		{
 			uri: "/setuid?bidder=pubmatic&uid=123&gdpr=1&gdpr_consent=" +
 				"BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw",
+			validFamilyNames:      []string{"pubmatic"},
 			gdprAllowsHostCookies: true,
 			existingSyncs:         nil,
 			expectedSyncs:         map[string]string{"pubmatic": "123"},
@@ -161,9 +179,10 @@ func TestSetUIDEndpoint(t *testing.T) {
 		},
 	}
 
+	metrics := &metricsConf.DummyMetricsEngine{}
 	for _, test := range testCases {
-		response := doRequest(makeRequest(test.uri, test.existingSyncs),
-			test.gdprAllowsHostCookies, test.gdprReturnsError)
+		response := doRequest(makeRequest(test.uri, test.existingSyncs), metrics,
+			test.validFamilyNames, test.gdprAllowsHostCookies, test.gdprReturnsError)
 		assert.Equal(t, test.expectedResponseCode, response.Code, "Test Case: %s. /setuid returned unexpected error code", test.description)
 
 		if test.expectedSyncs != nil {
@@ -178,12 +197,94 @@ func TestSetUIDEndpoint(t *testing.T) {
 	}
 }
 
+func TestSetUIDEndpointMetrics(t *testing.T) {
+	testCases := []struct {
+		uri                   string
+		cookies               []*usersync.PBSCookie
+		validFamilyNames      []string
+		gdprAllowsHostCookies bool
+		expectedMetricAction  pbsmetrics.RequestAction
+		expectedMetricBidder  openrtb_ext.BidderName
+		expectedResponseCode  int
+		description           string
+	}{
+		{
+			uri:                   "/setuid?bidder=pubmatic&uid=123",
+			cookies:               []*usersync.PBSCookie{},
+			validFamilyNames:      []string{"pubmatic"},
+			gdprAllowsHostCookies: true,
+			expectedMetricAction:  pbsmetrics.RequestActionSet,
+			expectedMetricBidder:  openrtb_ext.BidderName("pubmatic"),
+			expectedResponseCode:  200,
+			description:           "Success - Sync",
+		},
+		{
+			uri:                   "/setuid?bidder=pubmatic&uid=",
+			cookies:               []*usersync.PBSCookie{},
+			validFamilyNames:      []string{"pubmatic"},
+			gdprAllowsHostCookies: true,
+			expectedMetricAction:  pbsmetrics.RequestActionSet,
+			expectedMetricBidder:  openrtb_ext.BidderName("pubmatic"),
+			expectedResponseCode:  200,
+			description:           "Success - Unsync",
+		},
+		{
+			uri:                   "/setuid?bidder=pubmatic&uid=123",
+			cookies:               []*usersync.PBSCookie{usersync.NewPBSCookieWithOptOut()},
+			validFamilyNames:      []string{"pubmatic"},
+			gdprAllowsHostCookies: true,
+			expectedMetricAction:  pbsmetrics.RequestActionOptOut,
+			expectedResponseCode:  401,
+			description:           "Cookie Opted Out",
+		},
+		{
+			uri:                   "/setuid?bidder=pubmatic&uid=123",
+			cookies:               []*usersync.PBSCookie{},
+			validFamilyNames:      []string{},
+			gdprAllowsHostCookies: true,
+			expectedMetricAction:  pbsmetrics.RequestActionErr,
+			expectedResponseCode:  400,
+			description:           "Unsupported Cookie Name",
+		},
+		{
+			uri:                   "/setuid?bidder=pubmatic&uid=123&gdpr=1",
+			cookies:               []*usersync.PBSCookie{},
+			validFamilyNames:      []string{"pubmatic"},
+			gdprAllowsHostCookies: false,
+			expectedMetricAction:  pbsmetrics.RequestActionGDPR,
+			expectedMetricBidder:  openrtb_ext.BidderName("pubmatic"),
+			expectedResponseCode:  400,
+			description:           "Prevented By GDPR",
+		},
+	}
+
+	for _, test := range testCases {
+		metrics := &pbsmetrics.MetricsEngineMock{}
+		expectedLabels := pbsmetrics.UserLabels{
+			Action: test.expectedMetricAction,
+			Bidder: test.expectedMetricBidder,
+		}
+		metrics.On("RecordUserIDSet", expectedLabels).Once()
+
+		req := httptest.NewRequest("GET", test.uri, nil)
+		for _, v := range test.cookies {
+			addCookie(req, v)
+		}
+		response := doRequest(req, metrics, test.validFamilyNames, test.gdprAllowsHostCookies, false)
+
+		assert.Equal(t, test.expectedResponseCode, response.Code, test.description)
+		metrics.AssertExpectations(t)
+	}
+}
+
 func TestOptedOut(t *testing.T) {
 	request := httptest.NewRequest("GET", "/setuid?bidder=pubmatic&uid=123", nil)
 	cookie := usersync.NewPBSCookie()
 	cookie.SetPreference(false)
 	addCookie(request, cookie)
-	response := doRequest(request, true, false)
+	validFamilyNames := []string{"pubmatic"}
+	metrics := &metricsConf.DummyMetricsEngine{}
+	response := doRequest(request, metrics, validFamilyNames, true, false)
 
 	assert.Equal(t, http.StatusUnauthorized, response.Code)
 }
@@ -211,40 +312,51 @@ func TestSiteCookieCheck(t *testing.T) {
 	}
 }
 
-func TestValidateBidder(t *testing.T) {
+func TestGetFamilyName(t *testing.T) {
 	testCases := []struct {
-		bidderName  string
-		expectedErr string
-		description string
+		urlValues     url.Values
+		expectedName  string
+		expectedError string
+		description   string
 	}{
 		{
-			bidderName:  "appnexus",
-			description: "Should return no error for valid bidder",
+			urlValues:    url.Values{"bidder": []string{"valid"}},
+			expectedName: "valid",
+			description:  "Should return no error for valid family name",
 		},
 		{
-			bidderName:  "pubmatic",
-			description: "Should return no error for valid bidder",
+			urlValues:     url.Values{"bidder": []string{"VALID"}},
+			expectedError: "The bidder name provided is not supported by Prebid Server",
+			description:   "Should return error for different case",
 		},
 		{
-			bidderName:  "",
-			expectedErr: `"bidder" query param is required`,
-			description: "Should return an error for empty bidder name",
+			urlValues:     url.Values{"bidder": []string{"invalid"}},
+			expectedError: "The bidder name provided is not supported by Prebid Server",
+			description:   "Should return an error for unsupported bidder",
 		},
 		{
-			bidderName:  "some-random-bidder",
-			expectedErr: "The bidder name provided is not supported by Prebid Server",
-			description: "Should return an error for unsupported bidder",
+			urlValues:     url.Values{"bidder": []string{}},
+			expectedError: `"bidder" query param is required`,
+			description:   "Should return an error for empty bidder name",
+		},
+		{
+			urlValues:     url.Values{},
+			expectedError: `"bidder" query param is required`,
+			description:   "Should return an error for missing bidder name",
 		},
 	}
 
 	for _, test := range testCases {
-		err := validateBidder(test.bidderName)
-		if test.expectedErr != "" {
-			assert.EqualError(t, err, test.expectedErr, test.description)
+
+		name, err := getFamilyName(test.urlValues, map[string]struct{}{"valid": {}})
+
+		assert.Equal(t, test.expectedName, name, test.description)
+
+		if test.expectedError != "" {
+			assert.EqualError(t, err, test.expectedError, test.description)
 		} else {
-			assert.Nil(t, err, test.description)
+			assert.NoError(t, err, test.description)
 		}
-
 	}
 }
 
@@ -271,14 +383,20 @@ func makeRequest(uri string, existingSyncs map[string]string) *http.Request {
 	return request
 }
 
-func doRequest(req *http.Request, gdprAllowsHostCookies bool, gdprReturnsError bool) *httptest.ResponseRecorder {
+func doRequest(req *http.Request, metrics pbsmetrics.MetricsEngine, validFamilyNames []string, gdprAllowsHostCookies bool, gdprReturnsError bool) *httptest.ResponseRecorder {
+	cfg := config.Configuration{}
 	perms := &mockPermsSetUID{
 		allowHost: gdprAllowsHostCookies,
 		errorHost: gdprReturnsError,
 		allowPI:   true,
 	}
-	cfg := config.Configuration{}
-	endpoint := NewSetUIDEndpoint(cfg.HostCookie, perms, analyticsConf.NewPBSAnalytics(&cfg.Analytics), metricsConf.NewMetricsEngine(&cfg, openrtb_ext.BidderList()))
+	analytics := analyticsConf.NewPBSAnalytics(&cfg.Analytics)
+	syncers := make(map[openrtb_ext.BidderName]usersync.Usersyncer)
+	for _, name := range validFamilyNames {
+		syncers[openrtb_ext.BidderName(name)] = newFakeSyncer(name)
+	}
+
+	endpoint := NewSetUIDEndpoint(cfg.HostCookie, syncers, perms, analytics, metrics)
 	response := httptest.NewRecorder()
 	endpoint(response, req, nil)
 	return response
@@ -321,3 +439,28 @@ func (g *mockPermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) {
 	return g.allowPI, nil
 }
+
+func newFakeSyncer(familyName string) usersync.Usersyncer {
+	return fakeSyncer{
+		familyName: familyName,
+	}
+}
+
+type fakeSyncer struct {
+	familyName string
+}
+
+// FamilyNames implements the Usersyncer interface.
+func (s fakeSyncer) FamilyName() string {
+	return s.familyName
+}
+
+// GetUsersyncInfo implements the Usersyncer interface with a no-op.
+func (s fakeSyncer) GetUsersyncInfo(gdpr string, consent string) (*usersync.UsersyncInfo, error) {
+	return nil, nil
+}
+
+// GDPRVendorID implements the Usersyncer interface with a no-op.
+func (s fakeSyncer) GDPRVendorID() uint16 {
+	return 0
+}
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index f5bf23d5..2b0e37e6 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -1291,7 +1291,7 @@ func mockHandler(statusCode int, getBody string, postBody string) http.Handler {
 type wellBehavedCache struct{}
 
 func (c *wellBehavedCache) GetExtCacheData() (string, string) {
-	return "www.pbcserver.com", "pbcache/endpoint"
+	return "www.pbcserver.com", "/pbcache/endpoint"
 }
 
 func (c *wellBehavedCache) PutJson(ctx context.Context, values []prebid_cache_client.Cacheable) ([]string, []error) {
diff --git a/exchange/exchangetest/include-brand-category.json b/exchange/exchangetest/include-brand-category.json
index f27c0e6d..68e93e3a 100644
--- a/exchange/exchangetest/include-brand-category.json
+++ b/exchange/exchangetest/include-brand-category.json
@@ -106,8 +106,8 @@
                      "hb_bidder_appnexus": "appnexus",
                      "hb_cache_host": "www.pbcserver.com",
                      "hb_cache_host_appnex": "www.pbcserver.com",
-                     "hb_cache_path": "pbcache/endpoint",
-                     "hb_cache_path_appnex": "pbcache/endpoint",
+                     "hb_cache_path": "/pbcache/endpoint",
+                     "hb_cache_path_appnex": "/pbcache/endpoint",
                      "hb_pb": "0.20",
                      "hb_pb_appnexus": "0.20",
                      "hb_pb_cat_dur": "0.20_VideoGames_0s",
@@ -128,8 +128,8 @@
                       "hb_bidder_appnexus": "appnexus",
                       "hb_cache_host": "www.pbcserver.com",
                       "hb_cache_host_appnex": "www.pbcserver.com",
-                      "hb_cache_path": "pbcache/endpoint",
-                      "hb_cache_path_appnex": "pbcache/endpoint",
+                      "hb_cache_path": "/pbcache/endpoint",
+                      "hb_cache_path_appnex": "/pbcache/endpoint",
                       "hb_pb": "0.50",
                       "hb_pb_appnexus": "0.50",
                       "hb_pb_cat_dur": "0.50_HomeDecor_0s",
diff --git a/exchange/exchangetest/targeting-cache-vast-banner.json b/exchange/exchangetest/targeting-cache-vast-banner.json
index 5d0db274..215485cc 100644
--- a/exchange/exchangetest/targeting-cache-vast-banner.json
+++ b/exchange/exchangetest/targeting-cache-vast-banner.json
@@ -71,8 +71,8 @@
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_pb": "0.00",
                   "hb_pb_appnexus": "0.00",
                   "hb_size": "200x250",
diff --git a/exchange/exchangetest/targeting-cache-vast.json b/exchange/exchangetest/targeting-cache-vast.json
index 743f2ce3..f348dd1b 100644
--- a/exchange/exchangetest/targeting-cache-vast.json
+++ b/exchange/exchangetest/targeting-cache-vast.json
@@ -78,8 +78,8 @@
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_uuid": "0",
                   "hb_uuid_appnexus": "0",
                   "hb_pb": "0.00",
diff --git a/exchange/exchangetest/targeting-cache-zero.json b/exchange/exchangetest/targeting-cache-zero.json
index abe9df10..51301530 100644
--- a/exchange/exchangetest/targeting-cache-zero.json
+++ b/exchange/exchangetest/targeting-cache-zero.json
@@ -83,8 +83,8 @@
                   "hb_cache_host_appnex": "www.pbcserver.com",
                   "hb_cache_id": "0",
                   "hb_cache_id_appnexus": "0",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_uuid": "1",
                   "hb_uuid_appnexus": "1",
                   "hb_pb": "0.00",
diff --git a/exchange/exchangetest/targeting-mobile.json b/exchange/exchangetest/targeting-mobile.json
index d5661565..f3962cff 100644
--- a/exchange/exchangetest/targeting-mobile.json
+++ b/exchange/exchangetest/targeting-mobile.json
@@ -123,7 +123,7 @@
                 "targeting": {
                   "hb_bidder_audienceNe": "audienceNetwork",
                   "hb_cache_host_audien": "www.pbcserver.com",
-                  "hb_cache_path_audien": "pbcache/endpoint",
+                  "hb_cache_path_audien": "/pbcache/endpoint",
                   "hb_pb_audienceNetwor": "0.50",
                   "hb_size_audienceNetw": "200x250",
                   "hb_env_audienceNetwo": "mobile-app"
@@ -148,8 +148,8 @@
                   "hb_bidder": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_bidder_appnexus": "appnexus",
                   "hb_pb": "0.70",
                   "hb_pb_appnexus": "0.70",
@@ -188,8 +188,8 @@
                   "hb_bidder": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_bidder_appnexus": "appnexus",
                   "hb_pb": "0.60",
                   "hb_pb_appnexus": "0.60",
diff --git a/exchange/exchangetest/targeting-no-winners.json b/exchange/exchangetest/targeting-no-winners.json
index 73bf812c..891ff0ff 100644
--- a/exchange/exchangetest/targeting-no-winners.json
+++ b/exchange/exchangetest/targeting-no-winners.json
@@ -125,7 +125,7 @@
                 "targeting": {
                   "hb_bidder_audienceNe": "audienceNetwork",
                   "hb_cache_host_audien": "www.pbcserver.com",
-                  "hb_cache_path_audien": "pbcache/endpoint",
+                  "hb_cache_path_audien": "/pbcache/endpoint",
                   "hb_pb_audienceNetwor": "0.50",
                   "hb_size_audienceNetw": "200x250"
                 }
@@ -148,7 +148,7 @@
                 "targeting": {
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_pb_appnexus": "0.70",
                   "hb_size_appnexus": "200x250"
                 }
@@ -181,7 +181,7 @@
                 "targeting": {
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_pb_appnexus": "0.60",
                   "hb_size_appnexus": "300x500"
                 }
diff --git a/exchange/exchangetest/targeting-only-winners.json b/exchange/exchangetest/targeting-only-winners.json
index 807b7b6c..5f449af3 100644
--- a/exchange/exchangetest/targeting-only-winners.json
+++ b/exchange/exchangetest/targeting-only-winners.json
@@ -141,7 +141,7 @@
                 "targeting": {
                   "hb_bidder": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
                   "hb_pb": "0.70",
                   "hb_size": "200x250"
                 }
@@ -174,7 +174,7 @@
                 "targeting": {
                   "hb_bidder": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
                   "hb_pb": "0.60",
                   "hb_size": "300x500"
                 }
diff --git a/exchange/exchangetest/targeting-with-winners.json b/exchange/exchangetest/targeting-with-winners.json
index 208b3feb..f081b7ee 100644
--- a/exchange/exchangetest/targeting-with-winners.json
+++ b/exchange/exchangetest/targeting-with-winners.json
@@ -123,7 +123,7 @@
                 "targeting": {
                   "hb_bidder_audienceNe": "audienceNetwork",
                   "hb_cache_host_audien": "www.pbcserver.com",
-                  "hb_cache_path_audien": "pbcache/endpoint",
+                  "hb_cache_path_audien": "/pbcache/endpoint",
                   "hb_pb_audienceNetwor": "0.50",
                   "hb_size_audienceNetw": "200x250"
                 }
@@ -148,8 +148,8 @@
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_pb": "0.70",
                   "hb_pb_appnexus": "0.70",
                   "hb_size": "200x250",
@@ -186,8 +186,8 @@
                   "hb_bidder_appnexus": "appnexus",
                   "hb_cache_host": "www.pbcserver.com",
                   "hb_cache_host_appnex": "www.pbcserver.com",
-                  "hb_cache_path": "pbcache/endpoint",
-                  "hb_cache_path_appnex": "pbcache/endpoint",
+                  "hb_cache_path": "/pbcache/endpoint",
+                  "hb_cache_path_appnex": "/pbcache/endpoint",
                   "hb_pb": "0.60",
                   "hb_pb_appnexus": "0.60",
                   "hb_size": "300x500",
diff --git a/prebid_cache_client/client_test.go b/prebid_cache_client/client_test.go
index cbeaa38a..d3b5ee4b 100644
--- a/prebid_cache_client/client_test.go
+++ b/prebid_cache_client/client_test.go
@@ -156,10 +156,10 @@ func TestStripCacheHostAndPath(t *testing.T) {
 		{
 			inExtCacheURL: config.ExternalCache{
 				Host: "prebid-server.prebid.org",
-				Path: "pbcache/endpoint",
+				Path: "/pbcache/endpoint",
 			},
 			expectedHost: "prebid-server.prebid.org",
-			expectedPath: "pbcache/endpoint",
+			expectedPath: "/pbcache/endpoint",
 		},
 		{
 			inExtCacheURL: config.ExternalCache{
@@ -177,6 +177,22 @@ func TestStripCacheHostAndPath(t *testing.T) {
 			expectedHost: "",
 			expectedPath: "",
 		},
+		{
+			inExtCacheURL: config.ExternalCache{
+				Host: "prebid-server.prebid.org",
+				Path: "pbcache/endpoint",
+			},
+			expectedHost: "prebid-server.prebid.org",
+			expectedPath: "/pbcache/endpoint",
+		},
+		{
+			inExtCacheURL: config.ExternalCache{
+				Host: "prebidcache.net",
+				Path: "/",
+			},
+			expectedHost: "prebidcache.net",
+			expectedPath: "",
+		},
 	}
 	for _, test := range testInput {
 		//start client
diff --git a/privacy/ccpa/ccpa_test.go b/privacy/ccpa/ccpa_test.go
new file mode 100644
index 00000000..a5b48d14
--- /dev/null
+++ b/privacy/ccpa/ccpa_test.go
@@ -0,0 +1,74 @@
+package ccpa
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/mxmCherry/openrtb"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestWrite(t *testing.T) {
+	testCases := []struct {
+		description   string
+		policy        Policy
+		request       *openrtb.BidRequest
+		expected      *openrtb.BidRequest
+		expectedError bool
+	}{
+		{
+			description: "Disabled",
+			policy:      Policy{Value: ""},
+			request:     &openrtb.BidRequest{},
+			expected:    &openrtb.BidRequest{},
+		},
+		{
+			description: "Enabled With Nil Request Regs Object",
+			policy:      Policy{Value: "anyValue"},
+			request:     &openrtb.BidRequest{},
+			expected: &openrtb.BidRequest{Regs: &openrtb.Regs{
+				Ext: json.RawMessage(`{"us_privacy":"anyValue"}`)}},
+		},
+		{
+			description: "Enabled With Nil Request Regs Ext Object",
+			policy:      Policy{Value: "anyValue"},
+			request:     &openrtb.BidRequest{Regs: &openrtb.Regs{}},
+			expected: &openrtb.BidRequest{Regs: &openrtb.Regs{
+				Ext: json.RawMessage(`{"us_privacy":"anyValue"}`)}},
+		},
+		{
+			description: "Enabled With Existing Request Regs Ext Object - Doesn't Overwrite",
+			policy:      Policy{Value: "anyValue"},
+			request: &openrtb.BidRequest{Regs: &openrtb.Regs{
+				Ext: json.RawMessage(`{"existing":"any"}`)}},
+			expected: &openrtb.BidRequest{Regs: &openrtb.Regs{
+				Ext: json.RawMessage(`{"existing":"any","us_privacy":"anyValue"}`)}},
+		},
+		{
+			description: "Enabled With Existing Request Regs Ext Object - Overwrites",
+			policy:      Policy{Value: "anyValue"},
+			request: &openrtb.BidRequest{Regs: &openrtb.Regs{
+				Ext: json.RawMessage(`{"existing":"any","us_privacy":"toBeOverwritten"}`)}},
+			expected: &openrtb.BidRequest{Regs: &openrtb.Regs{
+				Ext: json.RawMessage(`{"existing":"any","us_privacy":"anyValue"}`)}},
+		},
+		{
+			description: "Enabled With Existing Malformed Request Regs Ext Object",
+			policy:      Policy{Value: "anyValue"},
+			request: &openrtb.BidRequest{Regs: &openrtb.Regs{
+				Ext: json.RawMessage(`malformed`)}},
+			expectedError: true,
+		},
+	}
+
+	for _, test := range testCases {
+		err := test.policy.Write(test.request)
+
+		if test.expectedError {
+			assert.Error(t, err, test.description)
+		} else {
+			assert.NoError(t, err, test.description)
+			assert.Equal(t, test.expected, test.request, test.description)
+		}
+	}
+}
diff --git a/privacy/gdpr/gdpr_test.go b/privacy/gdpr/gdpr_test.go
new file mode 100644
index 00000000..34605f07
--- /dev/null
+++ b/privacy/gdpr/gdpr_test.go
@@ -0,0 +1,74 @@
+package gdpr
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/mxmCherry/openrtb"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestWrite(t *testing.T) {
+	testCases := []struct {
+		description   string
+		policy        Policy
+		request       *openrtb.BidRequest
+		expected      *openrtb.BidRequest
+		expectedError bool
+	}{
+		{
+			description: "Disabled",
+			policy:      Policy{Consent: ""},
+			request:     &openrtb.BidRequest{},
+			expected:    &openrtb.BidRequest{},
+		},
+		{
+			description: "Enabled With Nil Request User Object",
+			policy:      Policy{Consent: "anyValue"},
+			request:     &openrtb.BidRequest{},
+			expected: &openrtb.BidRequest{User: &openrtb.User{
+				Ext: json.RawMessage(`{"consent":"anyValue"}`)}},
+		},
+		{
+			description: "Enabled With Nil Request User Ext Object",
+			policy:      Policy{Consent: "anyValue"},
+			request:     &openrtb.BidRequest{User: &openrtb.User{}},
+			expected: &openrtb.BidRequest{User: &openrtb.User{
+				Ext: json.RawMessage(`{"consent":"anyValue"}`)}},
+		},
+		{
+			description: "Enabled With Existing Request User Ext Object - Doesn't Overwrite",
+			policy:      Policy{Consent: "anyValue"},
+			request: &openrtb.BidRequest{User: &openrtb.User{
+				Ext: json.RawMessage(`{"existing":"any"}`)}},
+			expected: &openrtb.BidRequest{User: &openrtb.User{
+				Ext: json.RawMessage(`{"existing":"any","consent":"anyValue"}`)}},
+		},
+		{
+			description: "Enabled With Existing Request User Ext Object - Overwrites",
+			policy:      Policy{Consent: "anyValue"},
+			request: &openrtb.BidRequest{User: &openrtb.User{
+				Ext: json.RawMessage(`{"existing":"any","consent":"toBeOverwritten"}`)}},
+			expected: &openrtb.BidRequest{User: &openrtb.User{
+				Ext: json.RawMessage(`{"existing":"any","consent":"anyValue"}`)}},
+		},
+		{
+			description: "Enabled With Existing Malformed Request User Ext Object",
+			policy:      Policy{Consent: "anyValue"},
+			request: &openrtb.BidRequest{User: &openrtb.User{
+				Ext: json.RawMessage(`malformed`)}},
+			expectedError: true,
+		},
+	}
+
+	for _, test := range testCases {
+		err := test.policy.Write(test.request)
+
+		if test.expectedError {
+			assert.Error(t, err, test.description)
+		} else {
+			assert.NoError(t, err, test.description)
+			assert.Equal(t, test.expected, test.request, test.description)
+		}
+	}
+}
diff --git a/privacy/policies_test.go b/privacy/policies_test.go
new file mode 100644
index 00000000..69794252
--- /dev/null
+++ b/privacy/policies_test.go
@@ -0,0 +1,77 @@
+package privacy
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/mxmCherry/openrtb"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+func TestWritePoliciesNone(t *testing.T) {
+	request := &openrtb.BidRequest{}
+	policyWriters := []policyWriter{}
+
+	err := writePolicies(request, policyWriters)
+
+	assert.NoError(t, err)
+}
+
+func TestWritePoliciesOne(t *testing.T) {
+	request := &openrtb.BidRequest{}
+	mockWriter := new(mockPolicyWriter)
+	policyWriters := []policyWriter{
+		mockWriter,
+	}
+
+	mockWriter.On("Write", request).Return(nil).Once()
+
+	err := writePolicies(request, policyWriters)
+
+	assert.NoError(t, err)
+	mockWriter.AssertExpectations(t)
+}
+
+func TestWritePoliciesMany(t *testing.T) {
+	request := &openrtb.BidRequest{}
+	mockWriter1 := new(mockPolicyWriter)
+	mockWriter2 := new(mockPolicyWriter)
+	policyWriters := []policyWriter{
+		mockWriter1, mockWriter2,
+	}
+
+	mockWriter1.On("Write", request).Return(nil).Once()
+	mockWriter2.On("Write", request).Return(nil).Once()
+
+	err := writePolicies(request, policyWriters)
+
+	assert.NoError(t, err)
+	mockWriter1.AssertExpectations(t)
+	mockWriter2.AssertExpectations(t)
+}
+
+func TestWritePoliciesError(t *testing.T) {
+	request := &openrtb.BidRequest{}
+	mockWriter := new(mockPolicyWriter)
+	policyWriters := []policyWriter{
+		mockWriter,
+	}
+
+	expectedErr := errors.New("anyError")
+	mockWriter.On("Write", request).Return(expectedErr).Once()
+
+	err := writePolicies(request, policyWriters)
+
+	assert.Error(t, err, expectedErr)
+	mockWriter.AssertExpectations(t)
+}
+
+type mockPolicyWriter struct {
+	mock.Mock
+}
+
+func (m *mockPolicyWriter) Write(req *openrtb.BidRequest) error {
+	args := m.Called(req)
+	return args.Error(0)
+}
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 1c4b3b07..b97230b2 100644
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -14,6 +14,7 @@ func TestNewSyncerMap(t *testing.T) {
 	}
 	cfg := &config.Configuration{
 		Adapters: map[string]config.Adapter{
+			string(openrtb_ext.Bidder33Across):         syncConfig,
 			string(openrtb_ext.BidderAdform):           syncConfig,
 			string(openrtb_ext.BidderAdkernel):         syncConfig,
 			string(openrtb_ext.BidderAdkernelAdn):      syncConfig,
@@ -22,13 +23,16 @@ func TestNewSyncerMap(t *testing.T) {
 			string(openrtb_ext.BidderAdvangelists):     syncConfig,
 			string(openrtb_ext.BidderAppnexus):         syncConfig,
 			string(openrtb_ext.BidderBeachfront):       syncConfig,
-			string(openrtb_ext.BidderFacebook):         syncConfig,
 			string(openrtb_ext.BidderBrightroll):       syncConfig,
 			string(openrtb_ext.BidderConsumable):       syncConfig,
 			string(openrtb_ext.BidderConversant):       syncConfig,
 			string(openrtb_ext.BidderDatablocks):       syncConfig,
 			string(openrtb_ext.BidderEmxDigital):       syncConfig,
+			string(openrtb_ext.BidderEngageBDR):        syncConfig,
 			string(openrtb_ext.BidderEPlanning):        syncConfig,
+			string(openrtb_ext.BidderFacebook):         syncConfig,
+			string(openrtb_ext.BidderGamma):            syncConfig,
+			string(openrtb_ext.BidderGamoshi):          syncConfig,
 			string(openrtb_ext.BidderGrid):             syncConfig,
 			string(openrtb_ext.BidderGumGum):           syncConfig,
 			string(openrtb_ext.BidderImprovedigital):   syncConfig,
@@ -44,20 +48,16 @@ func TestNewSyncerMap(t *testing.T) {
 			string(openrtb_ext.BidderRubicon):          syncConfig,
 			string(openrtb_ext.BidderSharethrough):     syncConfig,
 			string(openrtb_ext.BidderSomoaudience):     syncConfig,
+			string(openrtb_ext.BidderSonobi):           syncConfig,
 			string(openrtb_ext.BidderSovrn):            syncConfig,
 			string(openrtb_ext.BidderSynacormedia):     syncConfig,
 			string(openrtb_ext.BidderTriplelift):       syncConfig,
 			string(openrtb_ext.BidderTripleliftNative): syncConfig,
-			string(openrtb_ext.Bidder33Across):         syncConfig,
-			string(openrtb_ext.BidderSonobi):           syncConfig,
-			string(openrtb_ext.BidderVrtcal):           syncConfig,
-			string(openrtb_ext.BidderYieldmo):          syncConfig,
-			string(openrtb_ext.BidderGamma):            syncConfig,
-			string(openrtb_ext.BidderVisx):             syncConfig,
-			string(openrtb_ext.BidderGamoshi):          syncConfig,
 			string(openrtb_ext.BidderUnruly):           syncConfig,
 			string(openrtb_ext.BidderVerizonMedia):     syncConfig,
-			string(openrtb_ext.BidderEngageBDR):        syncConfig,
+			string(openrtb_ext.BidderVisx):             syncConfig,
+			string(openrtb_ext.BidderVrtcal):           syncConfig,
+			string(openrtb_ext.BidderYieldmo):          syncConfig,
 		},
 	}