Repository: prebid/prebid-server
Pull request #3035 (2023-08-18 07:06:30)

New adapter: PGAMSSP
doc - https://github.com/prebid/prebid.github.io/pull/4810

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, cfcb9c47a5ed507fbbfc7f71d56c1b46c144f9b6 
#### pgamssp 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/5899828666_1692342463/pgamssp.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:29:		Builder			100.0%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:36:		MakeRequests		85.7%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:83:		makeRequest		85.7%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:100:	MakeBids		100.0%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:136:	getMediaTypeForImp	100.0%
total:									(statements)		92.2%

 ``` 

PGAMSSP: Thank you for the heads up.

On Thu, Aug 24, 2023 at 9:02 AM Sonali-More-Xandr ***@***.***>
wrote:

> ***@***.**** commented on this pull request.
> ------------------------------
>
> In static/bidder-info/pgamssp.yaml
> <https://github.com/prebid/prebid-server/pull/3035#discussion_r1304287998>
> :
>
> > @@ -0,0 +1,19 @@
> +endpoint: "http://us-east.pgammedia.com/pserver"
> +maintainer:
> +  email: ***@***.***"
>
> Email verification is done!
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/prebid/prebid-server/pull/3035#discussion_r1304287998>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/BB525SKG6HD5LBWYLBS4AKDXW5GFLANCNFSM6AAAAAA3VDICOA>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>

PGAMSSP: Received On Aug 24, 2023, at 9:02 AM, Sonali-More-Xandr ***@***.***> wrote:﻿
@Sonali-More-Xandr commented on this pull request.



In static/bidder-info/pgamssp.yaml:
> @@ -0,0 +1,19 @@
+endpoint: "http://us-east.pgammedia.com/pserver"
+maintainer:
+  email: ***@***.***"

Email verification is done!

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: ***@***.***>
PGAMSSP: > Couple of suggestion. Also requesting you to please resolve the conflict by merging master.

fixed conflicts
github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 9614955c76db1ce0305157a2834b9fee13a19eee 
#### pgamssp 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/6042053431_1693513164/pgamssp.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:29:		Builder			100.0%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:36:		MakeRequests		85.7%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:83:		makeRequest		85.7%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:100:	MakeBids		100.0%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:133:	getMediaTypeForImp	100.0%
total:									(statements)		92.1%

 ``` 

gargcreation1992: @PGAMSSP Requesting you to please address bot comment as well - https://github.com/prebid/prebid-server/pull/3035#discussion_r1298087247
PGAMSSP: > @PGAMSSP requesting you to please address the open comment.

thx, code was updated
github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 49dbd8496b8601d78cabd11ffa0dd2b69772b36d 
#### pgamssp 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/6146357921_1694435298/pgamssp.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:28:		Builder		100.0%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:35:		MakeRequests	85.7%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:82:		makeRequest	85.7%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:99:		MakeBids	100.0%
github.com/prebid/prebid-server/adapters/pgamssp/pgamssp.go:132:	getBidMediaType	85.7%
total:									(statements)	90.2%

 ``` 


Patch:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6ea29bd3..fd790699 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -32,9 +32,25 @@ jobs:
     outputs:
       hasWritePermission: ${{ steps.check.outputs.require-result }}
 
+  build-master:
+    name: Build master
+    needs: check-permission
+    if: contains(needs.check-permission.outputs.hasWritePermission, 'true')
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@master
+        with:
+          fetch-depth: 0
+          repository: ${{ github.repository }}
+          ref: master
+      - name: Build and validate
+        run: |
+          ./validate.sh
+
   publish-tag:
     name: Publish tag
-    needs: check-permission
+    needs: build-master
     if: contains(needs.check-permission.outputs.hasWritePermission, 'true')
     permissions:
       contents: write
diff --git a/adapters/pgamssp/pgamssp.go b/adapters/pgamssp/pgamssp.go
new file mode 100644
index 00000000..378b8e5c
--- /dev/null
+++ b/adapters/pgamssp/pgamssp.go
@@ -0,0 +1,145 @@
+package pgamssp
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	endpoint string
+}
+
+type reqBodyExt struct {
+	PGAMSspBidderExt reqBodyExtBidder `json:"bidder"`
+}
+
+type reqBodyExtBidder struct {
+	Type        string `json:"type"`
+	PlacementID string `json:"placementId,omitempty"`
+	EndpointID  string `json:"endpointId,omitempty"`
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+	return bidder, nil
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var err error
+	var adapterRequests []*adapters.RequestData
+
+	reqCopy := *request
+	for _, imp := range request.Imp {
+		reqCopy.Imp = []openrtb2.Imp{imp}
+
+		var bidderExt adapters.ExtImpBidder
+		var pgamExt openrtb_ext.ImpExtPgamSsp
+
+		if err = json.Unmarshal(reqCopy.Imp[0].Ext, &bidderExt); err != nil {
+			return nil, []error{err}
+		}
+		if err = json.Unmarshal(bidderExt.Bidder, &pgamExt); err != nil {
+			return nil, []error{err}
+		}
+
+		temp := reqBodyExt{PGAMSspBidderExt: reqBodyExtBidder{}}
+
+		if pgamExt.PlacementID != "" {
+			temp.PGAMSspBidderExt.PlacementID = pgamExt.PlacementID
+			temp.PGAMSspBidderExt.Type = "publisher"
+		} else if pgamExt.EndpointID != "" {
+			temp.PGAMSspBidderExt.EndpointID = pgamExt.EndpointID
+			temp.PGAMSspBidderExt.Type = "network"
+		}
+
+		finalyImpExt, err := json.Marshal(temp)
+		if err != nil {
+			return nil, []error{err}
+		}
+
+		reqCopy.Imp[0].Ext = finalyImpExt
+
+		adapterReq, err := a.makeRequest(&reqCopy)
+		if err != nil {
+			return nil, []error{err}
+		}
+
+		if adapterReq != nil {
+			adapterRequests = append(adapterRequests, adapterReq)
+		}
+	}
+	return adapterRequests, nil
+}
+
+func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, error) {
+	reqJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, err
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	return &adapters.RequestData{
+		Method:  "POST",
+		Uri:     a.endpoint,
+		Body:    reqJSON,
+		Headers: headers,
+	}, err
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if adapters.IsResponseStatusCodeNoContent(responseData) {
+		return nil, nil
+	}
+
+	if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil {
+		return nil, []error{err}
+	}
+
+	var response openrtb2.BidResponse
+	if err := json.Unmarshal(responseData.Body, &response); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+	bidResponse.Currency = response.Cur
+	for _, seatBid := range response.SeatBid {
+		for i := range seatBid.Bid {
+			bidType, err := getBidMediaType(&seatBid.Bid[i])
+			if err != nil {
+				return nil, []error{err}
+			}
+
+			b := &adapters.TypedBid{
+				Bid:     &seatBid.Bid[i],
+				BidType: bidType,
+			}
+			bidResponse.Bids = append(bidResponse.Bids, b)
+		}
+	}
+	return bidResponse, nil
+}
+
+func getBidMediaType(bid *openrtb2.Bid) (openrtb_ext.BidType, error) {
+	switch bid.MType {
+	case openrtb2.MarkupBanner:
+		return openrtb_ext.BidTypeBanner, nil
+	case openrtb2.MarkupVideo:
+		return openrtb_ext.BidTypeVideo, nil
+	case openrtb2.MarkupAudio:
+		return openrtb_ext.BidTypeAudio, nil
+	case openrtb2.MarkupNative:
+		return openrtb_ext.BidTypeNative, nil
+	default:
+		return "", fmt.Errorf("Unable to fetch mediaType in multi-format: %s", bid.ImpID)
+	}
+}
diff --git a/config/account.go b/config/account.go
index 038a8daa..e5265072 100644
--- a/config/account.go
+++ b/config/account.go
@@ -85,6 +85,10 @@ func (pf *AccountPriceFloors) validate(errs []error) []error {
 	return errs
 }
 
+func (pf *AccountPriceFloors) IsAdjustForBidAdjustmentEnabled() bool {
+	return pf.AdjustForBidAdjustment
+}
+
 // EnabledForChannelType indicates whether CCPA is turned on at the account level for the specified channel type
 // by using the channel type setting if defined or the general CCPA setting if defined; otherwise it returns nil
 func (a *AccountCCPA) EnabledForChannelType(channelType ChannelType) *bool {
diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index 50cb4491..0a81c2d5 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -19,11 +19,11 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/gdpr"
+	"github.com/prebid/prebid-server/macros"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/privacy/ccpa"
-	gdprPrivacy "github.com/prebid/prebid-server/privacy/gdpr"
 	gppPrivacy "github.com/prebid/prebid-server/privacy/gpp"
 	"github.com/prebid/prebid-server/stored_requests"
 	"github.com/prebid/prebid-server/usersync"
@@ -84,7 +84,7 @@ type cookieSyncEndpoint struct {
 }
 
 func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
-	request, privacyPolicies, err := c.parseRequest(r)
+	request, privacyMacros, err := c.parseRequest(r)
 	if err != nil {
 		c.writeParseRequestErrorMetrics(err)
 		c.handleError(w, err, http.StatusBadRequest)
@@ -102,24 +102,24 @@ func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ ht
 		c.handleError(w, errCookieSyncOptOut, http.StatusUnauthorized)
 	case usersync.StatusBlockedByGDPR:
 		c.metrics.RecordCookieSync(metrics.CookieSyncGDPRHostCookieBlocked)
-		c.handleResponse(w, request.SyncTypeFilter, cookie, privacyPolicies, nil)
+		c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil)
 	case usersync.StatusOK:
 		c.metrics.RecordCookieSync(metrics.CookieSyncOK)
 		c.writeSyncerMetrics(result.BiddersEvaluated)
-		c.handleResponse(w, request.SyncTypeFilter, cookie, privacyPolicies, result.SyncersChosen)
+		c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen)
 	}
 }
 
-func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, privacy.Policies, error) {
+func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, macros.UserSyncPrivacy, error) {
 	defer r.Body.Close()
 	body, err := io.ReadAll(r.Body)
 	if err != nil {
-		return usersync.Request{}, privacy.Policies{}, errCookieSyncBody
+		return usersync.Request{}, macros.UserSyncPrivacy{}, errCookieSyncBody
 	}
 
 	request := cookieSyncRequest{}
 	if err := json.Unmarshal(body, &request); err != nil {
-		return usersync.Request{}, privacy.Policies{}, fmt.Errorf("JSON parsing failed: %s", err.Error())
+		return usersync.Request{}, macros.UserSyncPrivacy{}, fmt.Errorf("JSON parsing failed: %s", err.Error())
 	}
 
 	if request.Account == "" {
@@ -127,42 +127,37 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr
 	}
 	account, fetchErrs := accountService.GetAccount(context.Background(), c.config, c.accountsFetcher, request.Account, c.metrics)
 	if len(fetchErrs) > 0 {
-		return usersync.Request{}, privacy.Policies{}, combineErrors(fetchErrs)
+		return usersync.Request{}, macros.UserSyncPrivacy{}, combineErrors(fetchErrs)
 	}
 
 	request = c.setLimit(request, account.CookieSync)
 	request = c.setCooperativeSync(request, account.CookieSync)
 
-	privacyPolicies, gdprSignal, err := extractPrivacyPolicies(request, c.privacyConfig.gdprConfig.DefaultValue)
+	privacyMacros, gdprSignal, privacyPolicies, err := extractPrivacyPolicies(request, c.privacyConfig.gdprConfig.DefaultValue)
 	if err != nil {
-		return usersync.Request{}, privacy.Policies{}, err
+		return usersync.Request{}, macros.UserSyncPrivacy{}, err
 	}
 
 	ccpaParsedPolicy := ccpa.ParsedPolicy{}
 	if request.USPrivacy != "" {
-		parsedPolicy, err := privacyPolicies.CCPA.Parse(c.privacyConfig.bidderHashSet)
+		parsedPolicy, err := ccpa.Policy{Consent: request.USPrivacy}.Parse(c.privacyConfig.bidderHashSet)
 		if err != nil {
-			privacyPolicies.CCPA.Consent = ""
+			privacyMacros.USPrivacy = ""
 		}
 		if c.privacyConfig.ccpaEnforce {
 			ccpaParsedPolicy = parsedPolicy
 		}
 	}
 
-	activityControl, activitiesErr := privacy.NewActivityControl(&account.Privacy)
-	if activitiesErr != nil {
-		if errortypes.ContainsFatalError([]error{activitiesErr}) {
-			activityControl = privacy.ActivityControl{}
-		}
-	}
+	activityControl := privacy.NewActivityControl(&account.Privacy)
 
 	syncTypeFilter, err := parseTypeFilter(request.FilterSettings)
 	if err != nil {
-		return usersync.Request{}, privacy.Policies{}, err
+		return usersync.Request{}, macros.UserSyncPrivacy{}, err
 	}
 
 	gdprRequestInfo := gdpr.RequestInfo{
-		Consent:    privacyPolicies.GDPR.Consent,
+		Consent:    privacyMacros.GDPRConsent,
 		GDPRSignal: gdprSignal,
 	}
 
@@ -180,22 +175,23 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr
 			gdprPermissions:  gdprPerms,
 			ccpaParsedPolicy: ccpaParsedPolicy,
 			activityControl:  activityControl,
+			activityRequest:  privacy.NewRequestFromPolicies(privacyPolicies),
 		},
 		SyncTypeFilter: syncTypeFilter,
 	}
-	return rx, privacyPolicies, nil
+	return rx, privacyMacros, nil
 }
 
-func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue string) (privacy.Policies, gdpr.Signal, error) {
+func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue string) (macros.UserSyncPrivacy, gdpr.Signal, privacy.Policies, error) {
 	// GDPR
-	gppSID, err := stringutil.StrToInt8Slice(request.GPPSid)
+	gppSID, err := stringutil.StrToInt8Slice(request.GPPSID)
 	if err != nil {
-		return privacy.Policies{}, gdpr.SignalNo, err
+		return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, err
 	}
 
 	gdprSignal, gdprString, err := extractGDPRSignal(request.GDPR, gppSID)
 	if err != nil {
-		return privacy.Policies{}, gdpr.SignalNo, err
+		return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, err
 	}
 
 	var gpp gpplib.GppContainer
@@ -203,7 +199,7 @@ func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue
 		var err error
 		gpp, err = gpplib.Parse(request.GPP)
 		if err != nil {
-			return privacy.Policies{}, gdpr.SignalNo, err
+			return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, err
 		}
 	}
 
@@ -214,33 +210,33 @@ func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue
 
 	if gdprConsent == "" {
 		if gdprSignal == gdpr.SignalYes {
-			return privacy.Policies{}, gdpr.SignalNo, errCookieSyncGDPRConsentMissing
+			return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, errCookieSyncGDPRConsentMissing
 		}
 
 		if gdprSignal == gdpr.SignalAmbiguous && gdpr.SignalNormalize(gdprSignal, usersyncDefaultGDPRValue) == gdpr.SignalYes {
-			return privacy.Policies{}, gdpr.SignalNo, errCookieSyncGDPRConsentMissingSignalAmbiguous
+			return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, errCookieSyncGDPRConsentMissingSignalAmbiguous
 		}
 	}
 
 	// CCPA
 	ccpaString, err := ccpa.SelectCCPAConsent(request.USPrivacy, gpp, gppSID)
 	if err != nil {
-		return privacy.Policies{}, gdpr.SignalNo, err
+		return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, err
 	}
 
-	return privacy.Policies{
-		GDPR: gdprPrivacy.Policy{
-			Signal:  gdprString,
-			Consent: gdprConsent,
-		},
-		CCPA: ccpa.Policy{
-			Consent: ccpaString,
-		},
-		GPP: gppPrivacy.Policy{
-			Consent: request.GPP,
-			RawSID:  request.GPPSid,
-		},
-	}, gdprSignal, nil
+	privacyMacros := macros.UserSyncPrivacy{
+		GDPR:        gdprString,
+		GDPRConsent: gdprConsent,
+		USPrivacy:   ccpaString,
+		GPP:         request.GPP,
+		GPPSID:      request.GPPSID,
+	}
+
+	privacyPolicies := privacy.Policies{
+		GPPSID: gppSID,
+	}
+
+	return privacyMacros, gdprSignal, privacyPolicies, nil
 }
 
 func extractGDPRSignal(requestGDPR *int, gppSID []int8) (gdpr.Signal, string, error) {
@@ -401,7 +397,7 @@ func (c *cookieSyncEndpoint) writeSyncerMetrics(biddersEvaluated []usersync.Bidd
 	}
 }
 
-func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, p privacy.Policies, s []usersync.SyncerChoice) {
+func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice) {
 	status := "no_cookie"
 	if co.HasAnyLiveSyncs() {
 		status = "ok"
@@ -414,7 +410,7 @@ func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.S
 
 	for _, syncerChoice := range s {
 		syncTypes := tf.ForBidder(syncerChoice.Bidder)
-		sync, err := syncerChoice.Syncer.GetSync(syncTypes, p)
+		sync, err := syncerChoice.Syncer.GetSync(syncTypes, m)
 		if err != nil {
 			glog.Errorf("Failed to get usersync info for %s: %v", syncerChoice.Bidder, err)
 			continue
@@ -465,7 +461,7 @@ type cookieSyncRequest struct {
 	USPrivacy       string                           `json:"us_privacy"`
 	Limit           int                              `json:"limit"`
 	GPP             string                           `json:"gpp"`
-	GPPSid          string                           `json:"gpp_sid"`
+	GPPSID          string                           `json:"gpp_sid"`
 	CooperativeSync *bool                            `json:"coopSync"`
 	FilterSettings  *cookieSyncRequestFilterSettings `json:"filterSettings"`
 	Account         string                           `json:"account"`
@@ -510,6 +506,7 @@ type usersyncPrivacy struct {
 	gdprPermissions  gdpr.Permissions
 	ccpaParsedPolicy ccpa.ParsedPolicy
 	activityControl  privacy.ActivityControl
+	activityRequest  privacy.ActivityRequest
 }
 
 func (p usersyncPrivacy) GDPRAllowsHostCookie() bool {
@@ -528,6 +525,8 @@ func (p usersyncPrivacy) CCPAAllowsBidderSync(bidder string) bool {
 }
 
 func (p usersyncPrivacy) ActivityAllowsUserSync(bidder string) bool {
-	return p.activityControl.Allow(privacy.ActivitySyncUser,
-		privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidder})
+	return p.activityControl.Allow(
+		privacy.ActivitySyncUser,
+		privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidder},
+		p.activityRequest)
 }
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 142f7320..2879610d 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -6,12 +6,13 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/prebid/prebid-server/privacy"
 	"net/http"
 	"net/url"
 	"strings"
 	"time"
 
+	"github.com/prebid/prebid-server/privacy"
+
 	"github.com/buger/jsonparser"
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
@@ -229,12 +230,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 
 	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
 
-	activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
-	if activitiesErr != nil {
-		errL = append(errL, activitiesErr)
-		writeError(errL, w, &labels)
-		return
-	}
+	activityControl := privacy.NewActivityControl(&account.Privacy)
 
 	secGPC := r.Header.Get("Sec-GPC")
 
@@ -253,7 +249,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 		HookExecutor:               hookExecutor,
 		QueryParams:                r.URL.Query(),
 		TCF2Config:                 tcf2Config,
-		Activities:                 activities,
+		Activities:                 activityControl,
 		TmaxAdjustments:            deps.tmaxAdjustments,
 	}
 
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 6fe5aab9..aeb5c20e 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -194,14 +194,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 
 	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
 
-	activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
-	if activitiesErr != nil {
-		errL = append(errL, activitiesErr)
-		if errortypes.ContainsFatalError(errL) {
-			writeError(errL, w, &labels)
-			return
-		}
-	}
+	activityControl := privacy.NewActivityControl(&account.Privacy)
 
 	ctx := context.Background()
 
@@ -252,7 +245,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 		PubID:                      labels.PubID,
 		HookExecutor:               hookExecutor,
 		TCF2Config:                 tcf2Config,
-		Activities:                 activities,
+		Activities:                 activityControl,
 		TmaxAdjustments:            deps.tmaxAdjustments,
 	}
 	auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index 5f848446..a2a519fa 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -303,14 +303,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		return
 	}
 
-	activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
-	if activitiesErr != nil {
-		errL = append(errL, activitiesErr)
-		if errortypes.ContainsFatalError(errL) {
-			handleError(&labels, w, errL, &vo, &debugLog)
-			return
-		}
-	}
+	activityControl := privacy.NewActivityControl(&account.Privacy)
 
 	secGPC := r.Header.Get("Sec-GPC")
 	auctionRequest := &exchange.AuctionRequest{
@@ -324,7 +317,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		PubID:                      labels.PubID,
 		HookExecutor:               hookexecution.EmptyHookExecutor{},
 		TmaxAdjustments:            deps.tmaxAdjustments,
-		Activities:                 activities,
+		Activities:                 activityControl,
 	}
 
 	auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, &debugLog)
diff --git a/endpoints/setuid.go b/endpoints/setuid.go
index 30a1cce2..64178444 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -104,15 +104,27 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
 			return
 		}
 
-		activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
-		if activitiesErr != nil {
-			if errortypes.ContainsFatalError([]error{activitiesErr}) {
-				activities = privacy.ActivityControl{}
-			}
+		activityControl := privacy.NewActivityControl(&account.Privacy)
+
+		gppSID, err := stringutil.StrToInt8Slice(query.Get("gpp_sid"))
+		if err != nil {
+			err := fmt.Errorf("invalid gpp_sid encoding, must be a csv list of integers")
+			w.WriteHeader(http.StatusBadRequest)
+			w.Write([]byte(err.Error()))
+			metricsEngine.RecordSetUid(metrics.SetUidBadRequest)
+			so.Errors = []error{err}
+			so.Status = http.StatusBadRequest
+			return
 		}
 
-		userSyncActivityAllowed := activities.Allow(privacy.ActivitySyncUser,
-			privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidderName})
+		policies := privacy.Policies{
+			GPPSID: gppSID,
+		}
+
+		userSyncActivityAllowed := activityControl.Allow(privacy.ActivitySyncUser,
+			privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidderName},
+			privacy.NewRequestFromPolicies(policies))
+
 		if !userSyncActivityAllowed {
 			w.WriteHeader(http.StatusUnavailableForLegalReasons)
 			return
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 484fe59c..f64b366d 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -133,6 +133,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/outbrain"
 	"github.com/prebid/prebid-server/adapters/ownadx"
 	"github.com/prebid/prebid-server/adapters/pangle"
+	"github.com/prebid/prebid-server/adapters/pgamssp"
 	"github.com/prebid/prebid-server/adapters/pubmatic"
 	"github.com/prebid/prebid-server/adapters/pubnative"
 	"github.com/prebid/prebid-server/adapters/pulsepoint"
@@ -287,6 +288,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderGamoshi:           gamoshi.Builder,
 		openrtb_ext.BidderGlobalsun:         globalsun.Builder,
 		openrtb_ext.BidderGothamads:         gothamads.Builder,
+		openrtb_ext.BidderGreedygame:        limelightDigital.Builder,
 		openrtb_ext.BidderGrid:              grid.Builder,
 		openrtb_ext.BidderGumGum:            gumgum.Builder,
 		openrtb_ext.BidderHuaweiAds:         huaweiads.Builder,
@@ -337,6 +339,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderOwnAdx:            ownadx.Builder,
 		openrtb_ext.BidderPangle:            pangle.Builder,
 		openrtb_ext.BidderPGAM:              adtelligent.Builder,
+		openrtb_ext.BidderPGAMSsp:           pgamssp.Builder,
 		openrtb_ext.BidderPubmatic:          pubmatic.Builder,
 		openrtb_ext.BidderPubnative:         pubnative.Builder,
 		openrtb_ext.BidderPulsepoint:        pulsepoint.Builder,
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 70d92c08..136f1040 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -319,7 +319,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog
 		Prebid: *requestExtPrebid,
 		SChain: requestExt.GetSChain(),
 	}
-	bidderRequests, privacyLabels, errs := e.requestSplitter.cleanOpenRTBRequests(ctx, *r, requestExtLegacy, gdprDefaultValue)
+	bidderRequests, privacyLabels, errs := e.requestSplitter.cleanOpenRTBRequests(ctx, *r, requestExtLegacy, gdprDefaultValue, bidAdjustmentFactors)
 	errs = append(errs, floorErrs...)
 
 	mergedBidAdj, err := bidadjustment.Merge(r.BidRequestWrapper, r.Account.BidAdjustments)
diff --git a/exchange/utils.go b/exchange/utils.go
index 267d928c..0becca21 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -54,9 +54,8 @@ type requestSplitter struct {
 func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 	auctionReq AuctionRequest,
 	requestExt *openrtb_ext.ExtRequest,
-	gdprDefaultValue gdpr.Signal,
+	gdprDefaultValue gdpr.Signal, bidAdjustmentFactors map[string]float64,
 ) (allowedBidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) {
-
 	req := auctionReq.BidRequestWrapper
 	aliases, errs := parseAliases(req.BidRequest)
 	if len(errs) > 0 {
@@ -93,6 +92,11 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 		}
 	}
 
+	if auctionReq.Account.PriceFloors.IsAdjustForBidAdjustmentEnabled() {
+		//Apply BidAdjustmentFactor to imp.BidFloor
+		applyBidAdjustmentToFloor(allBidderRequests, bidAdjustmentFactors)
+	}
+
 	gdprSignal, err := getGDPR(req)
 	if err != nil {
 		errs = append(errs, err)
@@ -153,7 +157,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 
 		// fetchBids activity
 		scopedName := privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidderRequest.BidderName.String()}
-		fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids, scopedName)
+		fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids, scopedName, privacy.NewRequestFromBidRequest(*req))
 		if !fetchBidsActivityAllowed {
 			// skip the call to a bidder if fetchBids activity is not allowed
 			// do not add this bidder to allowedBidderRequests
@@ -173,7 +177,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 			}
 		}
 
-		passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName)
+		passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName, privacy.NewRequestFromBidRequest(*req))
 		if !passIDActivityAllowed {
 			privacyEnforcement.UFPD = true
 		} else {
@@ -190,7 +194,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 			privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String())
 		}
 
-		passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName)
+		passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName, privacy.NewRequestFromBidRequest(*req))
 		if !passGeoActivityAllowed {
 			privacyEnforcement.PreciseGeo = true
 		} else {
@@ -212,7 +216,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 			applyFPD(auctionReq.FirstPartyData[bidderRequest.BidderName], bidderRequest.BidRequest)
 		}
 
-		privacyEnforcement.TID = !auctionReq.Activities.Allow(privacy.ActivityTransmitTids, scopedName)
+		privacyEnforcement.TID = !auctionReq.Activities.Allow(privacy.ActivityTransmitTIDs, scopedName, privacy.NewRequestFromBidRequest(*req))
 
 		privacyEnforcement.Apply(bidderRequest.BidRequest, auctionReq.Account.Privacy)
 		allowedBidderRequests = append(allowedBidderRequests, bidderRequest)
@@ -1071,3 +1075,25 @@ func getPrebidMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
 		Message: errMsg,
 	}
 }
+
+func applyBidAdjustmentToFloor(allBidderRequests []BidderRequest, bidAdjustmentFactors map[string]float64) {
+
+	if len(bidAdjustmentFactors) == 0 {
+		return
+	}
+
+	for _, bidderRequest := range allBidderRequests {
+		bidAdjustment := 1.0
+
+		if bidAdjustemntValue, ok := bidAdjustmentFactors[string(bidderRequest.BidderName)]; ok {
+			bidAdjustment = bidAdjustemntValue
+		}
+
+		if bidAdjustment != 1.0 {
+			for index, imp := range bidderRequest.BidRequest.Imp {
+				imp.BidFloor = imp.BidFloor / bidAdjustment
+				bidderRequest.BidRequest.Imp[index] = imp
+			}
+		}
+	}
+}
diff --git a/macros/macros.go b/macros/macros.go
index 7292e0c8..609e72cd 100644
--- a/macros/macros.go
+++ b/macros/macros.go
@@ -5,7 +5,7 @@ import (
 	"text/template"
 )
 
-// EndpointTemplateParams specifies params for an endpoint template
+// EndpointTemplateParams specifies macros for bidder endpoints.
 type EndpointTemplateParams struct {
 	Host        string
 	PublisherID string
@@ -17,8 +17,8 @@ type EndpointTemplateParams struct {
 	GvlID       string
 }
 
-// UserSyncTemplateParams specifies params for an user sync URL template
-type UserSyncTemplateParams struct {
+// UserSyncPrivacy specifies privacy policy macros, represented as strings, for user sync urls.
+type UserSyncPrivacy struct {
 	GDPR        string
 	GDPRConsent string
 	USPrivacy   string
@@ -30,10 +30,10 @@ type UserSyncTemplateParams struct {
 func ResolveMacros(aTemplate *template.Template, params interface{}) (string, error) {
 	strBuf := bytes.Buffer{}
 
-	err := aTemplate.Execute(&strBuf, params)
-	if err != nil {
+	if err := aTemplate.Execute(&strBuf, params); err != nil {
 		return "", err
 	}
+
 	res := strBuf.String()
 	return res, nil
 }
diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go
index 85cfed5c..2e190389 100644
--- a/openrtb_ext/bid.go
+++ b/openrtb_ext/bid.go
@@ -67,6 +67,7 @@ type ExtBidPrebidMeta struct {
 	RendererName         string          `json:"rendererName,omitempty"`
 	RendererVersion      string          `json:"rendererVersion,omitempty"`
 	RendererData         json.RawMessage `json:"rendererData,omitempty"`
+	RendererUrl          string          `json:"rendererUrl,omitempty"`
 	SecondaryCategoryIDs []string        `json:"secondaryCatIds,omitempty"`
 }
 
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index deeb1c58..1fb90e97 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -111,6 +111,7 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderGamoshi,
 	BidderGlobalsun,
 	BidderGothamads,
+	BidderGreedygame,
 	BidderGrid,
 	BidderGumGum,
 	BidderHuaweiAds,
@@ -161,6 +162,7 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderOwnAdx,
 	BidderPangle,
 	BidderPGAM,
+	BidderPGAMSsp,
 	BidderPubmatic,
 	BidderPubnative,
 	BidderPulsepoint,
@@ -403,6 +405,7 @@ const (
 	BidderGamoshi           BidderName = "gamoshi"
 	BidderGlobalsun         BidderName = "globalsun"
 	BidderGothamads         BidderName = "gothamads"
+	BidderGreedygame        BidderName = "greedygame"
 	BidderGrid              BidderName = "grid"
 	BidderGumGum            BidderName = "gumgum"
 	BidderHuaweiAds         BidderName = "huaweiads"
@@ -453,6 +456,7 @@ const (
 	BidderOwnAdx            BidderName = "ownadx"
 	BidderPangle            BidderName = "pangle"
 	BidderPGAM              BidderName = "pgam"
+	BidderPGAMSsp           BidderName = "pgamssp"
 	BidderPubmatic          BidderName = "pubmatic"
 	BidderPubnative         BidderName = "pubnative"
 	BidderPulsepoint        BidderName = "pulsepoint"
diff --git a/openrtb_ext/imp_pgamssp.go b/openrtb_ext/imp_pgamssp.go
new file mode 100644
index 00000000..56e4b8bf
--- /dev/null
+++ b/openrtb_ext/imp_pgamssp.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ImpExtPgamSsp struct {
+	PlacementID string `json:"placementId"`
+	EndpointID  string `json:"endpointId"`
+}
diff --git a/privacy/activity.go b/privacy/activity.go
index 6ca48ae0..2309d020 100644
--- a/privacy/activity.go
+++ b/privacy/activity.go
@@ -1,6 +1,7 @@
 package privacy
 
-// Activity defines privileges which can be controlled directly by the publisher or via privacy policies.
+// Activity defines Prebid Server actions which can be controlled directly
+// by the publisher or via privacy policies.
 type Activity int
 
 const (
@@ -10,8 +11,8 @@ const (
 	ActivityReportAnalytics
 	ActivityTransmitUserFPD
 	ActivityTransmitPreciseGeo
-	ActivityTransmitUniqueRequestIds
-	ActivityTransmitTids
+	ActivityTransmitUniqueRequestIDs
+	ActivityTransmitTIDs
 )
 
 func (a Activity) String() string {
@@ -28,9 +29,9 @@ func (a Activity) String() string {
 		return "transmitUfpd"
 	case ActivityTransmitPreciseGeo:
 		return "transmitPreciseGeo"
-	case ActivityTransmitUniqueRequestIds:
+	case ActivityTransmitUniqueRequestIDs:
 		return "transmitUniqueRequestIds"
-	case ActivityTransmitTids:
+	case ActivityTransmitTIDs:
 		return "transmitTid"
 	}
 
diff --git a/privacy/activitycontrol.go b/privacy/activitycontrol.go
index 5d68ce71..9d6668b3 100644
--- a/privacy/activitycontrol.go
+++ b/privacy/activitycontrol.go
@@ -2,6 +2,7 @@ package privacy
 
 import (
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
 type ActivityResult int
@@ -14,122 +15,93 @@ const (
 
 const defaultActivityResult = true
 
+func NewRequestFromPolicies(p Policies) ActivityRequest {
+	return ActivityRequest{policies: &p}
+}
+
+func NewRequestFromBidRequest(r openrtb_ext.RequestWrapper) ActivityRequest {
+	return ActivityRequest{bidRequest: &r}
+}
+
+type ActivityRequest struct {
+	policies   *Policies
+	bidRequest *openrtb_ext.RequestWrapper
+}
+
+func (r ActivityRequest) IsPolicies() bool {
+	return r.policies != nil
+}
+
+func (r ActivityRequest) IsBidRequest() bool {
+	return r.bidRequest != nil
+}
+
 type ActivityControl struct {
 	plans map[Activity]ActivityPlan
 }
 
-func NewActivityControl(privacyConf *config.AccountPrivacy) (ActivityControl, error) {
+func NewActivityControl(cfg *config.AccountPrivacy) ActivityControl {
 	ac := ActivityControl{}
-	var err error
-
-	if privacyConf == nil || privacyConf.AllowActivities == nil {
-		return ac, nil
-	}
-
-	plans := make(map[Activity]ActivityPlan)
 
-	plans[ActivitySyncUser], err = buildEnforcementPlan(privacyConf.AllowActivities.SyncUser)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityFetchBids], err = buildEnforcementPlan(privacyConf.AllowActivities.FetchBids)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityEnrichUserFPD], err = buildEnforcementPlan(privacyConf.AllowActivities.EnrichUserFPD)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityReportAnalytics], err = buildEnforcementPlan(privacyConf.AllowActivities.ReportAnalytics)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityTransmitUserFPD], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitUserFPD)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityTransmitPreciseGeo], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitPreciseGeo)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityTransmitUniqueRequestIds], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitUniqueRequestIds)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityTransmitTids], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitTids)
-	if err != nil {
-		return ac, err
+	if cfg == nil || cfg.AllowActivities == nil {
+		return ac
 	}
 
+	plans := make(map[Activity]ActivityPlan, 8)
+	plans[ActivitySyncUser] = buildPlan(cfg.AllowActivities.SyncUser)
+	plans[ActivityFetchBids] = buildPlan(cfg.AllowActivities.FetchBids)
+	plans[ActivityEnrichUserFPD] = buildPlan(cfg.AllowActivities.EnrichUserFPD)
+	plans[ActivityReportAnalytics] = buildPlan(cfg.AllowActivities.ReportAnalytics)
+	plans[ActivityTransmitUserFPD] = buildPlan(cfg.AllowActivities.TransmitUserFPD)
+	plans[ActivityTransmitPreciseGeo] = buildPlan(cfg.AllowActivities.TransmitPreciseGeo)
+	plans[ActivityTransmitUniqueRequestIDs] = buildPlan(cfg.AllowActivities.TransmitUniqueRequestIds)
+	plans[ActivityTransmitTIDs] = buildPlan(cfg.AllowActivities.TransmitTids)
 	ac.plans = plans
 
-	return ac, nil
+	return ac
 }
 
-func buildEnforcementPlan(activity config.Activity) (ActivityPlan, error) {
-	ef := ActivityPlan{}
-	rules, err := activityRulesToEnforcementRules(activity.Rules)
-	if err != nil {
-		return ef, err
+func buildPlan(activity config.Activity) ActivityPlan {
+	return ActivityPlan{
+		rules:         cfgToRules(activity.Rules),
+		defaultResult: cfgToDefaultResult(activity.Default),
 	}
-	ef.defaultResult = activityDefaultToDefaultResult(activity.Default)
-	ef.rules = rules
-	return ef, nil
 }
 
-func activityRulesToEnforcementRules(rules []config.ActivityRule) ([]Rule, error) {
+func cfgToRules(rules []config.ActivityRule) []Rule {
 	var enfRules []Rule
 
 	for _, r := range rules {
-		var result ActivityResult
+		result := ActivityDeny
 		if r.Allow {
 			result = ActivityAllow
-		} else {
-			result = ActivityDeny
 		}
 
-		componentName, err := conditionToRuleComponentNames(r.Condition.ComponentName)
-		if err != nil {
-			return nil, err
-		}
-
-		er := ComponentEnforcementRule{
+		er := ConditionRule{
 			result:        result,
-			componentName: componentName,
+			componentName: r.Condition.ComponentName,
 			componentType: r.Condition.ComponentType,
 		}
 		enfRules = append(enfRules, er)
 	}
-	return enfRules, nil
-}
-
-func conditionToRuleComponentNames(conditions []string) ([]Component, error) {
-	sn := make([]Component, 0)
-	for _, condition := range conditions {
-		scope, err := ParseComponent(condition)
-		if err != nil {
-			return nil, err
-		}
-		sn = append(sn, scope)
-	}
-	return sn, nil
+	return enfRules
 }
 
-func activityDefaultToDefaultResult(activityDefault *bool) bool {
+func cfgToDefaultResult(activityDefault *bool) bool {
 	if activityDefault == nil {
 		return defaultActivityResult
 	}
 	return *activityDefault
 }
 
-func (e ActivityControl) Allow(activity Activity, target Component) bool {
+func (e ActivityControl) Allow(activity Activity, target Component, request ActivityRequest) bool {
 	plan, planDefined := e.plans[activity]
 
 	if !planDefined {
 		return defaultActivityResult
 	}
 
-	return plan.Evaluate(target)
+	return plan.Evaluate(target, request)
 }
 
 type ActivityPlan struct {
@@ -137,9 +109,9 @@ type ActivityPlan struct {
 	rules         []Rule
 }
 
-func (p ActivityPlan) Evaluate(target Component) bool {
+func (p ActivityPlan) Evaluate(target Component, request ActivityRequest) bool {
 	for _, rule := range p.rules {
-		result := rule.Evaluate(target)
+		result := rule.Evaluate(target, request)
 		if result == ActivityDeny || result == ActivityAllow {
 			return result == ActivityAllow
 		}
diff --git a/privacy/component.go b/privacy/component.go
index a203c6bd..33767938 100644
--- a/privacy/component.go
+++ b/privacy/component.go
@@ -1,8 +1,6 @@
 package privacy
 
 import (
-	"errors"
-	"fmt"
 	"strings"
 )
 
@@ -18,48 +16,10 @@ type Component struct {
 	Name string
 }
 
-func (c Component) Matches(target Component) bool {
-	return strings.EqualFold(c.Type, target.Type) && (c.Name == "*" || strings.EqualFold(c.Name, target.Name))
+func (c Component) MatchesName(v string) bool {
+	return strings.EqualFold(c.Name, v)
 }
 
-var ErrComponentEmpty = errors.New("unable to parse empty component")
-
-func ParseComponent(v string) (Component, error) {
-	if len(v) == 0 {
-		return Component{}, ErrComponentEmpty
-	}
-
-	split := strings.Split(v, ".")
-
-	if len(split) == 2 {
-		if !validComponentType(split[0]) {
-			return Component{}, fmt.Errorf("unable to parse component (invalid type): %s", v)
-		}
-		return Component{
-			Type: split[0],
-			Name: split[1],
-		}, nil
-	}
-
-	if len(split) == 1 {
-		return Component{
-			Type: ComponentTypeBidder,
-			Name: split[0],
-		}, nil
-	}
-
-	return Component{}, fmt.Errorf("unable to parse component: %s", v)
-}
-
-func validComponentType(t string) bool {
-	t = strings.ToLower(t)
-
-	if t == ComponentTypeBidder ||
-		t == ComponentTypeAnalytics ||
-		t == ComponentTypeRealTimeData ||
-		t == ComponentTypeGeneral {
-		return true
-	}
-
-	return false
+func (c Component) MatchesType(v string) bool {
+	return strings.EqualFold(c.Type, v)
 }
diff --git a/privacy/gdpr/policy.go b/privacy/gdpr/validate.go
similarity index 68%
rename from privacy/gdpr/policy.go
rename to privacy/gdpr/validate.go
index 0464a9ff..662ad852 100644
--- a/privacy/gdpr/policy.go
+++ b/privacy/gdpr/validate.go
@@ -4,12 +4,6 @@ import (
 	"github.com/prebid/go-gdpr/vendorconsent"
 )
 
-// Policy represents the GDPR regulation for an OpenRTB bid request.
-type Policy struct {
-	Signal  string
-	Consent string
-}
-
 // ValidateConsent returns true if the consent string is empty or valid per the IAB TCF spec.
 func ValidateConsent(consent string) bool {
 	_, err := vendorconsent.ParseString(consent)
diff --git a/privacy/gdpr/policy_test.go b/privacy/gdpr/validate_test.go
similarity index 100%
rename from privacy/gdpr/policy_test.go
rename to privacy/gdpr/validate_test.go
diff --git a/privacy/gpp/gpp.go b/privacy/gpp/sid.go
similarity index 76%
rename from privacy/gpp/gpp.go
rename to privacy/gpp/sid.go
index 35000406..6e10eac9 100644
--- a/privacy/gpp/gpp.go
+++ b/privacy/gpp/sid.go
@@ -5,13 +5,6 @@ import (
 	gppConstants "github.com/prebid/go-gpp/constants"
 )
 
-// Policy represents the GPP privacy string container.
-// Currently just a placeholder until more expansive support is made.
-type Policy struct {
-	Consent string
-	RawSID  string // This is the CSV format ("2,6") that the IAB recommends for passing the SID(s) on a query string.
-}
-
 // IsSIDInList returns true if the 'sid' value is found in the gppSIDs array. Its logic is used in more than
 // one place in our codebase, therefore it was decided to make it its own function.
 func IsSIDInList(gppSIDs []int8, sid gppConstants.SectionID) bool {
diff --git a/privacy/gpp/gpp_test.go b/privacy/gpp/sid_test.go
similarity index 100%
rename from privacy/gpp/gpp_test.go
rename to privacy/gpp/sid_test.go
diff --git a/privacy/policies.go b/privacy/policies.go
index 214696e1..16fee92c 100644
--- a/privacy/policies.go
+++ b/privacy/policies.go
@@ -1,16 +1,6 @@
 package privacy
 
-import (
-	"github.com/prebid/prebid-server/privacy/ccpa"
-	"github.com/prebid/prebid-server/privacy/gdpr"
-	"github.com/prebid/prebid-server/privacy/gpp"
-	"github.com/prebid/prebid-server/privacy/lmt"
-)
-
-// Policies represents the privacy regulations for an OpenRTB bid request.
+// Policies contains privacy signals and consent for non-OpenRTB activities.
 type Policies struct {
-	CCPA ccpa.Policy
-	GDPR gdpr.Policy
-	LMT  lmt.Policy
-	GPP  gpp.Policy
+	GPPSID []int8
 }
diff --git a/privacy/rule.go b/privacy/rule.go
new file mode 100644
index 00000000..ef370f09
--- /dev/null
+++ b/privacy/rule.go
@@ -0,0 +1,5 @@
+package privacy
+
+type Rule interface {
+	Evaluate(target Component, request ActivityRequest) ActivityResult
+}
diff --git a/privacy/rule_condition.go b/privacy/rule_condition.go
new file mode 100644
index 00000000..2fc34506
--- /dev/null
+++ b/privacy/rule_condition.go
@@ -0,0 +1,84 @@
+package privacy
+
+// noClausesDefinedResult represents the default return when there is no matching criteria specified.
+const noClausesDefinedResult = true
+
+type ConditionRule struct {
+	result        ActivityResult
+	componentName []string
+	componentType []string
+	gppSID        []int8
+}
+
+func (r ConditionRule) Evaluate(target Component, request ActivityRequest) ActivityResult {
+	if matched := evaluateComponentName(target, r.componentName); !matched {
+		return ActivityAbstain
+	}
+
+	if matched := evaluateComponentType(target, r.componentType); !matched {
+		return ActivityAbstain
+	}
+
+	if matched := evaluateGPPSID(r.gppSID, request); !matched {
+		return ActivityAbstain
+	}
+
+	return r.result
+}
+
+func evaluateComponentName(target Component, componentNames []string) bool {
+	// no clauses are considered a match
+	if len(componentNames) == 0 {
+		return noClausesDefinedResult
+	}
+
+	for _, n := range componentNames {
+		if target.MatchesName(n) {
+			return true
+		}
+	}
+
+	return false
+}
+
+func evaluateComponentType(target Component, componentTypes []string) bool {
+	if len(componentTypes) == 0 {
+		return noClausesDefinedResult
+	}
+
+	// if there are clauses, at least one needs to match
+	for _, t := range componentTypes {
+		if target.MatchesType(t) {
+			return true
+		}
+	}
+
+	return false
+}
+
+func evaluateGPPSID(sid []int8, request ActivityRequest) bool {
+	if len(sid) == 0 {
+		return noClausesDefinedResult
+	}
+
+	for _, x := range getGPPSID(request) {
+		for _, y := range sid {
+			if x == y {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func getGPPSID(request ActivityRequest) []int8 {
+	if request.IsPolicies() {
+		return request.policies.GPPSID
+	}
+
+	if request.IsBidRequest() && request.bidRequest.Regs != nil {
+		return request.bidRequest.Regs.GPPSID
+	}
+
+	return nil
+}
diff --git a/privacy/rules.go b/privacy/rules.go
deleted file mode 100644
index 502e10ef..00000000
--- a/privacy/rules.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package privacy
-
-import (
-	"strings"
-)
-
-type Rule interface {
-	Evaluate(target Component) ActivityResult
-}
-
-type ComponentEnforcementRule struct {
-	result        ActivityResult
-	componentName []Component
-	componentType []string
-}
-
-func (r ComponentEnforcementRule) Evaluate(target Component) ActivityResult {
-	if matched := evaluateComponentName(target, r.componentName); !matched {
-		return ActivityAbstain
-	}
-
-	if matched := evaluateComponentType(target, r.componentType); !matched {
-		return ActivityAbstain
-	}
-
-	return r.result
-}
-
-func evaluateComponentName(target Component, componentNames []Component) bool {
-	// no clauses are considered a match
-	if len(componentNames) == 0 {
-		return true
-	}
-
-	// if there are clauses, at least one needs to match
-	for _, n := range componentNames {
-		if n.Matches(target) {
-			return true
-		}
-	}
-
-	return false
-}
-
-func evaluateComponentType(target Component, componentTypes []string) bool {
-	// no clauses are considered a match
-	if len(componentTypes) == 0 {
-		return true
-	}
-
-	// if there are clauses, at least one needs to match
-	for _, s := range componentTypes {
-		if strings.EqualFold(s, target.Type) {
-			return true
-		}
-	}
-
-	return false
-}
diff --git a/privacy/scrubber.go b/privacy/scrubber.go
index 4cc90c82..59b74a25 100644
--- a/privacy/scrubber.go
+++ b/privacy/scrubber.go
@@ -2,10 +2,11 @@ package privacy
 
 import (
 	"encoding/json"
+	"net"
+
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/util/iputil"
 	"github.com/prebid/prebid-server/util/ptrutil"
-	"net"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
 )
@@ -92,9 +93,11 @@ func (s scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enfo
 	var userExtParsed map[string]json.RawMessage
 	userExtModified := false
 
+	// expressed in two lines because IntelliJ cannot infer the generic type
 	var userCopy *openrtb2.User
 	userCopy = ptrutil.Clone(bidRequest.User)
 
+	// expressed in two lines because IntelliJ cannot infer the generic type
 	var deviceCopy *openrtb2.Device
 	deviceCopy = ptrutil.Clone(bidRequest.Device)
 
@@ -152,8 +155,7 @@ func (s scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enfo
 	if enforcement.TID {
 		//remove source.tid and imp.ext.tid
 		if bidRequest.Source != nil {
-			var sourceCopy *openrtb2.Source
-			sourceCopy = ptrutil.Clone(bidRequest.Source)
+			sourceCopy := ptrutil.Clone(bidRequest.Source)
 			sourceCopy.TID = ""
 			bidRequest.Source = sourceCopy
 		}
diff --git a/static/bidder-info/greedygame.yaml b/static/bidder-info/greedygame.yaml
new file mode 100644
index 00000000..5e73e1fe
--- /dev/null
+++ b/static/bidder-info/greedygame.yaml
@@ -0,0 +1,16 @@
+endpoint: "http://ads-pbs.rtb-greedygame.com/openrtb/{{.PublisherID}}?host={{.Host}}"
+maintainer:
+  email: "engineering@project-limelight.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - audio
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - audio
+      - native
diff --git a/static/bidder-info/pgamssp.yaml b/static/bidder-info/pgamssp.yaml
new file mode 100644
index 00000000..a762d8f1
--- /dev/null
+++ b/static/bidder-info/pgamssp.yaml
@@ -0,0 +1,19 @@
+endpoint: "http://us-east.pgammedia.com/pserver"
+maintainer:
+  email: "info@pgammedia.com"
+capabilities:
+  site:
+    mediaTypes:
+    - banner
+    - video
+    - native
+    
+  app:
+    mediaTypes:
+    - banner
+    - video
+    - native
+userSync:
+  redirect:
+    url: "https://cs.pgammedia.com/pserver?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir={{.RedirectURL}}"
+    userMacro: "[UID]"
diff --git a/static/bidder-params/greedygame.json b/static/bidder-params/greedygame.json
new file mode 100644
index 00000000..2057c147
--- /dev/null
+++ b/static/bidder-params/greedygame.json
@@ -0,0 +1,23 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "GreedyGame Adapter Params",
+  "description": "A schema which validates params accepted by the GreedyGame adapter",
+  "type": "object",
+
+  "properties": {
+    "host": {
+      "type": "string",
+      "description": "Ad network's RTB host",
+      "format": "hostname",
+      "pattern": "^.+\\..+$"
+    },
+    "publisherId": {
+      "type": ["integer", "string"],
+      "description": "Publisher ID",
+      "minimum": 1,
+      "pattern": "^[1-9][0-9]*$"
+    }
+  },
+
+  "required": ["host", "publisherId"]
+}
diff --git a/static/bidder-params/pgamssp.json b/static/bidder-params/pgamssp.json
new file mode 100644
index 00000000..9d060241
--- /dev/null
+++ b/static/bidder-params/pgamssp.json
@@ -0,0 +1,23 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "PgamSSP Adapter Params",
+    "description": "A schema which validates params accepted by the PgamSSP adapter",
+    "type": "object",
+  
+    "properties": {
+      "placementId": {
+        "type": "string",
+        "minLength": 1,
+        "description": "Placement ID"
+      },
+      "endpointId": {
+        "type": "string",
+        "minLength": 1,
+        "description": "Endpoint ID"
+      }
+    },
+    "oneOf": [
+      { "required": ["placementId"] },
+      { "required": ["endpointId"] }
+    ]
+  }
\ No newline at end of file
diff --git a/usersync/syncer.go b/usersync/syncer.go
index fb14201f..e561614f 100644
--- a/usersync/syncer.go
+++ b/usersync/syncer.go
@@ -11,14 +11,13 @@ import (
 	validator "github.com/asaskevich/govalidator"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/macros"
-	"github.com/prebid/prebid-server/privacy"
 )
 
 var (
-	errNoSyncTypesProvided        = errors.New("no sync types provided")
-	errNoSyncTypesSupported       = errors.New("no sync types supported")
-	errDefaultTypeMissingIFrame   = errors.New("default is set to iframe but no iframe endpoint is configured")
-	errDefaultTypeMissingRedirect = errors.New("default is set to redirect but no redirect endpoint is configured")
+	ErrSyncerEndpointRequired = errors.New("at least one endpoint (iframe and/or redirect) is required")
+	ErrSyncerKeyRequired      = errors.New("key is required")
+	errNoSyncTypesProvided    = errors.New("no sync types provided")
+	errNoSyncTypesSupported   = errors.New("no sync types supported")
 )
 
 // Syncer represents the user sync configuration for a bidder or a shared set of bidders.
@@ -35,7 +34,7 @@ type Syncer interface {
 
 	// GetSync returns a user sync for the user's device to perform, or an error if the none of the
 	// sync types are supported or if macro substitution fails.
-	GetSync(syncTypes []SyncType, privacyPolicies privacy.Policies) (Sync, error)
+	GetSync(syncTypes []SyncType, userSyncMacros macros.UserSyncPrivacy) (Sync, error)
 }
 
 // Sync represents a user sync to be performed by the user's device.
@@ -58,9 +57,6 @@ const (
 	setuidSyncTypeRedirect = "i" // i = image response
 )
 
-var ErrSyncerEndpointRequired = errors.New("at least one endpoint (iframe and/or redirect) is required")
-var ErrSyncerKeyRequired = errors.New("key is required")
-
 // NewSyncer creates a new Syncer from the provided configuration, or return an error if macro substition
 // fails or an endpoint url is invalid.
 func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer, bidder string) (Syncer, error) {
@@ -170,7 +166,7 @@ func escapeTemplate(x string) string {
 	return escaped.String()
 }
 
-var templateTestValues = macros.UserSyncTemplateParams{
+var templateTestValues = macros.UserSyncPrivacy{
 	GDPR:        "anyGDPR",
 	GDPRConsent: "anyGDPRConsent",
 	USPrivacy:   "anyCCPAConsent",
@@ -219,7 +215,7 @@ func (s standardSyncer) filterSupportedSyncTypes(syncTypes []SyncType) []SyncTyp
 	return supported
 }
 
-func (s standardSyncer) GetSync(syncTypes []SyncType, privacyPolicies privacy.Policies) (Sync, error) {
+func (s standardSyncer) GetSync(syncTypes []SyncType, userSyncMacros macros.UserSyncPrivacy) (Sync, error) {
 	syncType, err := s.chooseSyncType(syncTypes)
 	if err != nil {
 		return Sync{}, err
@@ -227,13 +223,7 @@ func (s standardSyncer) GetSync(syncTypes []SyncType, privacyPolicies privacy.Po
 
 	syncTemplate := s.chooseTemplate(syncType)
 
-	url, err := macros.ResolveMacros(syncTemplate, macros.UserSyncTemplateParams{
-		GDPR:        privacyPolicies.GDPR.Signal,
-		GDPRConsent: privacyPolicies.GDPR.Consent,
-		USPrivacy:   privacyPolicies.CCPA.Consent,
-		GPP:         privacyPolicies.GPP.Consent,
-		GPPSID:      privacyPolicies.GPP.RawSID,
-	})
+	url, err := macros.ResolveMacros(syncTemplate, userSyncMacros)
 	if err != nil {
 		return Sync{}, err
 	}


Test patch:
diff --git a/adapters/pgamssp/params_test.go b/adapters/pgamssp/params_test.go
new file mode 100644
index 00000000..3e603237
--- /dev/null
+++ b/adapters/pgamssp/params_test.go
@@ -0,0 +1,47 @@
+package pgamssp
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json schema. %v", err)
+	}
+
+	for _, p := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderPGAMSsp, json.RawMessage(p)); err != nil {
+			t.Errorf("Schema rejected valid params: %s", p)
+		}
+	}
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json schema. %v", err)
+	}
+
+	for _, p := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderPGAMSsp, json.RawMessage(p)); err == nil {
+			t.Errorf("Schema allowed invalid params: %s", p)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"placementId": "test"}`,
+	`{"placementId": "1"}`,
+	`{"endpointId": "test"}`,
+	`{"endpointId": "1"}`,
+}
+
+var invalidParams = []string{
+	`{"placementId": 42}`,
+	`{"endpointId": 42}`,
+	`{"placementId": "1", "endpointId": "1"}`,
+}
diff --git a/adapters/pgamssp/pgamssp_test.go b/adapters/pgamssp/pgamssp_test.go
new file mode 100644
index 00000000..596b813c
--- /dev/null
+++ b/adapters/pgamssp/pgamssp_test.go
@@ -0,0 +1,20 @@
+package pgamssp
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderPGAMSsp, config.Adapter{
+		Endpoint: "http://test.com/pserver"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "pgamssptest", bidder)
+}
diff --git a/adapters/pgamssp/pgamssptest/exemplary/endpointId.json b/adapters/pgamssp/pgamssptest/exemplary/endpointId.json
new file mode 100644
index 00000000..72805db1
--- /dev/null
+++ b/adapters/pgamssp/pgamssptest/exemplary/endpointId.json
@@ -0,0 +1,125 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "iPad"
+        },
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "tagid": "test",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "endpointId": "test"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://test.com/pserver",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "tagid": "test",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "endpointId": "test",
+                                    "type": "network"
+                                }
+                            }
+                        }
+                    ],
+                    "app": {
+                        "id": "1",
+                        "bundle": "com.wls.testwlsapplication"
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "iPad"
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://test.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "w": 300,
+                                    "h": 250,
+                                    "mtype": 1
+                                }
+                            ],
+                            "seat": "pgamssp"
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://test.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "w": 300,
+                        "h": 250,
+                        "mtype": 1
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/pgamssp/pgamssptest/exemplary/simple-banner.json b/adapters/pgamssp/pgamssptest/exemplary/simple-banner.json
new file mode 100644
index 00000000..3850c449
--- /dev/null
+++ b/adapters/pgamssp/pgamssptest/exemplary/simple-banner.json
@@ -0,0 +1,125 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "iPad"
+        },
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "tagid": "test",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://test.com/pserver",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "tagid": "test",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": "test",
+                                    "type": "publisher"
+                                }
+                            }
+                        }
+                    ],
+                    "app": {
+                        "id": "1",
+                        "bundle": "com.wls.testwlsapplication"
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "iPad"
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://test.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "w": 300,
+                                    "h": 250,
+                                    "mtype": 1
+                                }
+                            ],
+                            "seat": "pgamssp"
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://test.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "w": 300,
+                        "h": 250,
+                        "mtype": 1
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/pgamssp/pgamssptest/exemplary/simple-native.json b/adapters/pgamssp/pgamssptest/exemplary/simple-native.json
new file mode 100644
index 00000000..8796adac
--- /dev/null
+++ b/adapters/pgamssp/pgamssptest/exemplary/simple-native.json
@@ -0,0 +1,109 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "iPad"
+        },
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "tagid": "test",
+                "native": {
+                    "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+                    "ver": "1.1"
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://test.com/pserver",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "tagid": "test",
+                            "native": {
+                                "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+                                "ver": "1.1"
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": "test",
+                                    "type": "publisher"
+                                }
+                            }
+                        }
+                    ],
+                    "app": {
+                        "id": "1",
+                        "bundle": "com.wls.testwlsapplication"
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "iPad"
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://test.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "w": 300,
+                                    "h": 250,
+                                    "mtype": 4
+                                }
+                            ],
+                            "seat": "pgamssp"
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://test.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "w": 300,
+                        "h": 250,
+                        "mtype": 4
+                    },
+                    "type": "native"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/pgamssp/pgamssptest/exemplary/simple-video.json b/adapters/pgamssp/pgamssptest/exemplary/simple-video.json
new file mode 100644
index 00000000..a39d0111
--- /dev/null
+++ b/adapters/pgamssp/pgamssptest/exemplary/simple-video.json
@@ -0,0 +1,120 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "iPad"
+        },
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "tagid": "test",
+                "video": {
+                    "mimes": [
+                        "video/mp4"
+                    ],
+                    "protocols": [
+                        2,
+                        5
+                    ],
+                    "w": 1024,
+                    "h": 576
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ]
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://test.com/pserver",
+                "body": {
+                    "id": "test-request-id",
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "iPad"
+                    },
+                    "app": {
+                        "id": "1",
+                        "bundle": "com.wls.testwlsapplication"
+                    },
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "tagid": "test",
+                            "video": {
+                                "mimes": [
+                                    "video/mp4"
+                                ],
+                                "protocols": [
+                                    2,
+                                    5
+                                ],
+                                "w": 1024,
+                                "h": 576
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": "test",
+                                    "type": "publisher"
+                                }
+                            }
+                        }
+                    ]
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "<VAST version=\"3.0\"><Ad id=\"20001\" sequence=\"1\"><InLine><AdSystem version=\"4.0\"><![CDATA[iabtechlab]]></AdSystem><AdTitle><![CDATA[Inline Simple Ad]]></AdTitle><Impression><![CDATA[]]></Impression><Creatives><Creative id=\"5480\" sequence=\"1\"><Linear><Duration>00:01:00</Duration><MediaFiles><MediaFile id=\"5246\" delivery=\"progressive\" type=\"video/mp4\" bitrate=\"600\" width=\"640\" height=\"360\" minBitrate=\"500\" maxBitrate=\"700\" scalable=\"1\" maintainAspectRatio=\"1\" codec=\"0\"><![CDATA[https://s0.2mdn.net/4253510/google_ddm_animation_480P.mp4]]></MediaFile></MediaFiles><VideoClicks><ClickThrough id=\"blog\"><![CDATA[https://example.com]]></ClickThrough></VideoClicks></Linear></Creative></Creatives></InLine></Ad></VAST>",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "mtype": 2
+                                }
+                            ],
+                            "seat": "pgamssp"
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "<VAST version=\"3.0\"><Ad id=\"20001\" sequence=\"1\"><InLine><AdSystem version=\"4.0\"><![CDATA[iabtechlab]]></AdSystem><AdTitle><![CDATA[Inline Simple Ad]]></AdTitle><Impression><![CDATA[]]></Impression><Creatives><Creative id=\"5480\" sequence=\"1\"><Linear><Duration>00:01:00</Duration><MediaFiles><MediaFile id=\"5246\" delivery=\"progressive\" type=\"video/mp4\" bitrate=\"600\" width=\"640\" height=\"360\" minBitrate=\"500\" maxBitrate=\"700\" scalable=\"1\" maintainAspectRatio=\"1\" codec=\"0\"><![CDATA[https://s0.2mdn.net/4253510/google_ddm_animation_480P.mp4]]></MediaFile></MediaFiles><VideoClicks><ClickThrough id=\"blog\"><![CDATA[https://example.com]]></ClickThrough></VideoClicks></Linear></Creative></Creatives></InLine></Ad></VAST>",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "mtype": 2
+                    },
+                    "type": "video"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/pgamssp/pgamssptest/exemplary/simple-web-banner.json b/adapters/pgamssp/pgamssptest/exemplary/simple-web-banner.json
new file mode 100644
index 00000000..fca8ce17
--- /dev/null
+++ b/adapters/pgamssp/pgamssptest/exemplary/simple-web-banner.json
@@ -0,0 +1,125 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "tagid": "test",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ],
+        "site": {
+            "id": "1",
+            "domain": "test.com"
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ua": "Ubuntu"
+        }
+    },
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "http://test.com/pserver",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "test-imp-id",
+                            "tagid": "test",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    },
+                                    {
+                                        "w": 300,
+                                        "h": 600
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "placementId": "test",
+                                    "type": "publisher"
+                                }
+                            }
+                        }
+                    ],
+                    "site": {
+                        "id": "1",
+                        "domain": "test.com"
+                    },
+                    "device": {
+                        "ip": "123.123.123.123",
+                        "ua": "Ubuntu"
+                    }
+                }
+            },
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "bid": [
+                                {
+                                    "id": "test_bid_id",
+                                    "impid": "test-imp-id",
+                                    "price": 0.27543,
+                                    "adm": "<iframe id=\"adm-banner-1\" width=\"468\" height=\"60\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://test.com/pserver&k=bc2d316f39931a07d9a8dd249bf85fc0\"></iframe>",
+                                    "cid": "test_cid",
+                                    "crid": "test_crid",
+                                    "dealid": "test_dealid",
+                                    "w": 468,
+                                    "h": 60,
+                                    "mtype": 1
+                                }
+                            ],
+                            "seat": "pgamssp"
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+    "expectedBidResponses": [
+        {
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test_bid_id",
+                        "impid": "test-imp-id",
+                        "price": 0.27543,
+                        "adm": "<iframe id=\"adm-banner-1\" width=\"468\" height=\"60\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://test.com/pserver&k=bc2d316f39931a07d9a8dd249bf85fc0\"></iframe>",
+                        "cid": "test_cid",
+                        "crid": "test_crid",
+                        "dealid": "test_dealid",
+                        "w": 468,
+                        "h": 60,
+                        "mtype": 1
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/adapters/pgamssp/pgamssptest/supplemental/bad_media_type.json b/adapters/pgamssp/pgamssptest/supplemental/bad_media_type.json
new file mode 100644
index 00000000..6f016367
--- /dev/null
+++ b/adapters/pgamssp/pgamssptest/supplemental/bad_media_type.json
@@ -0,0 +1,82 @@
+{
+	"mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ],
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+        }
+    },
+	"httpCalls": [{
+		"expectedRequest": {
+			"uri": "http://test.com/pserver",
+			"body": {
+                "id": "test-request-id",
+                "imp": [
+                    {
+                        "id": "test-imp-id",
+                        "ext": {
+                            "bidder": {
+                                "placementId": "test",
+                                "type": "publisher"
+                            }
+                        }
+                    }
+                ],
+                "app": {
+                    "id": "1",
+                    "bundle": "com.wls.testwlsapplication"
+                },
+                "device": {
+                    "ip": "123.123.123.123",
+                    "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+                }
+            }
+		},
+		"mockResponse": {
+			"status": 200,
+			"body": {
+                "id": "test-request-id",
+                "seatbid": [
+                    {
+                        "bid": [
+                            {
+                                "id": "test_bid_id",
+                                "impid": "test-imp-id",
+                                "price": 0.27543,
+                                "adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://test.com/pserver&k=882b2510ed6d6c94fa69c99aa522a708\"></iframe>",
+                                "cid": "test_cid",
+                                "crid": "test_crid",
+                                "dealid": "test_dealid",
+                                "w": 300,
+                                "h": 250,
+                                "ext": {}
+                            }
+                        ],
+                        "seat": "pgamssp"
+                    }
+                ],
+                "cur": "USD"
+            }
+        }
+    }],
+    "expectedMakeBidsErrors": [
+        {
+          "value": "Unable to fetch mediaType in multi-format: test-imp-id",
+          "comparison": "literal"
+        }
+    ]
+}
diff --git a/adapters/pgamssp/pgamssptest/supplemental/bad_response.json b/adapters/pgamssp/pgamssptest/supplemental/bad_response.json
new file mode 100644
index 00000000..8c9f6f52
--- /dev/null
+++ b/adapters/pgamssp/pgamssptest/supplemental/bad_response.json
@@ -0,0 +1,84 @@
+{
+	"mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ],
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+        }
+    },
+	"httpCalls": [{
+		"expectedRequest": {
+			"uri": "http://test.com/pserver",
+			"body": {
+                "id": "test-request-id",
+                "imp": [
+                    {
+                        "id": "test-imp-id",
+                        "banner": {
+                            "format": [
+                                {
+                                    "w": 300,
+                                    "h": 250
+                                },
+                                {
+                                    "w": 300,
+                                    "h": 600
+                                }
+                            ]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": "test",
+                                "type": "publisher"
+                            }
+                        }
+                    }
+                ],
+                "app": {
+                    "id": "1",
+                    "bundle": "com.wls.testwlsapplication"
+                },
+                "device": {
+                    "ip": "123.123.123.123",
+                    "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+                }
+            }
+		},
+		"mockResponse": {
+			"status": 200,
+			"body": ""
+        }
+    }],
+    "expectedMakeBidsErrors": [
+        {
+          "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse",
+          "comparison": "literal"
+        }
+    ]
+}
diff --git a/adapters/pgamssp/pgamssptest/supplemental/status-204.json b/adapters/pgamssp/pgamssptest/supplemental/status-204.json
new file mode 100644
index 00000000..3869591e
--- /dev/null
+++ b/adapters/pgamssp/pgamssptest/supplemental/status-204.json
@@ -0,0 +1,79 @@
+{
+	"mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ],
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+        }
+    },
+	"httpCalls": [{
+		"expectedRequest": {
+			"uri": "http://test.com/pserver",
+			"body": {
+                "id": "test-request-id",
+                "imp": [
+                    {
+                        "id": "test-imp-id",
+                        "banner": {
+                            "format": [
+                                {
+                                    "w": 300,
+                                    "h": 250
+                                },
+                                {
+                                    "w": 300,
+                                    "h": 600
+                                }
+                            ]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": "test",
+                                "type": "publisher"
+                            }
+                        }
+                    }
+                ],
+                "app": {
+                    "id": "1",
+                    "bundle": "com.wls.testwlsapplication"
+                },
+                "device": {
+                    "ip": "123.123.123.123",
+                    "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+                }
+            }
+		},
+		"mockResponse": {
+			"status": 204,
+			"body": {}
+		}
+    }],
+    "expectedBidResponses": []
+}
diff --git a/adapters/pgamssp/pgamssptest/supplemental/status-not-200.json b/adapters/pgamssp/pgamssptest/supplemental/status-not-200.json
new file mode 100644
index 00000000..0c9b3eec
--- /dev/null
+++ b/adapters/pgamssp/pgamssptest/supplemental/status-not-200.json
@@ -0,0 +1,84 @@
+{
+	"mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "test-imp-id",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        },
+                        {
+                            "w": 300,
+                            "h": 600
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "placementId": "test"
+                    }
+                }
+            }
+        ],
+        "app": {
+            "id": "1",
+            "bundle": "com.wls.testwlsapplication"
+        },
+        "device": {
+            "ip": "123.123.123.123",
+            "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+        }
+    },
+	"httpCalls": [{
+		"expectedRequest": {
+			"uri": "http://test.com/pserver",
+			"body": {
+                "id": "test-request-id",
+                "imp": [
+                    {
+                        "id": "test-imp-id",
+                        "banner": {
+                            "format": [
+                                {
+                                    "w": 300,
+                                    "h": 250
+                                },
+                                {
+                                    "w": 300,
+                                    "h": 600
+                                }
+                            ]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": "test",
+                                "type": "publisher"
+                            }
+                        }
+                    }
+                ],
+                "app": {
+                    "id": "1",
+                    "bundle": "com.wls.testwlsapplication"
+                },
+                "device": {
+                    "ip": "123.123.123.123",
+                    "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+                }
+            }
+		},
+		"mockResponse": {
+			"status": 404,
+			"body": {}
+		}
+    }],
+    "expectedMakeBidsErrors": [
+        {
+          "value": "Unexpected status code: 404. Run with request.debug = 1 for more info",
+          "comparison": "literal"
+        }
+    ]
+}
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index 45c27f0e..68ab0ced 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -16,12 +16,11 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/gdpr"
+	"github.com/prebid/prebid-server/macros"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/privacy/ccpa"
-	gdprPrivacy "github.com/prebid/prebid-server/privacy/gdpr"
-	gppPrivacy "github.com/prebid/prebid-server/privacy/gpp"
 	"github.com/prebid/prebid-server/usersync"
 	"github.com/prebid/prebid-server/util/ptrutil"
 
@@ -102,7 +101,7 @@ func TestCookieSyncHandle(t *testing.T) {
 	syncTypeExpected := []usersync.SyncType{usersync.SyncTypeIFrame, usersync.SyncTypeRedirect}
 	sync := usersync.Sync{URL: "aURL", Type: usersync.SyncTypeRedirect, SupportCORS: true}
 	syncer := MockSyncer{}
-	syncer.On("GetSync", syncTypeExpected, privacy.Policies{}).Return(sync, nil).Maybe()
+	syncer.On("GetSync", syncTypeExpected, macros.UserSyncPrivacy{}).Return(sync, nil).Maybe()
 
 	cookieWithSyncs := usersync.NewCookie()
 	cookieWithSyncs.Sync("foo", "anyID")
@@ -366,7 +365,7 @@ func TestExtractGDPRSignal(t *testing.T) {
 			expected: testOutput{
 				gdprSignal: gdpr.SignalAmbiguous,
 				gdprString: "2",
-				err:        &errortypes.BadInput{"GDPR signal should be integer 0 or 1"},
+				err:        &errortypes.BadInput{Message: "GDPR signal should be integer 0 or 1"},
 			},
 		},
 		{
@@ -398,8 +397,9 @@ func TestExtractPrivacyPolicies(t *testing.T) {
 		usersyncDefaultGDPRValue string
 	}
 	type testOutput struct {
-		policies   privacy.Policies
+		macros     macros.UserSyncPrivacy
 		gdprSignal gdpr.Signal
+		policies   privacy.Policies
 		err        error
 	}
 	testCases := []struct {
@@ -413,8 +413,9 @@ func TestExtractPrivacyPolicies(t *testing.T) {
 				request: cookieSyncRequest{GPP: "malformedGPPString"},
 			},
 			expected: testOutput{
-				policies:   privacy.Policies{},
+				macros:     macros.UserSyncPrivacy{},
 				gdprSignal: gdpr.SignalNo,
+				policies:   privacy.Policies{},
 				err:        errors.New("error parsing GPP header, header must have type=3"),
 			},
 		},
@@ -423,14 +424,15 @@ func TestExtractPrivacyPolicies(t *testing.T) {
 			in: testInput{
 				request: cookieSyncRequest{
 					GPP:       "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
-					GPPSid:    "malformed",
+					GPPSID:    "malformed",
 					USPrivacy: "1YYY",
 				},
 			},
 			expected: testOutput{
-				policies:   privacy.Policies{},
+				macros:     macros.UserSyncPrivacy{},
 				gdprSignal: gdpr.SignalNo,
-				err:        &strconv.NumError{"ParseInt", "malformed", strconv.ErrSyntax},
+				policies:   privacy.Policies{},
+				err:        &strconv.NumError{Func: "ParseInt", Num: "malformed", Err: strconv.ErrSyntax},
 			},
 		},
 		{
@@ -438,13 +440,14 @@ func TestExtractPrivacyPolicies(t *testing.T) {
 			in: testInput{
 				request: cookieSyncRequest{
 					GPP:       "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
-					GPPSid:    "6",
+					GPPSID:    "6",
 					USPrivacy: "1YYY",
 				},
 			},
 			expected: testOutput{
-				policies:   privacy.Policies{},
+				macros:     macros.UserSyncPrivacy{},
 				gdprSignal: gdpr.SignalNo,
+				policies:   privacy.Policies{},
 				err:        errors.New("request.us_privacy consent does not match uspv1"),
 			},
 		},
@@ -453,40 +456,36 @@ func TestExtractPrivacyPolicies(t *testing.T) {
 			in: testInput{
 				request: cookieSyncRequest{
 					GPP:    "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
-					GPPSid: "6",
+					GPPSID: "6",
 				},
 			},
 			expected: testOutput{
-				policies: privacy.Policies{
-					GDPR: gdprPrivacy.Policy{
-						Signal:  "0",
-						Consent: "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
-					},
-					CCPA: ccpa.Policy{
-						Consent: "1YNN",
-					},
-					GPP: gppPrivacy.Policy{
-						Consent: "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
-						RawSID:  "6",
-					},
+				macros: macros.UserSyncPrivacy{
+					GDPR:        "0",
+					GDPRConsent: "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
+					USPrivacy:   "1YNN",
+					GPP:         "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN",
+					GPPSID:      "6",
 				},
 				gdprSignal: gdpr.SignalNo,
+				policies:   privacy.Policies{GPPSID: []int8{6}},
 				err:        nil,
 			},
 		},
 	}
 	for _, tc := range testCases {
-		// run
-		outPolicies, outSignal, outErr := extractPrivacyPolicies(tc.in.request, tc.in.usersyncDefaultGDPRValue)
-		// assertions
-		assert.Equal(t, tc.expected.policies, outPolicies, tc.desc)
+		outMacros, outSignal, outPolicies, outErr := extractPrivacyPolicies(tc.in.request, tc.in.usersyncDefaultGDPRValue)
+
+		assert.Equal(t, tc.expected.macros, outMacros, tc.desc)
 		assert.Equal(t, tc.expected.gdprSignal, outSignal, tc.desc)
+		assert.Equal(t, tc.expected.policies, outPolicies, tc.desc)
 		assert.Equal(t, tc.expected.err, outErr, tc.desc)
 	}
 }
 
 func TestCookieSyncParseRequest(t *testing.T) {
 	expectedCCPAParsedPolicy, _ := ccpa.Policy{Consent: "1NYN"}.Parse(map[string]struct{}{})
+	emptyActivityPoliciesRequest := privacy.NewRequestFromPolicies(privacy.Policies{})
 
 	testCases := []struct {
 		description          string
@@ -496,7 +495,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 		givenCCPAEnabled     bool
 		givenAccountRequired bool
 		expectedError        string
-		expectedPrivacy      privacy.Policies
+		expectedPrivacy      macros.UserSyncPrivacy
 		expectedRequest      usersync.Request
 	}{
 
@@ -521,18 +520,12 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{
-				GDPR: gdprPrivacy.Policy{
-					Signal:  "1",
-					Consent: "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
-				},
-				CCPA: ccpa.Policy{
-					Consent: "1NYN",
-				},
-				GPP: gppPrivacy.Policy{
-					Consent: "DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
-					RawSID:  "2",
-				},
+			expectedPrivacy: macros.UserSyncPrivacy{
+				GDPR:        "1",
+				GDPRConsent: "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
+				USPrivacy:   "1NYN",
+				GPP:         "DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA",
+				GPPSID:      "2",
 			},
 			expectedRequest: usersync.Request{
 				Bidders: []string{"a", "b"},
@@ -544,6 +537,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				Privacy: usersyncPrivacy{
 					gdprPermissions:  &fakePermissions{},
 					ccpaParsedPolicy: expectedCCPAParsedPolicy,
+					activityRequest:  privacy.NewRequestFromPolicies(privacy.Policies{GPPSID: []int8{2}}),
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -568,14 +562,10 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{
-				GDPR: gdprPrivacy.Policy{
-					Signal:  "1",
-					Consent: "anyGDPRConsent",
-				},
-				CCPA: ccpa.Policy{
-					Consent: "1NYN",
-				},
+			expectedPrivacy: macros.UserSyncPrivacy{
+				GDPR:        "1",
+				GDPRConsent: "anyGDPRConsent",
+				USPrivacy:   "1NYN",
 			},
 			expectedRequest: usersync.Request{
 				Bidders: []string{"a", "b"},
@@ -587,6 +577,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				Privacy: usersyncPrivacy{
 					gdprPermissions:  &fakePermissions{},
 					ccpaParsedPolicy: expectedCCPAParsedPolicy,
+					activityRequest:  emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -599,10 +590,11 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			givenBody:        strings.NewReader(`{}`),
 			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
 			givenCCPAEnabled: true,
-			expectedPrivacy:  privacy.Policies{},
+			expectedPrivacy:  macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -621,7 +613,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        true,
@@ -629,6 +621,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -647,7 +640,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        false,
@@ -655,6 +648,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -673,7 +667,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        false,
@@ -681,6 +675,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -699,7 +694,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        false,
@@ -707,6 +702,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -725,7 +721,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        true,
@@ -733,6 +729,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -751,7 +748,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Cooperative: usersync.Cooperative{
 					Enabled:        true,
@@ -759,6 +756,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				},
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -771,10 +769,11 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			givenBody:        strings.NewReader(`{"us_privacy":"invalid"}`),
 			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
 			givenCCPAEnabled: true,
-			expectedPrivacy:  privacy.Policies{},
+			expectedPrivacy:  macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -787,13 +786,13 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			givenBody:        strings.NewReader(`{"us_privacy":"1NYN"}`),
 			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
 			givenCCPAEnabled: false,
-			expectedPrivacy: privacy.Policies{
-				CCPA: ccpa.Policy{
-					Consent: "1NYN"},
+			expectedPrivacy: macros.UserSyncPrivacy{
+				USPrivacy: "1NYN",
 			},
 			expectedRequest: usersync.Request{
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -827,12 +826,13 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			givenBody:        strings.NewReader(`{"gdpr":0}`),
 			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
 			givenCCPAEnabled: true,
-			expectedPrivacy: privacy.Policies{
-				GDPR: gdprPrivacy.Policy{Signal: "0"},
+			expectedPrivacy: macros.UserSyncPrivacy{
+				GDPR: "0",
 			},
 			expectedRequest: usersync.Request{
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -852,12 +852,13 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			givenBody:        strings.NewReader(`{}`),
 			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
 			givenCCPAEnabled: true,
-			expectedPrivacy: privacy.Policies{
-				GDPR: gdprPrivacy.Policy{Signal: ""},
+			expectedPrivacy: macros.UserSyncPrivacy{
+				GDPR: "",
 			},
 			expectedRequest: usersync.Request{
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -893,7 +894,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups: [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Bidders: []string{"a", "b"},
 				Cooperative: usersync.Cooperative{
@@ -903,6 +904,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				Limit: 30,
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -924,7 +926,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Bidders: []string{"a", "b"},
 				Cooperative: usersync.Cooperative{
@@ -934,6 +936,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				Limit: 20,
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -955,7 +958,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 					PriorityGroups:   [][]string{{"a", "b", "c"}},
 				},
 			},
-			expectedPrivacy: privacy.Policies{},
+			expectedPrivacy: macros.UserSyncPrivacy{},
 			expectedRequest: usersync.Request{
 				Bidders: []string{"a", "b"},
 				Cooperative: usersync.Cooperative{
@@ -965,6 +968,7 @@ func TestCookieSyncParseRequest(t *testing.T) {
 				Limit: 20,
 				Privacy: usersyncPrivacy{
 					gdprPermissions: &fakePermissions{},
+					activityRequest: emptyActivityPoliciesRequest,
 				},
 				SyncTypeFilter: usersync.SyncTypeFilter{
 					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
@@ -974,38 +978,6 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			expectedError:        errCookieSyncAccountBlocked.Error(),
 			givenAccountRequired: true,
 		},
-
-		{
-			description: "Account Defaults - Invalid Activities",
-			givenBody: strings.NewReader(`{` +
-				`"bidders":["a", "b"],` +
-				`"account":"ValidAccountInvalidActivities"` +
-				`}`),
-			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
-			givenCCPAEnabled: true,
-			givenConfig: config.UserSync{
-				Cooperative: config.UserSyncCooperative{
-					EnabledByDefault: false,
-					PriorityGroups:   [][]string{{"a", "b", "c"}},
-				},
-			},
-			expectedPrivacy: privacy.Policies{},
-			expectedRequest: usersync.Request{
-				Bidders: []string{"a", "b"},
-				Cooperative: usersync.Cooperative{
-					Enabled:        false,
-					PriorityGroups: [][]string{{"a", "b", "c"}},
-				},
-				Limit: 0,
-				Privacy: usersyncPrivacy{
-					gdprPermissions: &fakePermissions{},
-				},
-				SyncTypeFilter: usersync.SyncTypeFilter{
-					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
-					Redirect: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
-				},
-			},
-		},
 	}
 
 	for _, test := range testCases {
@@ -1593,21 +1565,21 @@ func TestCookieSyncHandleResponse(t *testing.T) {
 		Redirect: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
 	}
 	syncTypeExpected := []usersync.SyncType{usersync.SyncTypeRedirect}
-	privacyPolicies := privacy.Policies{CCPA: ccpa.Policy{Consent: "anyConsent"}}
+	privacyMacros := macros.UserSyncPrivacy{USPrivacy: "anyConsent"}
 
 	// The & in the URL is necessary to test proper JSON encoding.
 	syncA := usersync.Sync{URL: "https://syncA.com/sync?a=1&b=2", Type: usersync.SyncTypeRedirect, SupportCORS: true}
 	syncerA := MockSyncer{}
-	syncerA.On("GetSync", syncTypeExpected, privacyPolicies).Return(syncA, nil).Maybe()
+	syncerA.On("GetSync", syncTypeExpected, privacyMacros).Return(syncA, nil).Maybe()
 
 	// The & in the URL is necessary to test proper JSON encoding.
 	syncB := usersync.Sync{URL: "https://syncB.com/sync?a=1&b=2", Type: usersync.SyncTypeRedirect, SupportCORS: false}
 	syncerB := MockSyncer{}
-	syncerB.On("GetSync", syncTypeExpected, privacyPolicies).Return(syncB, nil).Maybe()
+	syncerB.On("GetSync", syncTypeExpected, privacyMacros).Return(syncB, nil).Maybe()
 
 	syncWithError := usersync.Sync{}
 	syncerWithError := MockSyncer{}
-	syncerWithError.On("GetSync", syncTypeExpected, privacyPolicies).Return(syncWithError, errors.New("anyError")).Maybe()
+	syncerWithError.On("GetSync", syncTypeExpected, privacyMacros).Return(syncWithError, errors.New("anyError")).Maybe()
 
 	testCases := []struct {
 		description         string
@@ -1705,7 +1677,7 @@ func TestCookieSyncHandleResponse(t *testing.T) {
 
 		writer := httptest.NewRecorder()
 		endpoint := cookieSyncEndpoint{pbsAnalytics: &mockAnalytics}
-		endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyPolicies, test.givenSyncersChosen)
+		endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen)
 
 		if assert.Equal(t, writer.Code, http.StatusOK, test.description+":http_status") {
 			assert.Equal(t, writer.Header().Get("Content-Type"), "application/json; charset=utf-8", test.description+":http_header")
@@ -1934,8 +1906,7 @@ func TestCookieSyncActivityControlIntegration(t *testing.T) {
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			activities, err := privacy.NewActivityControl(test.accountPrivacy)
-			assert.NoError(t, err)
+			activities := privacy.NewActivityControl(test.accountPrivacy)
 			up := usersyncPrivacy{
 				activityControl: activities,
 			}
@@ -2021,8 +1992,8 @@ func (m *MockSyncer) SupportsType(syncTypes []usersync.SyncType) bool {
 	return args.Bool(0)
 }
 
-func (m *MockSyncer) GetSync(syncTypes []usersync.SyncType, privacyPolicies privacy.Policies) (usersync.Sync, error) {
-	args := m.Called(syncTypes, privacyPolicies)
+func (m *MockSyncer) GetSync(syncTypes []usersync.SyncType, privacyMacros macros.UserSyncPrivacy) (usersync.Sync, error) {
+	args := m.Called(syncTypes, privacyMacros)
 	return args.Get(0).(usersync.Sync), args.Error(1)
 }
 
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index 3c2528e2..da4522eb 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -65,20 +65,6 @@ func TestVideoEndpointImpressionsNumber(t *testing.T) {
 	assert.Equal(t, resp.AdPods[0].Targeting[0].HbDeal, "ABC_123", "If DealID exists in bid response, hb_deal targeting needs to be added to resp")
 }
 
-func TestVideoEndpointInvalidPrivacyConfig(t *testing.T) {
-	ex := &mockExchangeVideo{}
-	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
-	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
-	recorder := httptest.NewRecorder()
-
-	deps := mockDepsInvalidPrivacy(t, ex)
-	deps.VideoAuctionEndpoint(recorder, req, nil)
-
-	respBytes := recorder.Body.Bytes()
-	expectedErrorMessage := "Critical error while running the video endpoint:  unable to parse component: bidderA.BidderB.bidderC"
-	assert.Equal(t, expectedErrorMessage, string(respBytes), "error message is incorrect")
-}
-
 func TestVideoEndpointImpressionsDuration(t *testing.T) {
 	ex := &mockExchangeVideo{}
 	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_different_durations.json")
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 05030cc7..4f37eb77 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -15,9 +15,9 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/gdpr"
+	"github.com/prebid/prebid-server/macros"
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
-	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/usersync"
 	"github.com/stretchr/testify/assert"
 
@@ -217,8 +217,7 @@ func TestSetUIDEndpoint(t *testing.T) {
 			description:            "Sets uid for a bidder allowed by GDPR in GPP, throws warning because GDPR legacy values weren't used",
 		},
 		{
-			uri: "/setuid?bidder=pubmatic&uid=123&gdpr=1&gdpr_consent=" +
-				"malformed",
+			uri:                    "/setuid?bidder=pubmatic&uid=123&gdpr=1&gdpr_consent=malformed",
 			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
 			gdprAllowsHostCookies:  true,
 			gdprMalformed:          true,
@@ -306,6 +305,26 @@ func TestSetUIDEndpoint(t *testing.T) {
 			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
 			description:            "Set uid for valid bidder with valid account provided with invalid user sync activity",
 		},
+		{
+			description:            "gppsid-valid",
+			uri:                    "/setuid?bidder=appnexus&uid=123&gpp_sid=100,101", // fake sids to avoid GDPR logic in this test
+			syncersBidderNameToKey: map[string]string{"appnexus": "appnexus"},
+			existingSyncs:          nil,
+			gdprAllowsHostCookies:  true,
+			expectedSyncs:          map[string]string{"appnexus": "123"},
+			expectedStatusCode:     http.StatusOK,
+			expectedHeaders:        map[string]string{"Content-Type": "text/html", "Content-Length": "0"},
+		},
+		{
+			description:            "gppsid-malformed",
+			uri:                    "/setuid?bidder=appnexus&uid=123&gpp_sid=malformed",
+			syncersBidderNameToKey: map[string]string{"appnexus": "appnexus"},
+			existingSyncs:          nil,
+			gdprAllowsHostCookies:  true,
+			expectedSyncs:          nil,
+			expectedStatusCode:     http.StatusBadRequest,
+			expectedBody:           "invalid gpp_sid encoding, must be a csv list of integers",
+		},
 	}
 
 	analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{})
@@ -1476,7 +1495,7 @@ func (s fakeSyncer) SupportsType(syncTypes []usersync.SyncType) bool {
 	return true
 }
 
-func (s fakeSyncer) GetSync(syncTypes []usersync.SyncType, privacyPolicies privacy.Policies) (usersync.Sync, error) {
+func (s fakeSyncer) GetSync(syncTypes []usersync.SyncType, privacyMacros macros.UserSyncPrivacy) (usersync.Sync, error) {
 	return usersync.Sync{}, nil
 }
 
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 919f8dce..8ca96880 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -2352,10 +2352,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 		impExtInfoMap[impID] = ImpExtInfo{}
 	}
 
-	activityControl, err := privacy.NewActivityControl(spec.AccountPrivacy)
-	if err != nil {
-		t.Errorf("%s: Exchange returned an unexpected error. Got %s", filename, err.Error())
-	}
+	activityControl := privacy.NewActivityControl(spec.AccountPrivacy)
 
 	auctionRequest := &AuctionRequest{
 		BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &spec.IncomingRequest.OrtbRequest},
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 1ab86fcd..ee6dad1a 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -486,7 +486,7 @@ func TestCleanOpenRTBRequests(t *testing.T) {
 			hostSChainNode:    nil,
 			bidderInfo:        config.BidderInfos{},
 		}
-		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo)
+		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{})
 		if test.hasError {
 			assert.NotNil(t, err, "Error shouldn't be nil")
 		} else {
@@ -552,7 +552,7 @@ func TestCleanOpenRTBRequestsWithFPD(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo)
+		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{})
 		assert.Empty(t, err, "No errors should be returned")
 		for _, bidderRequest := range bidderRequests {
 			bidderName := bidderRequest.BidderName
@@ -867,7 +867,7 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		actualBidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		actualBidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		assert.Empty(t, err, "No errors should be returned")
 		assert.Len(t, actualBidderRequests, len(test.expectedBidderRequests), "result len doesn't match for testCase %s", test.description)
 		for _, actualBidderRequest := range actualBidderRequests {
@@ -1036,7 +1036,7 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		result := bidderRequests[0]
 
 		assert.Nil(t, errs)
@@ -1113,7 +1113,7 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		_, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, gdpr.SignalNo)
+		_, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, gdpr.SignalNo, map[string]float64{})
 
 		assert.ElementsMatch(t, []error{test.expectError}, errs, test.description)
 	}
@@ -1172,7 +1172,7 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		result := bidderRequests[0]
 
 		assert.Nil(t, errs)
@@ -1265,7 +1265,7 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 		if test.hasError == true {
 			assert.NotNil(t, errs)
 			assert.Len(t, bidderRequests, 0)
@@ -1336,7 +1336,7 @@ func TestCleanOpenRTBRequestsBidderParams(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 		if test.hasError == true {
 			assert.NotNil(t, errs)
 			assert.Len(t, bidderRequests, 0)
@@ -1923,7 +1923,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		result := results[0]
 
 		assert.Nil(t, errs)
@@ -2156,7 +2156,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdprDefaultValue)
+		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdprDefaultValue, map[string]float64{})
 		result := results[0]
 
 		if test.expectError {
@@ -2257,7 +2257,7 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 
 		// extract bidder name from each request in the results
 		bidders := []openrtb_ext.BidderName{}
@@ -2344,7 +2344,7 @@ func TestCleanOpenRTBRequestsWithOpenRTBDowngrade(t *testing.T) {
 				hostSChainNode:    nil,
 				bidderInfo:        test.bidderInfos,
 			}
-			bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo)
+			bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{})
 			assert.Nil(t, err, "Err should be nil")
 			bidRequest := bidderRequests[0]
 			assert.Equal(t, test.expectRegs, bidRequest.BidRequest.Regs)
@@ -2605,7 +2605,8 @@ func newBidRequest(t *testing.T) *openrtb2.BidRequest {
 			Geo:      &openrtb2.Geo{Lat: 123.456},
 		},
 		Imp: []openrtb2.Imp{{
-			ID: "some-imp-id",
+			BidFloor: 100,
+			ID:       "some-imp-id",
 			Banner: &openrtb2.Banner{
 				Format: []openrtb2.Format{{
 					W: 300,
@@ -3100,7 +3101,7 @@ func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) {
 		hostSChainNode:    nil,
 		bidderInfo:        config.BidderInfos{},
 	}
-	bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+	bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 
 	assert.Nil(t, errs)
 	assert.Len(t, bidderRequests, 2, "Bid request count is not 2")
@@ -3116,6 +3117,117 @@ func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) {
 	assert.Equal(t, axonixPrebidSchainsSchain, bidRequestSourceExts["axonix"], "Incorrect axonix bid request schain in source.ext")
 }
 
+func TestCleanOpenRTBRequestsBidAdjustment(t *testing.T) {
+	tcf2Consent := "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA"
+	falseValue := false
+	testCases := []struct {
+		description         string
+		gdprAccountEnabled  *bool
+		gdprHostEnabled     bool
+		gdpr                string
+		gdprConsent         string
+		gdprScrub           bool
+		permissionsError    error
+		gdprDefaultValue    string
+		expectPrivacyLabels metrics.PrivacyLabels
+		expectError         bool
+		bidAdjustmentFactor map[string]float64
+		expectedImp         []openrtb2.Imp
+	}{
+		{
+			description:        "BidFloor Adjustment Done for Appnexus",
+			gdprAccountEnabled: &falseValue,
+			gdprHostEnabled:    true,
+			gdpr:               "1",
+			gdprConsent:        tcf2Consent,
+			gdprScrub:          false,
+			gdprDefaultValue:   "1",
+			expectPrivacyLabels: metrics.PrivacyLabels{
+				GDPREnforced:   false,
+				GDPRTCFVersion: "",
+			},
+			bidAdjustmentFactor: map[string]float64{"appnexus": 0.50},
+			expectedImp: []openrtb2.Imp{{
+				BidFloor: 200,
+				ID:       "some-imp-id",
+				Banner: &openrtb2.Banner{
+					Format: []openrtb2.Format{{
+						W: 300,
+						H: 250,
+					}, {
+						W: 300,
+						H: 600,
+					}},
+				},
+				Ext: json.RawMessage(`{"bidder":{"placementId":1}}`),
+			}},
+		},
+		{
+			description:        "bidAjustement Not provided",
+			gdprAccountEnabled: &falseValue,
+			gdprHostEnabled:    true,
+			gdpr:               "1",
+			gdprConsent:        tcf2Consent,
+			gdprScrub:          false,
+			gdprDefaultValue:   "1",
+			expectPrivacyLabels: metrics.PrivacyLabels{
+				GDPREnforced:   false,
+				GDPRTCFVersion: "",
+			},
+			bidAdjustmentFactor: map[string]float64{},
+			expectedImp: []openrtb2.Imp{{
+				BidFloor: 100,
+				ID:       "some-imp-id",
+				Banner: &openrtb2.Banner{
+					Format: []openrtb2.Format{{
+						W: 300,
+						H: 250,
+					}, {
+						W: 300,
+						H: 600,
+					}},
+				},
+				Ext: json.RawMessage(`{"bidder":{"placementId":1}}`),
+			}},
+		},
+	}
+	for _, test := range testCases {
+		req := newBidRequest(t)
+		accountConfig := config.Account{
+			GDPR: config.AccountGDPR{
+				Enabled: &falseValue,
+			},
+			PriceFloors: config.AccountPriceFloors{
+				AdjustForBidAdjustment: true,
+			},
+		}
+		auctionReq := AuctionRequest{
+			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
+			UserSyncs:         &emptyUsersync{},
+			Account:           accountConfig,
+		}
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+				passGeo:         !test.gdprScrub,
+				passID:          !test.gdprScrub,
+				activitiesError: test.permissionsError,
+			},
+		}.Builder
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metrics.MetricsEngineMock{},
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, test.bidAdjustmentFactor)
+		result := results[0]
+		assert.Nil(t, errs)
+		assert.Equal(t, test.expectedImp, result.BidRequest.Imp, test.description)
+	}
+}
+
 func TestApplyFPD(t *testing.T) {
 
 	testCases := []struct {
@@ -3450,7 +3562,7 @@ func TestCleanOpenRTBRequestsFilterBidderRequestExt(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 		assert.Equal(t, test.wantError, len(errs) != 0, test.desc)
 		sort.Slice(bidderRequests, func(i, j int) bool {
 			return bidderRequests[i].BidderCoreName < bidderRequests[j].BidderCoreName
@@ -4364,8 +4476,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		activities, err := privacy.NewActivityControl(&test.privacyConfig)
-		assert.NoError(t, err, "")
+		activities := privacy.NewActivityControl(&test.privacyConfig)
 		auctionReq := AuctionRequest{
 			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req},
 			UserSyncs:         &emptyUsersync{},
@@ -4381,7 +4492,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) {
 		}
 
 		t.Run(test.name, func(t *testing.T) {
-			bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+			bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 			assert.Empty(t, errs)
 			assert.Len(t, bidderRequests, test.expectedReqNumber)
 
@@ -4441,3 +4552,122 @@ func getTransmitTIDActivityConfig(componentName string, allow bool) config.Accou
 		},
 	}
 }
+
+func TestApplyBidAdjustmentToFloor(t *testing.T) {
+	type args struct {
+		allBidderRequests    []BidderRequest
+		bidAdjustmentFactors map[string]float64
+	}
+	tests := []struct {
+		name                      string
+		args                      args
+		expectedAllBidderRequests []BidderRequest
+	}{
+		{
+			name: " bidAdjustmentFactor is empty",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+			},
+		},
+		{
+			name: "bidAdjustmentFactor not present for request bidder",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{"pubmatic": 1.0},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+			},
+		},
+		{
+			name: "bidAdjustmentFactor present for request bidder",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{"pubmatic": 1.0, "appnexus": 0.75},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 133.33333333333334}, {BidFloor: 200}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+			},
+		},
+		{
+			name: "bidAdjustmentFactor present only for appnexus request bidder",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("pubmatic"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{"appnexus": 0.75},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 133.33333333333334}, {BidFloor: 200}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+					},
+					BidderName: openrtb_ext.BidderName("pubmatic"),
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			applyBidAdjustmentToFloor(tt.args.allBidderRequests, tt.args.bidAdjustmentFactors)
+			assert.Equal(t, tt.expectedAllBidderRequests, tt.args.allBidderRequests, tt.name)
+		})
+	}
+}
diff --git a/macros/macros_test.go b/macros/macros_test.go
index 055624b6..db1b8be9 100644
--- a/macros/macros_test.go
+++ b/macros/macros_test.go
@@ -26,7 +26,7 @@ func TestResolveMacros(t *testing.T) {
 		},
 		{
 			givenTemplate:  endpointTemplate,
-			givenParams:    UserSyncTemplateParams{GDPR: "SomeGDPR", GDPRConsent: "SomeGDPRConsent"},
+			givenParams:    UserSyncPrivacy{GDPR: "SomeGDPR", GDPRConsent: "SomeGDPRConsent"},
 			expectedResult: "",
 			expectedError:  true,
 		},
diff --git a/privacy/activitycontrol_test.go b/privacy/activitycontrol_test.go
index db5d588d..743888df 100644
--- a/privacy/activitycontrol_test.go
+++ b/privacy/activitycontrol_test.go
@@ -1,10 +1,10 @@
 package privacy
 
 import (
-	"errors"
 	"testing"
 
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/util/ptrutil"
 	"github.com/stretchr/testify/assert"
 )
@@ -14,136 +14,48 @@ func TestNewActivityControl(t *testing.T) {
 		name            string
 		privacyConf     config.AccountPrivacy
 		activityControl ActivityControl
-		err             error
 	}{
 		{
-			name:            "privacy_config_is_empty",
+			name:            "empty",
 			privacyConf:     config.AccountPrivacy{},
 			activityControl: ActivityControl{plans: nil},
-			err:             nil,
 		},
 		{
-			name: "privacy_config_is_specified_and_correct",
+			name: "specified_and_correct",
 			privacyConf: config.AccountPrivacy{
 				AllowActivities: &config.AllowActivities{
-					SyncUser:                 getDefaultActivityConfig(),
-					FetchBids:                getDefaultActivityConfig(),
-					EnrichUserFPD:            getDefaultActivityConfig(),
-					ReportAnalytics:          getDefaultActivityConfig(),
-					TransmitUserFPD:          getDefaultActivityConfig(),
-					TransmitPreciseGeo:       getDefaultActivityConfig(),
-					TransmitUniqueRequestIds: getDefaultActivityConfig(),
-					TransmitTids:             getDefaultActivityConfig(),
+					SyncUser:                 getTestActivityConfig(true),
+					FetchBids:                getTestActivityConfig(true),
+					EnrichUserFPD:            getTestActivityConfig(true),
+					ReportAnalytics:          getTestActivityConfig(true),
+					TransmitUserFPD:          getTestActivityConfig(true),
+					TransmitPreciseGeo:       getTestActivityConfig(false),
+					TransmitUniqueRequestIds: getTestActivityConfig(true),
+					TransmitTids:             getTestActivityConfig(true),
 				},
 			},
 			activityControl: ActivityControl{plans: map[Activity]ActivityPlan{
-				ActivitySyncUser:                 getDefaultActivityPlan(),
-				ActivityFetchBids:                getDefaultActivityPlan(),
-				ActivityEnrichUserFPD:            getDefaultActivityPlan(),
-				ActivityReportAnalytics:          getDefaultActivityPlan(),
-				ActivityTransmitUserFPD:          getDefaultActivityPlan(),
-				ActivityTransmitPreciseGeo:       getDefaultActivityPlan(),
-				ActivityTransmitUniqueRequestIds: getDefaultActivityPlan(),
-				ActivityTransmitTids:             getDefaultActivityPlan(),
+				ActivitySyncUser:                 getTestActivityPlan(ActivityAllow),
+				ActivityFetchBids:                getTestActivityPlan(ActivityAllow),
+				ActivityEnrichUserFPD:            getTestActivityPlan(ActivityAllow),
+				ActivityReportAnalytics:          getTestActivityPlan(ActivityAllow),
+				ActivityTransmitUserFPD:          getTestActivityPlan(ActivityAllow),
+				ActivityTransmitPreciseGeo:       getTestActivityPlan(ActivityDeny),
+				ActivityTransmitUniqueRequestIDs: getTestActivityPlan(ActivityAllow),
+				ActivityTransmitTIDs:             getTestActivityPlan(ActivityAllow),
 			}},
-			err: nil,
-		},
-		{
-			name: "privacy_config_is_specified_and_SyncUser_is_incorrect",
-			privacyConf: config.AccountPrivacy{
-				AllowActivities: &config.AllowActivities{
-					SyncUser: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_FetchBids_is_incorrect",
-			privacyConf: config.AccountPrivacy{
-				AllowActivities: &config.AllowActivities{
-					FetchBids: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_EnrichUserFPD_is_incorrect",
-			privacyConf: config.AccountPrivacy{
-				AllowActivities: &config.AllowActivities{
-					EnrichUserFPD: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_ReportAnalytics_is_incorrect",
-			privacyConf: config.AccountPrivacy{
-				AllowActivities: &config.AllowActivities{
-					ReportAnalytics: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_TransmitUserFPD_is_incorrect",
-			privacyConf: config.AccountPrivacy{
-				AllowActivities: &config.AllowActivities{
-					TransmitUserFPD: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_TransmitPreciseGeo_is_incorrect",
-			privacyConf: config.AccountPrivacy{
-				AllowActivities: &config.AllowActivities{
-					TransmitPreciseGeo: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_TransmitUniqueRequestIds_is_incorrect",
-			privacyConf: config.AccountPrivacy{
-				AllowActivities: &config.AllowActivities{
-					TransmitUniqueRequestIds: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_TransmitTids_is_incorrect",
-			privacyConf: config.AccountPrivacy{
-				AllowActivities: &config.AllowActivities{
-					TransmitTids: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
 		},
 	}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualAC, actualErr := NewActivityControl(&test.privacyConf)
-			if test.err == nil {
-				assert.Equal(t, test.activityControl, actualAC)
-				assert.NoError(t, actualErr)
-			} else {
-				assert.EqualError(t, actualErr, test.err.Error())
-			}
+			actualAC := NewActivityControl(&test.privacyConf)
+			assert.Equal(t, test.activityControl, actualAC)
 		})
 	}
 }
 
-func TestActivityDefaultToDefaultResult(t *testing.T) {
+func TestCfgToDefaultResult(t *testing.T) {
 	testCases := []struct {
 		name            string
 		activityDefault *bool
@@ -168,14 +80,13 @@ func TestActivityDefaultToDefaultResult(t *testing.T) {
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualResult := activityDefaultToDefaultResult(test.activityDefault)
+			actualResult := cfgToDefaultResult(test.activityDefault)
 			assert.Equal(t, test.expectedResult, actualResult)
 		})
 	}
 }
 
-func TestAllowActivityControl(t *testing.T) {
-
+func TestActivityControlAllow(t *testing.T) {
 	testCases := []struct {
 		name            string
 		activityControl ActivityControl
@@ -193,7 +104,7 @@ func TestAllowActivityControl(t *testing.T) {
 		{
 			name: "activity_not_defined",
 			activityControl: ActivityControl{plans: map[Activity]ActivityPlan{
-				ActivitySyncUser: getDefaultActivityPlan()}},
+				ActivitySyncUser: getTestActivityPlan(ActivityAllow)}},
 			activity:       ActivityFetchBids,
 			target:         Component{Type: "bidder", Name: "bidderA"},
 			activityResult: true,
@@ -201,7 +112,7 @@ func TestAllowActivityControl(t *testing.T) {
 		{
 			name: "activity_defined_but_not_found_default_returned",
 			activityControl: ActivityControl{plans: map[Activity]ActivityPlan{
-				ActivityFetchBids: getDefaultActivityPlan()}},
+				ActivityFetchBids: getTestActivityPlan(ActivityAllow)}},
 			activity:       ActivityFetchBids,
 			target:         Component{Type: "bidder", Name: "bidderB"},
 			activityResult: true,
@@ -209,7 +120,7 @@ func TestAllowActivityControl(t *testing.T) {
 		{
 			name: "activity_defined_and_allowed",
 			activityControl: ActivityControl{plans: map[Activity]ActivityPlan{
-				ActivityFetchBids: getDefaultActivityPlan()}},
+				ActivityFetchBids: getTestActivityPlan(ActivityAllow)}},
 			activity:       ActivityFetchBids,
 			target:         Component{Type: "bidder", Name: "bidderA"},
 			activityResult: true,
@@ -218,20 +129,39 @@ func TestAllowActivityControl(t *testing.T) {
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualResult := test.activityControl.Allow(test.activity, test.target)
+			actualResult := test.activityControl.Allow(test.activity, test.target, ActivityRequest{})
 			assert.Equal(t, test.activityResult, actualResult)
 
 		})
 	}
 }
 
-// constants
-func getDefaultActivityConfig() config.Activity {
+func TestActivityRequest(t *testing.T) {
+	t.Run("empty", func(t *testing.T) {
+		r := ActivityRequest{}
+		assert.False(t, r.IsPolicies())
+		assert.False(t, r.IsBidRequest())
+	})
+
+	t.Run("policies", func(t *testing.T) {
+		r := NewRequestFromPolicies(Policies{})
+		assert.True(t, r.IsPolicies())
+		assert.False(t, r.IsBidRequest())
+	})
+
+	t.Run("request", func(t *testing.T) {
+		r := NewRequestFromBidRequest(openrtb_ext.RequestWrapper{})
+		assert.False(t, r.IsPolicies())
+		assert.True(t, r.IsBidRequest())
+	})
+}
+
+func getTestActivityConfig(allow bool) config.Activity {
 	return config.Activity{
 		Default: ptrutil.ToPtr(true),
 		Rules: []config.ActivityRule{
 			{
-				Allow: true,
+				Allow: allow,
 				Condition: config.ActivityCondition{
 					ComponentName: []string{"bidderA"},
 					ComponentType: []string{"bidder"},
@@ -241,32 +171,15 @@ func getDefaultActivityConfig() config.Activity {
 	}
 }
 
-func getDefaultActivityPlan() ActivityPlan {
+func getTestActivityPlan(result ActivityResult) ActivityPlan {
 	return ActivityPlan{
 		defaultResult: true,
 		rules: []Rule{
-			ComponentEnforcementRule{
-				result: ActivityAllow,
-				componentName: []Component{
-					{Type: "bidder", Name: "bidderA"},
-				},
+			ConditionRule{
+				result:        result,
+				componentName: []string{"bidderA"},
 				componentType: []string{"bidder"},
 			},
 		},
 	}
 }
-
-func getIncorrectActivityConfig() config.Activity {
-	return config.Activity{
-		Default: ptrutil.ToPtr(true),
-		Rules: []config.ActivityRule{
-			{
-				Allow: true,
-				Condition: config.ActivityCondition{
-					ComponentName: []string{"bidder.bidderA.bidderB"},
-					ComponentType: []string{"bidder"},
-				},
-			},
-		},
-	}
-}
diff --git a/privacy/component_test.go b/privacy/component_test.go
index 38aa105d..9d9d1644 100644
--- a/privacy/component_test.go
+++ b/privacy/component_test.go
@@ -1,124 +1,87 @@
 package privacy
 
 import (
-	"errors"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 )
 
-func TestComponentMatches(t *testing.T) {
+func TestComponentMatchesName(t *testing.T) {
 	testCases := []struct {
 		name   string
 		given  Component
-		target Component
+		target string
 		result bool
 	}{
 		{
-			name:   "full",
+			name:   "match",
 			given:  Component{Type: "a", Name: "b"},
-			target: Component{Type: "a", Name: "b"},
+			target: "b",
 			result: true,
 		},
 		{
-			name:   "name-wildcard",
-			given:  Component{Type: "a", Name: "*"},
-			target: Component{Type: "a", Name: "b"},
-			result: true,
-		},
-		{
-			name:   "different",
+			name:   "wrong-field",
 			given:  Component{Type: "a", Name: "b"},
-			target: Component{Type: "foo", Name: "bar"},
+			target: "a",
 			result: false,
 		},
 		{
-			name:   "different-type",
+			name:   "different-value",
 			given:  Component{Type: "a", Name: "b"},
-			target: Component{Type: "foo", Name: "b"},
+			target: "foo",
 			result: false,
 		},
 		{
-			name:   "different-name",
+			name:   "different-case",
 			given:  Component{Type: "a", Name: "b"},
-			target: Component{Type: "a", Name: "foo"},
-			result: false,
+			target: "B",
+			result: true,
 		},
 	}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			assert.Equal(t, test.result, test.given.Matches(test.target))
+			assert.Equal(t, test.result, test.given.MatchesName(test.target))
 		})
 	}
 }
 
-func TestParseComponent(t *testing.T) {
+func TestComponentMatchesType(t *testing.T) {
 	testCases := []struct {
-		name          string
-		component     string
-		expected      Component
-		expectedError error
+		name   string
+		given  Component
+		target string
+		result bool
 	}{
 		{
-			name:          "empty",
-			component:     "",
-			expected:      Component{},
-			expectedError: errors.New("unable to parse empty component"),
-		},
-		{
-			name:          "too-many-parts",
-			component:     "bidder.bidderA.bidderB",
-			expected:      Component{},
-			expectedError: errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name:          "type-bidder",
-			component:     "bidder.bidderA",
-			expected:      Component{Type: "bidder", Name: "bidderA"},
-			expectedError: nil,
-		},
-		{
-			name:          "type-analytics",
-			component:     "analytics.bidderA",
-			expected:      Component{Type: "analytics", Name: "bidderA"},
-			expectedError: nil,
-		},
-		{
-			name:          "type-default",
-			component:     "bidderA",
-			expected:      Component{Type: "bidder", Name: "bidderA"},
-			expectedError: nil,
+			name:   "match",
+			given:  Component{Type: "a", Name: "b"},
+			target: "a",
+			result: true,
 		},
 		{
-			name:          "type-rtd",
-			component:     "rtd.test",
-			expected:      Component{Type: "rtd", Name: "test"},
-			expectedError: nil,
+			name:   "wrong-field",
+			given:  Component{Type: "a", Name: "b"},
+			target: "b",
+			result: false,
 		},
 		{
-			name:          "type-general",
-			component:     "general.test",
-			expected:      Component{Type: "general", Name: "test"},
-			expectedError: nil,
+			name:   "different-value",
+			given:  Component{Type: "a", Name: "b"},
+			target: "foo",
+			result: false,
 		},
 		{
-			name:          "type-invalid",
-			component:     "invalid.test",
-			expected:      Component{},
-			expectedError: errors.New("unable to parse component (invalid type): invalid.test"),
+			name:   "different-case",
+			given:  Component{Type: "a", Name: "b"},
+			target: "A",
+			result: true,
 		},
 	}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualSN, actualErr := ParseComponent(test.component)
-			if test.expectedError == nil {
-				assert.Equal(t, test.expected, actualSN)
-				assert.NoError(t, actualErr)
-			} else {
-				assert.EqualError(t, actualErr, test.expectedError.Error())
-			}
+			assert.Equal(t, test.result, test.given.MatchesType(test.target))
 		})
 	}
 }
diff --git a/privacy/rule_condition_test.go b/privacy/rule_condition_test.go
new file mode 100644
index 00000000..bb1d81c0
--- /dev/null
+++ b/privacy/rule_condition_test.go
@@ -0,0 +1,278 @@
+package privacy
+
+import (
+	"testing"
+
+	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestComponentEnforcementRuleEvaluate(t *testing.T) {
+	testCases := []struct {
+		name           string
+		componentRule  ConditionRule
+		target         Component
+		activityResult ActivityResult
+	}{
+		{
+			name: "activity_is_allowed",
+			componentRule: ConditionRule{
+				result:        ActivityAllow,
+				componentName: []string{"bidderA"},
+				componentType: []string{"bidder"},
+			},
+			target:         Component{Type: "bidder", Name: "bidderA"},
+			activityResult: ActivityAllow,
+		},
+		{
+			name: "activity_is_not_allowed",
+			componentRule: ConditionRule{
+				result:        ActivityDeny,
+				componentName: []string{"bidderA"},
+				componentType: []string{"bidder"},
+			},
+			target:         Component{Type: "bidder", Name: "bidderA"},
+			activityResult: ActivityDeny,
+		},
+		{
+			name: "abstain_both_clauses_do_not_match",
+			componentRule: ConditionRule{
+				result:        ActivityAllow,
+				componentName: []string{"bidderA"},
+				componentType: []string{"bidder"},
+			},
+			target:         Component{Type: "bidder", Name: "bidderB"},
+			activityResult: ActivityAbstain,
+		},
+		{
+			name: "abstain_gppsid",
+			componentRule: ConditionRule{
+				result: ActivityAllow,
+				gppSID: []int8{1},
+			},
+			target:         Component{Type: "bidder", Name: "bidderB"},
+			activityResult: ActivityAbstain,
+		},
+		{
+			name: "activity_is_not_allowed_componentName_only",
+			componentRule: ConditionRule{
+				result:        ActivityAllow,
+				componentName: []string{"bidderA"},
+			},
+			target:         Component{Type: "bidder", Name: "bidderA"},
+			activityResult: ActivityAllow,
+		},
+		{
+			name: "activity_is_allowed_componentType_only",
+			componentRule: ConditionRule{
+				result:        ActivityAllow,
+				componentType: []string{"bidder"},
+			},
+			target:         Component{Type: "bidder", Name: "bidderB"},
+			activityResult: ActivityAllow,
+		},
+		{
+			name: "no-conditions-allow",
+			componentRule: ConditionRule{
+				result: ActivityAllow,
+			},
+			target:         Component{Type: "bidder", Name: "bidderA"},
+			activityResult: ActivityAllow,
+		},
+		{
+			name: "no-conditions-deny",
+			componentRule: ConditionRule{
+				result: ActivityDeny,
+			},
+			target:         Component{Type: "bidder", Name: "bidderA"},
+			activityResult: ActivityDeny,
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			actualResult := test.componentRule.Evaluate(test.target, ActivityRequest{})
+			assert.Equal(t, test.activityResult, actualResult)
+		})
+	}
+}
+
+func TestEvaluateGPPSID(t *testing.T) {
+	testCases := []struct {
+		name         string
+		sidCondition []int8
+		sidRequest   []int8
+		expected     bool
+	}{
+		{
+			name:         "condition-nil-request-nil",
+			sidCondition: nil,
+			sidRequest:   nil,
+			expected:     true,
+		},
+		{
+			name:         "condition-empty-request-nil",
+			sidCondition: []int8{},
+			sidRequest:   nil,
+			expected:     true,
+		},
+		{
+			name:         "condition-nil-request-empty",
+			sidCondition: nil,
+			sidRequest:   []int8{},
+			expected:     true,
+		},
+		{
+			name:         "condition-empty-request-empty",
+			sidCondition: []int8{},
+			sidRequest:   []int8{},
+			expected:     true,
+		},
+		{
+			name:         "condition-one-request-nil",
+			sidCondition: []int8{1},
+			sidRequest:   nil,
+			expected:     false,
+		},
+		{
+			name:         "condition-many-request-nil",
+			sidCondition: []int8{1, 2},
+			sidRequest:   nil,
+			expected:     false,
+		},
+		{
+			name:         "condition-one-request-empty",
+			sidCondition: []int8{1},
+			sidRequest:   []int8{},
+			expected:     false,
+		},
+		{
+			name:         "condition-many-request-empty",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{},
+			expected:     false,
+		},
+		{
+			name:         "condition-one-request-one-match",
+			sidCondition: []int8{1},
+			sidRequest:   []int8{1},
+			expected:     true,
+		},
+		{
+			name:         "condition-one-request-one-nomatch",
+			sidCondition: []int8{1},
+			sidRequest:   []int8{2},
+			expected:     false,
+		},
+		{
+			name:         "condition-one-request-many-match",
+			sidCondition: []int8{1},
+			sidRequest:   []int8{1, 2},
+			expected:     true,
+		},
+		{
+			name:         "condition-one-request-many-nomatch",
+			sidCondition: []int8{3},
+			sidRequest:   []int8{1, 2},
+			expected:     false,
+		},
+		{
+			name:         "condition-nil-request-one",
+			sidCondition: nil,
+			sidRequest:   []int8{1},
+			expected:     true,
+		},
+		{
+			name:         "condition-nil-request-many",
+			sidCondition: nil,
+			sidRequest:   []int8{1, 2},
+			expected:     true,
+		},
+		{
+			name:         "condition-empty-request-one",
+			sidCondition: []int8{},
+			sidRequest:   []int8{1},
+			expected:     true,
+		},
+		{
+			name:         "condition-empty-request-many",
+			sidCondition: []int8{},
+			sidRequest:   []int8{1, 2},
+			expected:     true,
+		},
+		{
+			name:         "condition-many-request-one-match",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{1},
+			expected:     true,
+		},
+		{
+			name:         "condition-many-request-one-nomatch",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{3},
+			expected:     false,
+		},
+		{
+			name:         "condition-many-request-many-match",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{1, 2},
+			expected:     true,
+		},
+		{
+			name:         "condition-many-request-many-nomatch",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{3, 4},
+			expected:     false,
+		},
+		{
+			name:         "condition-many-request-many-mixed",
+			sidCondition: []int8{1, 2},
+			sidRequest:   []int8{2, 3},
+			expected:     true,
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			actualResult := evaluateGPPSID(test.sidCondition, NewRequestFromPolicies(Policies{GPPSID: test.sidRequest}))
+			assert.Equal(t, test.expected, actualResult)
+		})
+	}
+}
+
+func TestGetGPPSID(t *testing.T) {
+	testCases := []struct {
+		name     string
+		request  ActivityRequest
+		expected []int8
+	}{
+		{
+			name:     "empty",
+			request:  ActivityRequest{},
+			expected: nil,
+		},
+		{
+			name:     "policies",
+			request:  ActivityRequest{policies: &Policies{GPPSID: []int8{1}}},
+			expected: []int8{1},
+		},
+		{
+			name:     "request-regs",
+			request:  ActivityRequest{bidRequest: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Regs: &openrtb2.Regs{GPPSID: []int8{1}}}}},
+			expected: []int8{1},
+		},
+		{
+			name:     "request-regs-nil",
+			request:  ActivityRequest{bidRequest: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Regs: nil}}},
+			expected: nil,
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			actualResult := getGPPSID(test.request)
+			assert.Equal(t, test.expected, actualResult)
+		})
+	}
+}
diff --git a/privacy/rules_test.go b/privacy/rules_test.go
deleted file mode 100644
index 7e80c9ce..00000000
--- a/privacy/rules_test.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package privacy
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestComponentEnforcementRuleEvaluate(t *testing.T) {
-	testCases := []struct {
-		name           string
-		componentRule  ComponentEnforcementRule
-		target         Component
-		activityResult ActivityResult
-	}{
-		{
-			name: "activity_is_allowed",
-			componentRule: ComponentEnforcementRule{
-				result: ActivityAllow,
-				componentName: []Component{
-					{Type: "bidder", Name: "bidderA"},
-				},
-				componentType: []string{"bidder"},
-			},
-			target:         Component{Type: "bidder", Name: "bidderA"},
-			activityResult: ActivityAllow,
-		},
-		{
-			name: "activity_is_not_allowed",
-			componentRule: ComponentEnforcementRule{
-				result: ActivityDeny,
-				componentName: []Component{
-					{Type: "bidder", Name: "bidderA"},
-				},
-				componentType: []string{"bidder"},
-			},
-			target:         Component{Type: "bidder", Name: "bidderA"},
-			activityResult: ActivityDeny,
-		},
-		{
-			name: "abstain_both_clauses_do_not_match",
-			componentRule: ComponentEnforcementRule{
-				result: ActivityAllow,
-				componentName: []Component{
-					{Type: "bidder", Name: "bidderA"},
-				},
-				componentType: []string{"bidder"},
-			},
-			target:         Component{Type: "bidder", Name: "bidderB"},
-			activityResult: ActivityAbstain,
-		},
-		{
-			name: "activity_is_not_allowed_componentName_only",
-			componentRule: ComponentEnforcementRule{
-				result: ActivityAllow,
-				componentName: []Component{
-					{Type: "bidder", Name: "bidderA"},
-				},
-			},
-			target:         Component{Type: "bidder", Name: "bidderA"},
-			activityResult: ActivityAllow,
-		},
-		{
-			name: "activity_is_allowed_componentType_only",
-			componentRule: ComponentEnforcementRule{
-				result:        ActivityAllow,
-				componentType: []string{"bidder"},
-			},
-			target:         Component{Type: "bidder", Name: "bidderB"},
-			activityResult: ActivityAllow,
-		},
-		{
-			name: "no-conditions-allow",
-			componentRule: ComponentEnforcementRule{
-				result: ActivityAllow,
-			},
-			target:         Component{Type: "bidder", Name: "bidderA"},
-			activityResult: ActivityAllow,
-		},
-		{
-			name: "no-conditions-deny",
-			componentRule: ComponentEnforcementRule{
-				result: ActivityDeny,
-			},
-			target:         Component{Type: "bidder", Name: "bidderA"},
-			activityResult: ActivityDeny,
-		},
-	}
-
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			actualResult := test.componentRule.Evaluate(test.target)
-			assert.Equal(t, test.activityResult, actualResult)
-
-		})
-	}
-}
diff --git a/usersync/chooser_test.go b/usersync/chooser_test.go
index adc217ae..4f38002c 100644
--- a/usersync/chooser_test.go
+++ b/usersync/chooser_test.go
@@ -1,11 +1,12 @@
 package usersync
 
 import (
-	"github.com/prebid/prebid-server/privacy"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/mock"
 	"testing"
 	"time"
+
+	"github.com/prebid/prebid-server/macros"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 )
 
 func TestNewChooser(t *testing.T) {
@@ -376,7 +377,7 @@ func (s fakeSyncer) SupportsType(syncTypes []SyncType) bool {
 	return false
 }
 
-func (fakeSyncer) GetSync(syncTypes []SyncType, privacyPolicies privacy.Policies) (Sync, error) {
+func (fakeSyncer) GetSync([]SyncType, macros.UserSyncPrivacy) (Sync, error) {
 	return Sync{}, nil
 }
 
diff --git a/usersync/syncer_test.go b/usersync/syncer_test.go
index c309b899..72167add 100644
--- a/usersync/syncer_test.go
+++ b/usersync/syncer_test.go
@@ -6,9 +6,6 @@ import (
 
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/macros"
-	"github.com/prebid/prebid-server/privacy"
-	"github.com/prebid/prebid-server/privacy/ccpa"
-	"github.com/prebid/prebid-server/privacy/gdpr"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -16,7 +13,7 @@ func TestNewSyncer(t *testing.T) {
 	var (
 		supportCORS      = true
 		hostConfig       = config.UserSync{ExternalURL: "http://host.com", RedirectURL: "{{.ExternalURL}}/host"}
-		macroValues      = macros.UserSyncTemplateParams{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"}
+		macroValues      = macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"}
 		iframeConfig     = &config.SyncerEndpoint{URL: "https://bidder.com/iframe?redirect={{.RedirectURL}}"}
 		redirectConfig   = &config.SyncerEndpoint{URL: "https://bidder.com/redirect?redirect={{.RedirectURL}}"}
 		errParseConfig   = &config.SyncerEndpoint{URL: "{{malformed}}"}
@@ -186,7 +183,7 @@ func TestBuildTemplate(t *testing.T) {
 		key           = "anyKey"
 		syncTypeValue = "x"
 		hostConfig    = config.UserSync{ExternalURL: "http://host.com", RedirectURL: "{{.ExternalURL}}/host"}
-		macroValues   = macros.UserSyncTemplateParams{GDPR: "A", GDPRConsent: "B", USPrivacy: "C", GPP: "D", GPPSID: "1"}
+		macroValues   = macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C", GPP: "D", GPPSID: "1"}
 	)
 
 	testCases := []struct {
@@ -451,7 +448,7 @@ func TestValidateTemplate(t *testing.T) {
 		{
 			description:   "Contains Unrecognized Macro",
 			given:         template.Must(template.New("test").Parse("invalid:{{.DoesNotExist}}")),
-			expectedError: "template: test:1:10: executing \"test\" at <.DoesNotExist>: can't evaluate field DoesNotExist in type macros.UserSyncTemplateParams",
+			expectedError: "template: test:1:10: executing \"test\" at <.DoesNotExist>: can't evaluate field DoesNotExist in type macros.UserSyncPrivacy",
 		},
 		{
 			description:   "Not A Url",
@@ -636,45 +633,45 @@ func TestSyncerGetSync(t *testing.T) {
 	)
 
 	testCases := []struct {
-		description          string
-		givenSyncer          standardSyncer
-		givenSyncTypes       []SyncType
-		givenPrivacyPolicies privacy.Policies
-		expectedError        string
-		expectedSync         Sync
+		description    string
+		givenSyncer    standardSyncer
+		givenSyncTypes []SyncType
+		givenMacros    macros.UserSyncPrivacy
+		expectedError  string
+		expectedSync   Sync
 	}{
 		{
-			description:          "No Sync Types",
-			givenSyncer:          standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
-			givenSyncTypes:       []SyncType{},
-			givenPrivacyPolicies: privacy.Policies{GDPR: gdpr.Policy{Signal: "A", Consent: "B"}, CCPA: ccpa.Policy{Consent: "C"}},
-			expectedError:        "no sync types provided",
+			description:    "No Sync Types",
+			givenSyncer:    standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
+			givenSyncTypes: []SyncType{},
+			givenMacros:    macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"},
+			expectedError:  "no sync types provided",
 		},
 		{
-			description:          "IFrame",
-			givenSyncer:          standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
-			givenSyncTypes:       []SyncType{SyncTypeIFrame},
-			givenPrivacyPolicies: privacy.Policies{GDPR: gdpr.Policy{Signal: "A", Consent: "B"}, CCPA: ccpa.Policy{Consent: "C"}},
-			expectedSync:         Sync{URL: "iframe,gdpr:A,gdprconsent:B,ccpa:C", Type: SyncTypeIFrame, SupportCORS: false},
+			description:    "IFrame",
+			givenSyncer:    standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
+			givenSyncTypes: []SyncType{SyncTypeIFrame},
+			givenMacros:    macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"},
+			expectedSync:   Sync{URL: "iframe,gdpr:A,gdprconsent:B,ccpa:C", Type: SyncTypeIFrame, SupportCORS: false},
 		},
 		{
-			description:          "Redirect",
-			givenSyncer:          standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
-			givenSyncTypes:       []SyncType{SyncTypeRedirect},
-			givenPrivacyPolicies: privacy.Policies{GDPR: gdpr.Policy{Signal: "A", Consent: "B"}, CCPA: ccpa.Policy{Consent: "C"}},
-			expectedSync:         Sync{URL: "redirect,gdpr:A,gdprconsent:B,ccpa:C", Type: SyncTypeRedirect, SupportCORS: false},
+			description:    "Redirect",
+			givenSyncer:    standardSyncer{iframe: iframeTemplate, redirect: redirectTemplate},
+			givenSyncTypes: []SyncType{SyncTypeRedirect},
+			givenMacros:    macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"},
+			expectedSync:   Sync{URL: "redirect,gdpr:A,gdprconsent:B,ccpa:C", Type: SyncTypeRedirect, SupportCORS: false},
 		},
 		{
-			description:          "Macro Error",
-			givenSyncer:          standardSyncer{iframe: malformedTemplate},
-			givenSyncTypes:       []SyncType{SyncTypeIFrame},
-			givenPrivacyPolicies: privacy.Policies{GDPR: gdpr.Policy{Signal: "A", Consent: "B"}, CCPA: ccpa.Policy{Consent: "C"}},
-			expectedError:        "template: test:1:20: executing \"test\" at <.DoesNotExist>: can't evaluate field DoesNotExist in type macros.UserSyncTemplateParams",
+			description:    "Macro Error",
+			givenSyncer:    standardSyncer{iframe: malformedTemplate},
+			givenSyncTypes: []SyncType{SyncTypeIFrame},
+			givenMacros:    macros.UserSyncPrivacy{GDPR: "A", GDPRConsent: "B", USPrivacy: "C"},
+			expectedError:  "template: test:1:20: executing \"test\" at <.DoesNotExist>: can't evaluate field DoesNotExist in type macros.UserSyncPrivacy",
 		},
 	}
 
 	for _, test := range testCases {
-		result, err := test.givenSyncer.GetSync(test.givenSyncTypes, test.givenPrivacyPolicies)
+		result, err := test.givenSyncer.GetSync(test.givenSyncTypes, test.givenMacros)
 
 		if test.expectedError == "" {
 			assert.NoError(t, err, test.description+":err")
diff --git a/usersync/syncersbuilder_test.go b/usersync/syncersbuilder_test.go
index fbc18639..15c53dba 100644
--- a/usersync/syncersbuilder_test.go
+++ b/usersync/syncersbuilder_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 
 	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/privacy"
+	"github.com/prebid/prebid-server/macros"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -160,7 +160,7 @@ func TestBuildSyncers(t *testing.T) {
 			assert.Empty(t, errs, test.description+":err")
 			resultRenderedIFrameURLS := map[string]string{}
 			for k, v := range result {
-				iframeRendered, err := v.GetSync([]SyncType{SyncTypeIFrame}, privacy.Policies{})
+				iframeRendered, err := v.GetSync([]SyncType{SyncTypeIFrame}, macros.UserSyncPrivacy{})
 				if assert.NoError(t, err, test.description+"key:%s,:iframe_render", k) {
 					resultRenderedIFrameURLS[k] = iframeRendered.URL
 				}