Repository: prebid/prebid-server
Pull request #2281 (2022-06-28 02:43:54)

pubmatic adapter: add param prebiddealpriority
- allow new param `prebiddealpriority`
- minor refactor

pm-nilesh-chate: Hi @SyntaxNode, I'm done with the code changes. Could you please review the PR.

Thanks :)

Patch:
diff --git a/.github/workflows/issue_prioritization.yml b/.github/workflows/issue_prioritization.yml
new file mode 100644
index 00000000..9a178577
--- /dev/null
+++ b/.github/workflows/issue_prioritization.yml
@@ -0,0 +1,90 @@
+name: Issue tracking
+on:
+  issues:
+    types:
+      - opened
+      - labeled
+jobs:
+  track_issue:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Generate token
+        id: generate_token
+        uses: tibdex/github-app-token@36464acb844fc53b9b8b2401da68844f6b05ebb0
+        with:
+          app_id: ${{ secrets.PBS_PROJECT_APP_ID }}
+          private_key: ${{ secrets.PBS_PROJECT_APP_PEM }}
+
+      - name: Get project data
+        env:
+          GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
+          ORGANIZATION: prebid
+          DATE_FIELD: Created on
+          PROJECT_NUMBER: 4
+        run: |
+          gh api graphql -f query='
+            query($org: String!, $number: Int!) {
+              organization(login: $org){
+                projectNext(number: $number) {
+                  id
+                  fields(first:100) {
+                    nodes {
+                      id
+                      name
+                      settings
+                    }
+                  }
+                }
+              }
+            }' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
+
+          echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
+          echo 'DATE_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "'"$DATE_FIELD"'") | .id' project_data.json) >> $GITHUB_ENV
+
+      - name: Add issue to project
+        env:
+          GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
+          ISSUE_ID: ${{ github.event.issue.node_id }}
+        run: |
+          gh api graphql -f query='
+            mutation($project:ID!, $issue:ID!) {
+              addProjectNextItem(input: {projectId: $project, contentId: $issue}) {
+                projectNextItem {
+                  id,
+                  content {
+                     ... on Issue {
+                        createdAt
+                     }
+                     ... on PullRequest {
+                        createdAt
+                     }                     
+                  }
+                }
+              }
+            }' -f project=$PROJECT_ID -f issue=$ISSUE_ID > issue_data.json
+          
+          echo 'ITEM_ID='$(jq '.data.addProjectNextItem.projectNextItem.id' issue_data.json) >> $GITHUB_ENV
+          echo 'ITEM_CREATION_DATE='$(jq '.data.addProjectNextItem.projectNextItem.content.createdAt' issue_data.json) >> $GITHUB_ENV
+
+      - name: Set fields
+        env:
+          GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
+        run: |
+          gh api graphql -f query='
+            mutation (
+              $project: ID!
+              $item: ID!
+              $date_field: ID!
+              $date_value: String!
+            ) {
+              set_creation_date: updateProjectNextItemField(input: {
+                projectId: $project
+                itemId: $item
+                fieldId: $date_field
+                value: $date_value
+              }) {
+                projectNextItem {
+                  id
+                }
+              }
+            }' -f project=$PROJECT_ID -f item=$ITEM_ID -f date_field=$DATE_FIELD_ID -f date_value=$ITEM_CREATION_DATE --silent
diff --git a/account/account.go b/account/account.go
index 45506e93..64c7ee3e 100644
--- a/account/account.go
+++ b/account/account.go
@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"fmt"
 
+	"github.com/buger/jsonparser"
 	"github.com/prebid/go-gdpr/consentconstants"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
@@ -51,7 +52,25 @@ func GetAccount(ctx context.Context, cfg *config.Configuration, fetcher stored_r
 		completeJSON, err := jsonpatch.MergePatch(cfg.AccountDefaultsJSON(), accountJSON)
 		if err == nil {
 			err = json.Unmarshal(completeJSON, account)
+
+			// this logic exists for backwards compatibility. If the initial unmarshal fails above, we attempt to
+			// resolve it by converting the GDPR enforce purpose fields and then attempting an unmarshal again before
+			// declaring a malformed account error.
+			// unmarshal fetched account to determine if it is well-formed
+			if _, ok := err.(*json.UnmarshalTypeError); ok {
+				// attempt to convert deprecated GDPR enforce purpose fields to resolve issue
+				completeJSON, err = ConvertGDPREnforcePurposeFields(completeJSON)
+				// unmarshal again to check if unmarshal error still exists after GDPR field conversion
+				err = json.Unmarshal(completeJSON, account)
+
+				if _, ok := err.(*json.UnmarshalTypeError); ok {
+					return nil, []error{&errortypes.MalformedAcct{
+						Message: fmt.Sprintf("The prebid-server account config for account id \"%s\" is malformed. Please reach out to the prebid server host.", accountID),
+					}}
+				}
+			}
 		}
+
 		if err != nil {
 			errs = append(errs, err)
 			return nil, errs
@@ -120,3 +139,79 @@ func setDerivedConfig(account *config.Account) {
 		}
 	}
 }
+
+// PatchAccount represents the GDPR portion of a publisher account configuration that can be mutated
+// for backwards compatibility reasons
+type PatchAccount struct {
+	GDPR map[string]*PatchAccountGDPRPurpose `json:"gdpr"`
+}
+
+// PatchAccountGDPRPurpose represents account-specific GDPR purpose configuration data that can be mutated
+// for backwards compatibility reasons
+type PatchAccountGDPRPurpose struct {
+	EnforceAlgo    string `json:"enforce_algo,omitempty"`
+	EnforcePurpose *bool  `json:"enforce_purpose,omitempty"`
+}
+
+// ConvertGDPREnforcePurposeFields is responsible for ensuring account GDPR config backwards compatibility
+// given the recent type change of gdpr.purpose{1-10}.enforce_purpose from a string to a bool. This function
+// iterates over each GDPR purpose config and sets enforce_purpose and enforce_algo to the appropriate
+// bool and string values respectively if enforce_purpose is a string and enforce_algo is not set
+func ConvertGDPREnforcePurposeFields(config []byte) (newConfig []byte, err error) {
+	gdprJSON, _, _, err := jsonparser.Get(config, "gdpr")
+	if err != nil && err == jsonparser.KeyPathNotFoundError {
+		return config, nil
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	newAccount := PatchAccount{
+		GDPR: map[string]*PatchAccountGDPRPurpose{},
+	}
+
+	for i := 1; i <= 10; i++ {
+		purposeName := fmt.Sprintf("purpose%d", i)
+
+		enforcePurpose, purposeDataType, _, err := jsonparser.Get(gdprJSON, purposeName, "enforce_purpose")
+		if err != nil && err == jsonparser.KeyPathNotFoundError {
+			continue
+		}
+		if err != nil {
+			return nil, err
+		}
+		if purposeDataType != jsonparser.String {
+			continue
+		}
+
+		_, _, _, err = jsonparser.Get(gdprJSON, purposeName, "enforce_algo")
+		if err != nil && err != jsonparser.KeyPathNotFoundError {
+			return nil, err
+		}
+		if err == nil {
+			continue
+		}
+
+		newEnforcePurpose := false
+		if string(enforcePurpose) == "full" {
+			newEnforcePurpose = true
+		}
+
+		newAccount.GDPR[purposeName] = &PatchAccountGDPRPurpose{
+			EnforceAlgo:    "full",
+			EnforcePurpose: &newEnforcePurpose,
+		}
+	}
+
+	patchConfig, err := json.Marshal(newAccount)
+	if err != nil {
+		return nil, err
+	}
+
+	newConfig, err = jsonpatch.MergePatch(config, patchConfig)
+	if err != nil {
+		return nil, err
+	}
+
+	return newConfig, nil
+}
diff --git a/adapters/pubmatic/pubmatic.go b/adapters/pubmatic/pubmatic.go
index 50fed350..464980f7 100644
--- a/adapters/pubmatic/pubmatic.go
+++ b/adapters/pubmatic/pubmatic.go
@@ -24,9 +24,10 @@ type PubmaticAdapter struct {
 }
 
 type pubmaticBidExt struct {
-	BidType           *int                 `json:"BidType,omitempty"`
-	VideoCreativeInfo *pubmaticBidExtVideo `json:"video,omitempty"`
-	Marketplace       string               `json:"marketplace,omitempty"`
+	BidType            *int                 `json:"BidType,omitempty"`
+	VideoCreativeInfo  *pubmaticBidExtVideo `json:"video,omitempty"`
+	Marketplace        string               `json:"marketplace,omitempty"`
+	PrebidDealPriority int                  `json:"prebiddealpriority,omitempty"`
 }
 
 type pubmaticWrapperExt struct {
@@ -387,40 +388,40 @@ func (a *PubmaticAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa
 	for _, sb := range bidResp.SeatBid {
 		for i := 0; i < len(sb.Bid); i++ {
 			bid := sb.Bid[i]
-			impVideo := &openrtb_ext.ExtBidPrebidVideo{}
-
 			if len(bid.Cat) > 1 {
 				bid.Cat = bid.Cat[0:1]
 			}
 
-			seat := ""
+			typedBid := &adapters.TypedBid{
+				Bid:      &bid,
+				BidType:  openrtb_ext.BidTypeBanner,
+				BidVideo: &openrtb_ext.ExtBidPrebidVideo{},
+			}
+
 			var bidExt *pubmaticBidExt
-			bidType := openrtb_ext.BidTypeBanner
 			err := json.Unmarshal(bid.Ext, &bidExt)
 			if err != nil {
 				errs = append(errs, err)
 			} else if bidExt != nil {
-				seat = bidExt.Marketplace
+				typedBid.Seat = openrtb_ext.BidderName(bidExt.Marketplace)
+				typedBid.BidType = getBidType(bidExt)
+				if bidExt.PrebidDealPriority > 0 {
+					typedBid.DealPriority = bidExt.PrebidDealPriority
+				}
+
 				if bidExt.VideoCreativeInfo != nil && bidExt.VideoCreativeInfo.Duration != nil {
-					impVideo.Duration = *bidExt.VideoCreativeInfo.Duration
+					typedBid.BidVideo.Duration = *bidExt.VideoCreativeInfo.Duration
 				}
-				bidType = getBidType(bidExt)
 			}
 
-			if bidType == openrtb_ext.BidTypeNative {
+			if typedBid.BidType == openrtb_ext.BidTypeNative {
 				bid.AdM, err = getNativeAdm(bid.AdM)
 				if err != nil {
 					errs = append(errs, err)
 				}
 			}
 
-			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
-				Bid:      &bid,
-				BidType:  bidType,
-				BidVideo: impVideo,
-				Seat:     openrtb_ext.BidderName(seat),
-			})
-
+			bidResponse.Bids = append(bidResponse.Bids, typedBid)
 		}
 	}
 	return bidResponse, errs
diff --git a/adapters/yieldlab/yieldlab.go b/adapters/yieldlab/yieldlab.go
index 619fdf93..cba103af 100644
--- a/adapters/yieldlab/yieldlab.go
+++ b/adapters/yieldlab/yieldlab.go
@@ -49,6 +49,10 @@ func (a *YieldlabAdapter) makeEndpointURL(req *openrtb2.BidRequest, params *open
 	q.Set("ts", a.cacheBuster())
 	q.Set("t", a.makeTargetingValues(params))
 
+	if hasFormats, formats := a.makeFormats(req); hasFormats {
+		q.Set("sizes", formats)
+	}
+
 	if req.User != nil && req.User.BuyerUID != "" {
 		q.Set("ids", "ylid:"+req.User.BuyerUID)
 	}
@@ -88,6 +92,21 @@ func (a *YieldlabAdapter) makeEndpointURL(req *openrtb2.BidRequest, params *open
 	return uri.String(), nil
 }
 
+func (a *YieldlabAdapter) makeFormats(req *openrtb2.BidRequest) (bool, string) {
+	var formatsPerAdslot []string
+	for _, impression := range req.Imp {
+		if !impIsTypeBannerOnly(impression) {
+			continue
+		}
+
+		adslotID := a.extractAdslotID(impression)
+		for _, format := range impression.Banner.Format {
+			formatsPerAdslot = append(formatsPerAdslot, fmt.Sprintf("%s:%d|%d", adslotID, format.W, format.H))
+		}
+	}
+	return len(formatsPerAdslot) != 0, strings.Join(formatsPerAdslot, ",")
+}
+
 func (a *YieldlabAdapter) getGDPR(request *openrtb2.BidRequest) (string, string, error) {
 	consent := ""
 	if request.User != nil && request.User.Ext != nil {
@@ -340,3 +359,8 @@ func splitSize(size string) (uint64, uint64, error) {
 	return width, height, nil
 
 }
+
+// impIsTypeBannerOnly returns true if impression is only from type banner. Mixed typed with banner would also result in false.
+func impIsTypeBannerOnly(impression openrtb2.Imp) bool {
+	return impression.Banner != nil && impression.Audio == nil && impression.Video == nil && impression.Native == nil
+}
diff --git a/amp/parse.go b/amp/parse.go
index 478ce84f..2c83ae22 100644
--- a/amp/parse.go
+++ b/amp/parse.go
@@ -2,11 +2,18 @@ package amp
 
 import (
 	"errors"
+	"fmt"
 	"net/http"
 	"strconv"
 	"strings"
 
+	tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2"
+
 	"github.com/mxmCherry/openrtb/v16/openrtb2"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/privacy"
+	"github.com/prebid/prebid-server/privacy/ccpa"
+	"github.com/prebid/prebid-server/privacy/gdpr"
 )
 
 // Params defines the parameters of an AMP request.
@@ -14,13 +21,15 @@ type Params struct {
 	Account         string
 	CanonicalURL    string
 	Consent         string
+	ConsentType     int64
 	Debug           bool
+	GdprApplies     *bool
 	Origin          string
 	Size            Size
 	Slot            string
 	StoredRequestID string
-	Timeout         *uint64
 	Targeting       string
+	Timeout         *uint64
 }
 
 // Size defines size information of an AMP request.
@@ -32,6 +41,109 @@ type Size struct {
 	Width          int64
 }
 
+// Policy consent types
+const (
+	ConsentNone      = 0
+	ConsentTCF1      = 1
+	ConsentTCF2      = 2
+	ConsentUSPrivacy = 3
+)
+
+// ReadPolicy returns a privacy writer in accordance to the query values consent, consent_type and gdpr_applies.
+// Returned policy writer could either be GDPR, CCPA or NilPolicy. The second return value is a warning.
+func ReadPolicy(ampParams Params, pbsConfigGDPREnabled bool) (privacy.PolicyWriter, error) {
+	if len(ampParams.Consent) == 0 {
+		return privacy.NilPolicyWriter{}, nil
+	}
+
+	var rv privacy.PolicyWriter = privacy.NilPolicyWriter{}
+	var warning error
+	var warningMsg string
+
+	// If consent_type was set to CCPA or GDPR TCF2, we return a valid writer even if the consent string is invalid
+	switch ampParams.ConsentType {
+	case ConsentTCF1:
+		warningMsg = "TCF1 consent is deprecated and no longer supported."
+	case ConsentTCF2:
+		if pbsConfigGDPREnabled {
+			rv = buildGdprTCF2ConsentWriter(ampParams)
+			// Log warning if GDPR consent string is invalid
+			warningMsg = validateTCf2ConsentString(ampParams.Consent)
+		}
+	case ConsentUSPrivacy:
+		rv = ccpa.ConsentWriter{ampParams.Consent}
+		if ccpa.ValidateConsent(ampParams.Consent) {
+			if parseGdprApplies(ampParams.GdprApplies) == 1 {
+				// Log warning because AMP request comes with both a valid CCPA string and gdpr_applies set to true
+				warningMsg = "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string"
+			}
+		} else {
+			// Log warning if CCPA string is invalid
+			warningMsg = fmt.Sprintf("Consent string '%s' is not a valid CCPA consent string.", ampParams.Consent)
+		}
+	default:
+		if ccpa.ValidateConsent(ampParams.Consent) {
+			rv = ccpa.ConsentWriter{ampParams.Consent}
+			if parseGdprApplies(ampParams.GdprApplies) == 1 {
+				warningMsg = "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string"
+			}
+		} else if pbsConfigGDPREnabled && len(validateTCf2ConsentString(ampParams.Consent)) == 0 {
+			rv = buildGdprTCF2ConsentWriter(ampParams)
+		} else {
+			warningMsg = fmt.Sprintf("Consent string '%s' is not recognized as one of the supported formats CCPA or TCF2.", ampParams.Consent)
+		}
+	}
+
+	if len(warningMsg) > 0 {
+		warning = &errortypes.Warning{
+			Message:     warningMsg,
+			WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
+		}
+	}
+	return rv, warning
+}
+
+// buildGdprTCF2ConsentWriter returns a gdpr.ConsentWriter that will set regs.ext.gdpr to the value
+// of 1 if gdpr_applies wasn't defined. The reason for this is that this function gets called when
+// GDPR applies, even if field gdpr_applies wasn't set in the AMP endpoint query.
+func buildGdprTCF2ConsentWriter(ampParams Params) gdpr.ConsentWriter {
+	writer := gdpr.ConsentWriter{Consent: ampParams.Consent}
+
+	// If gdpr_applies was not set, regs.ext.gdpr must equal 1
+	var gdprValue int8 = 1
+	if ampParams.GdprApplies != nil {
+		// set regs.ext.gdpr if non-nil gdpr_applies was set to true
+		gdprValue = parseGdprApplies(ampParams.GdprApplies)
+	}
+	writer.RegExtGDPR = &gdprValue
+
+	return writer
+}
+
+// parseGdprApplies returns a 0 if gdprApplies was not set or if false, and a 1 if
+// gdprApplies was set to true
+func parseGdprApplies(gdprApplies *bool) int8 {
+	gdpr := int8(0)
+
+	if gdprApplies != nil && *gdprApplies {
+		gdpr = int8(1)
+	}
+
+	return gdpr
+}
+
+// ParseParams parses the AMP parameters from a HTTP request.
+func validateTCf2ConsentString(consent string) string {
+	if tcf2.IsConsentV2(consent) {
+		if _, err := tcf2.ParseString(consent); err != nil {
+			return err.Error()
+		}
+	} else {
+		return fmt.Sprintf("Consent string '%s' is not a valid TCF2 consent string.", consent)
+	}
+	return ""
+}
+
 // ParseParams parses the AMP parameters from a HTTP request.
 func ParseParams(httpRequest *http.Request) (Params, error) {
 	query := httpRequest.URL.Query()
@@ -45,6 +157,7 @@ func ParseParams(httpRequest *http.Request) (Params, error) {
 		Account:      query.Get("account"),
 		CanonicalURL: query.Get("curl"),
 		Consent:      chooseConsent(query.Get("consent_string"), query.Get("gdpr_consent")),
+		ConsentType:  parseInt(query.Get("consent_type")),
 		Debug:        query.Get("debug") == "1",
 		Origin:       query.Get("__amp_source_origin"),
 		Size: Size{
@@ -56,17 +169,34 @@ func ParseParams(httpRequest *http.Request) (Params, error) {
 		},
 		Slot:            query.Get("slot"),
 		StoredRequestID: tagID,
-		Timeout:         parseIntPtr(query.Get("timeout")),
 		Targeting:       query.Get("targeting"),
 	}
+	var err error
+	urlQueryGdprApplies := query.Get("gdpr_applies")
+	if len(urlQueryGdprApplies) > 0 {
+		if params.GdprApplies, err = parseBoolPtr(urlQueryGdprApplies); err != nil {
+			return params, err
+		}
+	}
+
+	urlQueryTimeout := query.Get("timeout")
+	if len(urlQueryTimeout) > 0 {
+		if params.Timeout, err = parseIntPtr(urlQueryTimeout); err != nil {
+			return params, err
+		}
+	}
+
 	return params, nil
 }
 
-func parseIntPtr(value string) *uint64 {
-	if parsed, err := strconv.ParseUint(value, 10, 64); err == nil {
-		return &parsed
+func parseIntPtr(value string) (*uint64, error) {
+	var rv uint64
+	var err error
+
+	if rv, err = strconv.ParseUint(value, 10, 64); err != nil {
+		return nil, err
 	}
-	return nil
+	return &rv, nil
 }
 
 func parseInt(value string) int64 {
@@ -76,6 +206,16 @@ func parseInt(value string) int64 {
 	return 0
 }
 
+func parseBoolPtr(value string) (*bool, error) {
+	var rv bool
+	var err error
+
+	if rv, err = strconv.ParseBool(value); err != nil {
+		return nil, err
+	}
+	return &rv, nil
+}
+
 func parseMultisize(multisize string) []openrtb2.Format {
 	if multisize == "" {
 		return nil
diff --git a/config/accounts.go b/config/accounts.go
index 8705b167..e5bae8af 100644
--- a/config/accounts.go
+++ b/config/accounts.go
@@ -125,13 +125,10 @@ func (a *AccountGDPR) PurposeEnforced(purpose consentconstants.Purpose) (value,
 	if a.PurposeConfigs[purpose] == nil {
 		return true, false
 	}
-	if a.PurposeConfigs[purpose].EnforcePurpose == TCF2FullEnforcement {
-		return true, true
-	}
-	if a.PurposeConfigs[purpose].EnforcePurpose == TCF2NoEnforcement {
-		return false, true
+	if a.PurposeConfigs[purpose].EnforcePurpose == nil {
+		return true, false
 	}
-	return true, false
+	return *a.PurposeConfigs[purpose].EnforcePurpose, true
 }
 
 // PurposeEnforcingVendors gets the account level enforce vendors setting for a given purpose returning the value and
@@ -179,7 +176,8 @@ func (a *AccountGDPR) PurposeOneTreatmentAccessAllowed() (value, exists bool) {
 
 // AccountGDPRPurpose represents account-specific GDPR purpose configuration
 type AccountGDPRPurpose struct {
-	EnforcePurpose string `mapstructure:"enforce_purpose" json:"enforce_purpose,omitempty"`
+	EnforceAlgo    string `mapstructure:"enforce_algo" json:"enforce_algo,omitempty"`
+	EnforcePurpose *bool  `mapstructure:"enforce_purpose" json:"enforce_purpose,omitempty"`
 	EnforceVendors *bool  `mapstructure:"enforce_vendors" json:"enforce_vendors,omitempty"`
 	// Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed
 	VendorExceptions   []openrtb_ext.BidderName `mapstructure:"vendor_exceptions" json:"vendor_exceptions"`
diff --git a/config/config.go b/config/config.go
index a6d71ee0..827b0988 100644
--- a/config/config.go
+++ b/config/config.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"net/url"
 	"reflect"
+	"strconv"
 	"strings"
 	"time"
 
@@ -259,11 +260,11 @@ func (cfg *GDPR) validatePurposes(errs []error) []error {
 	}
 
 	for i := 0; i < len(purposeConfigs); i++ {
-		enforcePurposeValue := purposeConfigs[i].EnforcePurpose
-		enforcePurposeField := fmt.Sprintf("gdpr.tcf2.purpose%d.enforce_purpose", (i + 1))
+		enforceAlgoValue := purposeConfigs[i].EnforceAlgo
+		enforceAlgoField := fmt.Sprintf("gdpr.tcf2.purpose%d.enforce_algo", (i + 1))
 
-		if enforcePurposeValue != TCF2NoEnforcement && enforcePurposeValue != TCF2FullEnforcement {
-			errs = append(errs, fmt.Errorf("%s must be \"no\" or \"full\". Got %s", enforcePurposeField, enforcePurposeValue))
+		if enforceAlgoValue != TCF2FullEnforcement {
+			errs = append(errs, fmt.Errorf("%s must be \"full\". Got %s", enforceAlgoField, enforceAlgoValue))
 		}
 	}
 	return errs
@@ -284,7 +285,6 @@ func (t *GDPRTimeouts) ActiveTimeout() time.Duration {
 
 const (
 	TCF2FullEnforcement = "full"
-	TCF2NoEnforcement   = "no"
 )
 
 // TCF2 defines the TCF2 specific configurations for GDPR
@@ -332,10 +332,7 @@ func (t *TCF2) PurposeEnforced(purpose consentconstants.Purpose) (value bool) {
 	if t.PurposeConfigs[purpose] == nil {
 		return false
 	}
-	if t.PurposeConfigs[purpose].EnforcePurpose == TCF2FullEnforcement {
-		return true
-	}
-	return false
+	return t.PurposeConfigs[purpose].EnforcePurpose
 }
 
 // PurposeEnforcingVendors checks if enforcing vendors is turned on for a given purpose. With enforcing vendors
@@ -388,7 +385,8 @@ func (t *TCF2) PurposeOneTreatmentAccessAllowed() (value bool) {
 // Making a purpose struct so purpose specific details can be added later.
 type TCF2Purpose struct {
 	Enabled        bool   `mapstructure:"enabled"` // Deprecated: Use enforce_purpose instead
-	EnforcePurpose string `mapstructure:"enforce_purpose"`
+	EnforceAlgo    string `mapstructure:"enforce_algo"`
+	EnforcePurpose bool   `mapstructure:"enforce_purpose"`
 	EnforceVendors bool   `mapstructure:"enforce_vendors"`
 	// Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed
 	VendorExceptions   []openrtb_ext.BidderName `mapstructure:"vendor_exceptions"`
@@ -1181,7 +1179,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	migrateConfig(v)
 	migrateConfigPurposeOneTreatment(v)
 	migrateConfigSpecialFeature1(v)
-	migrateConfigTCF2PurposeEnabledFlags(v)
+	migrateConfigTCF2PurposeFlags(v)
 
 	// These defaults must be set after the migrate functions because those functions look for the presence of these
 	// config fields and there isn't a way to detect presence of a config field using the viper package if a default
@@ -1196,16 +1194,26 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("gdpr.tcf2.purpose8.enabled", true)
 	v.SetDefault("gdpr.tcf2.purpose9.enabled", true)
 	v.SetDefault("gdpr.tcf2.purpose10.enabled", true)
-	v.SetDefault("gdpr.tcf2.purpose1.enforce_purpose", TCF2FullEnforcement)
-	v.SetDefault("gdpr.tcf2.purpose2.enforce_purpose", TCF2FullEnforcement)
-	v.SetDefault("gdpr.tcf2.purpose3.enforce_purpose", TCF2FullEnforcement)
-	v.SetDefault("gdpr.tcf2.purpose4.enforce_purpose", TCF2FullEnforcement)
-	v.SetDefault("gdpr.tcf2.purpose5.enforce_purpose", TCF2FullEnforcement)
-	v.SetDefault("gdpr.tcf2.purpose6.enforce_purpose", TCF2FullEnforcement)
-	v.SetDefault("gdpr.tcf2.purpose7.enforce_purpose", TCF2FullEnforcement)
-	v.SetDefault("gdpr.tcf2.purpose8.enforce_purpose", TCF2FullEnforcement)
-	v.SetDefault("gdpr.tcf2.purpose9.enforce_purpose", TCF2FullEnforcement)
-	v.SetDefault("gdpr.tcf2.purpose10.enforce_purpose", TCF2FullEnforcement)
+	v.SetDefault("gdpr.tcf2.purpose1.enforce_algo", TCF2FullEnforcement)
+	v.SetDefault("gdpr.tcf2.purpose2.enforce_algo", TCF2FullEnforcement)
+	v.SetDefault("gdpr.tcf2.purpose3.enforce_algo", TCF2FullEnforcement)
+	v.SetDefault("gdpr.tcf2.purpose4.enforce_algo", TCF2FullEnforcement)
+	v.SetDefault("gdpr.tcf2.purpose5.enforce_algo", TCF2FullEnforcement)
+	v.SetDefault("gdpr.tcf2.purpose6.enforce_algo", TCF2FullEnforcement)
+	v.SetDefault("gdpr.tcf2.purpose7.enforce_algo", TCF2FullEnforcement)
+	v.SetDefault("gdpr.tcf2.purpose8.enforce_algo", TCF2FullEnforcement)
+	v.SetDefault("gdpr.tcf2.purpose9.enforce_algo", TCF2FullEnforcement)
+	v.SetDefault("gdpr.tcf2.purpose10.enforce_algo", TCF2FullEnforcement)
+	v.SetDefault("gdpr.tcf2.purpose1.enforce_purpose", true)
+	v.SetDefault("gdpr.tcf2.purpose2.enforce_purpose", true)
+	v.SetDefault("gdpr.tcf2.purpose3.enforce_purpose", true)
+	v.SetDefault("gdpr.tcf2.purpose4.enforce_purpose", true)
+	v.SetDefault("gdpr.tcf2.purpose5.enforce_purpose", true)
+	v.SetDefault("gdpr.tcf2.purpose6.enforce_purpose", true)
+	v.SetDefault("gdpr.tcf2.purpose7.enforce_purpose", true)
+	v.SetDefault("gdpr.tcf2.purpose8.enforce_purpose", true)
+	v.SetDefault("gdpr.tcf2.purpose9.enforce_purpose", true)
+	v.SetDefault("gdpr.tcf2.purpose10.enforce_purpose", true)
 	v.SetDefault("gdpr.tcf2.purpose_one_treatment.enabled", true)
 	v.SetDefault("gdpr.tcf2.purpose_one_treatment.access_allowed", true)
 	v.SetDefault("gdpr.tcf2.special_feature1.enforce", true)
@@ -1261,6 +1269,41 @@ func migrateConfigSpecialFeature1(v *viper.Viper) {
 	}
 }
 
+func migrateConfigTCF2PurposeFlags(v *viper.Viper) {
+	migrateConfigTCF2EnforcePurposeFlags(v)
+	migrateConfigTCF2PurposeEnabledFlags(v)
+}
+
+func migrateConfigTCF2EnforcePurposeFlags(v *viper.Viper) {
+	for i := 1; i <= 10; i++ {
+		algoField := fmt.Sprintf("gdpr.tcf2.purpose%d.enforce_algo", i)
+		purposeField := fmt.Sprintf("gdpr.tcf2.purpose%d.enforce_purpose", i)
+
+		if !v.IsSet(purposeField) {
+			continue
+		}
+		if _, ok := v.Get(purposeField).(string); !ok {
+			continue
+		}
+		if v.IsSet(algoField) {
+			glog.Warningf("using %s and ignoring deprecated %s string type", algoField, purposeField)
+		} else {
+			v.Set(algoField, TCF2FullEnforcement)
+
+			glog.Warningf("setting %s to \"%s\" based on deprecated %s string type \"%s\"", algoField, TCF2FullEnforcement, purposeField, v.GetString(purposeField))
+		}
+
+		oldPurposeFieldValue := v.GetString(purposeField)
+		newPurposeFieldValue := "false"
+		if oldPurposeFieldValue == TCF2FullEnforcement {
+			newPurposeFieldValue = "true"
+		}
+
+		glog.Warningf("converting %s from string \"%s\" to bool \"%s\"; string type is deprecated", purposeField, oldPurposeFieldValue, newPurposeFieldValue)
+		v.Set(purposeField, newPurposeFieldValue)
+	}
+}
+
 func migrateConfigTCF2PurposeEnabledFlags(v *viper.Viper) {
 	for i := 1; i <= 10; i++ {
 		oldField := fmt.Sprintf("gdpr.tcf2.purpose%d.enabled", i)
@@ -1272,20 +1315,12 @@ func migrateConfigTCF2PurposeEnabledFlags(v *viper.Viper) {
 				glog.Warningf("using %s and ignoring deprecated %s", newField, oldField)
 			} else {
 				glog.Warningf("%s is deprecated and should be changed to %s", oldField, newField)
-				if oldConfig {
-					v.Set(newField, TCF2FullEnforcement)
-				} else {
-					v.Set(newField, TCF2NoEnforcement)
-				}
+				v.Set(newField, oldConfig)
 			}
 		}
 
 		if v.IsSet(newField) {
-			if v.GetString(newField) == TCF2FullEnforcement {
-				v.Set(oldField, "true")
-			} else {
-				v.Set(oldField, "false")
-			}
+			v.Set(oldField, strconv.FormatBool(v.GetBool(newField)))
 		}
 	}
 }
diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index c6f651ca..8854c46d 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -33,6 +33,7 @@ var (
 	errCookieSyncGDPRConsentMissingSignalAmbiguous = errors.New("gdpr_consent is required. gdpr is not specified and is assumed to be 1 by the server. set gdpr=0 to exempt this request")
 	errCookieSyncInvalidBiddersType                = errors.New("invalid bidders type. must either be a string '*' or a string array of bidders")
 	errCookieSyncAccountBlocked                    = errors.New("account is disabled, please reach out to the prebid server host")
+	errCookieSyncAccountConfigMalformed            = errors.New("account config is malformed and could not be read")
 	errCookieSyncAccountInvalid                    = errors.New("account must be valid if provided, please reach out to the prebid server host")
 )
 
@@ -199,6 +200,8 @@ func (c *cookieSyncEndpoint) writeParseRequestErrorMetrics(err error) {
 	switch err {
 	case errCookieSyncAccountBlocked:
 		c.metrics.RecordCookieSync(metrics.CookieSyncAccountBlocked)
+	case errCookieSyncAccountConfigMalformed:
+		c.metrics.RecordCookieSync(metrics.CookieSyncAccountConfigMalformed)
 	case errCookieSyncAccountInvalid:
 		c.metrics.RecordCookieSync(metrics.CookieSyncAccountInvalid)
 	default:
@@ -305,6 +308,8 @@ func combineErrors(errs []error) error {
 			return errCookieSyncAccountBlocked
 		case errortypes.AcctRequiredErrorCode:
 			return errCookieSyncAccountInvalid
+		case errortypes.MalformedAcctErrorCode:
+			return errCookieSyncAccountConfigMalformed
 		}
 
 		errorStrings = append(errorStrings, err.Error())
diff --git a/endpoints/events/event.go b/endpoints/events/event.go
index 8c9c252a..6e7561ff 100644
--- a/endpoints/events/event.go
+++ b/endpoints/events/event.go
@@ -206,7 +206,9 @@ func HandleAccountServiceErrors(errs []error) (status int, messages []string) {
 		if errCode == errortypes.BlacklistedAppErrorCode || errCode == errortypes.BlacklistedAcctErrorCode {
 			status = http.StatusServiceUnavailable
 		}
-
+		if errCode == errortypes.MalformedAcctErrorCode {
+			status = http.StatusInternalServerError
+		}
 		if errCode == errortypes.TimeoutErrorCode && status == http.StatusBadRequest {
 			status = http.StatusGatewayTimeout
 		}
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 20361187..5d0e11f6 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -6,12 +6,13 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	jsonpatch "gopkg.in/evanphx/json-patch.v4"
 	"net/http"
 	"net/url"
 	"strings"
 	"time"
 
+	jsonpatch "gopkg.in/evanphx/json-patch.v4"
+
 	accountService "github.com/prebid/prebid-server/account"
 	"github.com/prebid/prebid-server/amp"
 	"github.com/prebid/prebid-server/analytics"
@@ -20,9 +21,6 @@ import (
 	"github.com/prebid/prebid-server/exchange"
 	"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"
-	"github.com/prebid/prebid-server/privacy/gdpr"
 	"github.com/prebid/prebid-server/stored_requests"
 	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
 	"github.com/prebid/prebid-server/stored_responses"
@@ -145,7 +143,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 	if errortypes.ContainsFatalError(errL) {
 		w.WriteHeader(http.StatusBadRequest)
 		for _, err := range errortypes.FatalOnly(errL) {
-			w.Write([]byte(fmt.Sprintf("Invalid request format: %s\n", err.Error())))
+			w.Write([]byte(fmt.Sprintf("Invalid request: %s\n", err.Error())))
 		}
 		labels.RequestStatus = metrics.RequestStatusBadInput
 		return
@@ -182,11 +180,16 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 				metricsStatus = metrics.RequestStatusBlacklisted
 				break
 			}
+			if errCode == errortypes.MalformedAcctErrorCode {
+				httpStatus = http.StatusInternalServerError
+				metricsStatus = metrics.RequestStatusAccountConfigErr
+				break
+			}
 		}
 		w.WriteHeader(httpStatus)
 		labels.RequestStatus = metricsStatus
 		for _, err := range errortypes.FatalOnly(errL) {
-			w.Write([]byte(fmt.Sprintf("Invalid request format: %s\n", err.Error())))
+			w.Write([]byte(fmt.Sprintf("Invalid request: %s\n", err.Error())))
 		}
 		ao.Errors = append(ao.Errors, acctIDErrs...)
 		return
@@ -443,7 +446,7 @@ func (deps *endpointDeps) overrideWithParams(ampParams amp.Params, req *openrtb2
 		req.Imp[0].TagID = ampParams.Slot
 	}
 
-	policyWriter, policyWriterErr := readPolicy(ampParams.Consent)
+	policyWriter, policyWriterErr := amp.ReadPolicy(ampParams, deps.cfg.GDPR.Enabled)
 	if policyWriterErr != nil {
 		return []error{policyWriterErr}
 	}
@@ -578,25 +581,6 @@ func setAmpExt(site *openrtb2.Site, value string) {
 	}
 }
 
-func readPolicy(consent string) (privacy.PolicyWriter, error) {
-	if len(consent) == 0 {
-		return privacy.NilPolicyWriter{}, nil
-	}
-
-	if gdpr.ValidateConsent(consent) {
-		return gdpr.ConsentWriter{consent}, nil
-	}
-
-	if ccpa.ValidateConsent(consent) {
-		return ccpa.ConsentWriter{consent}, nil
-	}
-
-	return privacy.NilPolicyWriter{}, &errortypes.Warning{
-		Message:     fmt.Sprintf("Consent '%s' is not recognized as either CCPA or GDPR TCF.", consent),
-		WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
-	}
-}
-
 // Sets the effective publisher ID for amp request
 func setEffectiveAmpPubID(req *openrtb2.BidRequest, account string) {
 	var pub *openrtb2.Publisher
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 9935ea74..8bc55ebf 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -1844,6 +1844,10 @@ func writeError(errs []error, w http.ResponseWriter, labels *metrics.Labels) boo
 				httpStatus = http.StatusServiceUnavailable
 				metricsStatus = metrics.RequestStatusBlacklisted
 				break
+			} else if erVal == errortypes.MalformedAcctErrorCode {
+				httpStatus = http.StatusInternalServerError
+				metricsStatus = metrics.RequestStatusAccountConfigErr
+				break
 			}
 		}
 		w.WriteHeader(httpStatus)
diff --git a/endpoints/openrtb2/sample-requests/account-malformed/malformed-acct.json b/endpoints/openrtb2/sample-requests/account-malformed/malformed-acct.json
new file mode 100644
index 00000000..93612993
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/account-malformed/malformed-acct.json
@@ -0,0 +1,65 @@
+{
+  "description": "Account is valid but account config is malformed",
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "user": {},
+    "site": {
+      "id": "cool_app",
+      "publisher": {
+        "id": "malformed_acct"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ],
+    "tmax": 500,
+    "ext": {
+      "prebid": {
+        "aliases": {
+          "districtm": "appnexus"
+        },
+        "bidadjustmentfactors": {
+          "appnexus": 1.01,
+          "districtm": 0.98,
+          "rubicon": 0.99
+        },
+        "cache": {
+          "bids": {}
+        },
+        "targeting": {
+          "includewinners": false,
+          "pricegranularity": {
+            "precision": 2,
+            "ranges": [
+              {
+                "max": 20,
+                "increment": 0.10
+              }
+            ]
+          }
+        }
+      }
+    }
+  },
+  "expectedReturnCode": 500,
+  "expectedErrorMessage": "Invalid request: The prebid-server account config for account id \"malformed_acct\" is malformed. Please reach out to the prebid server host.\n"
+}
diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-ccpa-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-ccpa-through-query.json
new file mode 100644
index 00000000..cfc943f5
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-ccpa-through-query.json
@@ -0,0 +1,87 @@
+{
+  "description": "CCPA consent type, valid US privacy string: expect regs.ext.gdpr set to 0 and user.ext.us_privacy set to the US string",
+  "query": "tag_id=101&consent_type=3&consent_string=1YYY",
+  "config": {
+    "mockBidders": [
+      {"bidderName": "appnexus", "currency": "USD", "price": 15}
+    ]
+  },
+  "mockBidRequest": {
+    "id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
+    "site": {"page": "prebid.org"},
+    "imp": [
+      {
+        "id": "/19968336/header-bid-tag-0",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ]
+  },
+  "expectedValidatedBidRequest": {
+    "id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
+    "site": {
+      "page": "prebid.org",
+      "publisher": {"cattax":0},
+      "ext": {"amp":1}
+    },
+    "device": {
+      "ip":"192.0.2.1",
+      "privacypolicy":0
+    },
+    "at": 1,
+    "imp": [
+      {
+        "id": "/19968336/header-bid-tag-0",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "secure": 1,
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ],
+    "regs": {
+      "ext": {"us_privacy":"1YYY"}
+    },
+    "ext": {"prebid":{"cache":{"bids":{"returnCreative":null},"vastxml":null},"channel":{"name":"amp","version":""},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true,"includebrandcategory":null,"includeformat":false,"durationrangesec":null,"preferdeals":false}}}
+  },
+  "expectedAmpResponse": {
+    "targeting": {
+      "hb_bidder": "appnexus",
+      "hb_bidder_appnexus": "appnexus",
+      "hb_cache_host": "www.pbcserver.com",
+      "hb_cache_host_appnex": "www.pbcserver.com",
+      "hb_cache_id": "0",
+      "hb_cache_id_appnexus": "0",
+      "hb_cache_path": "/pbcache/endpoint",
+      "hb_cache_path_appnex": "/pbcache/endpoint",
+      "hb_pb": "15.00",
+      "hb_pb_appnexus": "15.00"
+    },
+    "warnings": {
+      "general": [
+        {"code": 10002, "message": "debug turned off for account"}
+      ]
+    }
+  },
+  "expectedReturnCode": 200
+}
diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-legacy-tcf2-consent-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-legacy-tcf2-consent-through-query.json
new file mode 100644
index 00000000..7ae643ac
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-legacy-tcf2-consent-through-query.json
@@ -0,0 +1,90 @@
+{
+  "description": "TCF2 consent type, valid consent string passed through legacy gdpr_consent field, gdpr_applies set to true: expect regs.ext.gdpr to be set to 1 and user.ext.consent equal to the consent_string",
+  "query": "tag_id=101&consent_type=2&gdpr_applies=true&gdpr_consent=CPdECS0PdECS0ACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
+  "config": {
+    "mockBidders": [
+      {"bidderName": "appnexus", "currency": "USD", "price": 15}
+    ]
+  },
+  "mockBidRequest": {
+    "id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
+    "site": {"page": "prebid.org"},
+    "imp": [
+      {
+        "id": "/19968336/header-bid-tag-0",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ]
+  },
+  "expectedValidatedBidRequest": {
+    "id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
+    "site": {
+      "page": "prebid.org",
+      "publisher": {"cattax":0},
+      "ext": {"amp":1}
+    },
+    "device": {
+      "ip":"192.0.2.1",
+      "privacypolicy":0
+    },
+    "at": 1,
+    "imp": [
+      {
+        "id": "/19968336/header-bid-tag-0",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "secure": 1,
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ],
+    "regs": {
+      "ext": {"gdpr":1}
+    },
+    "user": {
+      "ext": {"consent":"CPdECS0PdECS0ACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA"}
+    },
+    "ext": {"prebid":{"cache":{"bids":{"returnCreative":null},"vastxml":null},"channel":{"name":"amp","version":""},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true,"includebrandcategory":null,"includeformat":false,"durationrangesec":null,"preferdeals":false}}}
+  },
+  "expectedAmpResponse": {
+    "targeting": {
+      "hb_bidder": "appnexus",
+      "hb_bidder_appnexus": "appnexus",
+      "hb_cache_host": "www.pbcserver.com",
+      "hb_cache_host_appnex": "www.pbcserver.com",
+      "hb_cache_id": "0",
+      "hb_cache_id_appnexus": "0",
+      "hb_cache_path": "/pbcache/endpoint",
+      "hb_cache_path_appnex": "/pbcache/endpoint",
+      "hb_pb": "15.00",
+      "hb_pb_appnexus": "15.00"
+    },
+    "warnings": {
+      "general": [
+        {"code": 10002, "message": "debug turned off for account"}
+      ]
+    }
+  },
+  "expectedReturnCode": 200
+}
diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf1-consent-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf1-consent-through-query.json
new file mode 100644
index 00000000..2829aa72
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf1-consent-through-query.json
@@ -0,0 +1,90 @@
+{
+  "description": "TCF1 consent type, valid consent string, and gdpr_applies. Expect regs.ext.gdpr to be set to 1 because the stored request comes with that value and user.ext.consent not set because TCF1 is deprecated",
+  "query": "tag_id=101&consent_type=1&consent_string&BOMhx4COMhx4CABABBAAABAAAAAfTABAAAA",
+  "config": {
+    "mockBidders": [
+      {"bidderName": "appnexus", "currency": "USD", "price": 15}
+    ]
+  },
+  "mockBidRequest": {
+    "id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
+    "site": {"page": "prebid.org"},
+    "regs": {
+      "ext": {"gdpr":1}
+    },
+    "imp": [
+      {
+        "id": "/19968336/header-bid-tag-0",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ]
+  },
+  "expectedValidatedBidRequest": {
+    "id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
+    "site": {
+      "page": "prebid.org",
+      "publisher": {"cattax":0},
+      "ext": {"amp":1}
+    },
+    "device": {
+      "ip":"192.0.2.1",
+      "privacypolicy":0
+    },
+    "at": 1,
+    "imp": [
+      {
+        "id": "/19968336/header-bid-tag-0",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "secure": 1,
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ],
+    "regs": {
+      "ext": {"gdpr":1}
+    },
+    "ext": {"prebid":{"cache":{"bids":{"returnCreative":null},"vastxml":null},"channel":{"name":"amp","version":""},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true,"includebrandcategory":null,"includeformat":false,"durationrangesec":null,"preferdeals":false}}}
+  },
+  "expectedAmpResponse": {
+    "targeting": {
+      "hb_bidder": "appnexus",
+      "hb_bidder_appnexus": "appnexus",
+      "hb_cache_host": "www.pbcserver.com",
+      "hb_cache_host_appnex": "www.pbcserver.com",
+      "hb_cache_id": "0",
+      "hb_cache_id_appnexus": "0",
+      "hb_cache_path": "/pbcache/endpoint",
+      "hb_cache_path_appnex": "/pbcache/endpoint",
+      "hb_pb": "15.00",
+      "hb_pb_appnexus": "15.00"
+    },
+    "warnings": {
+      "general": [
+        {"code": 10002, "message": "debug turned off for account"}
+      ]
+    }
+  },
+  "expectedReturnCode": 200
+}
diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf2-consent-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf2-consent-through-query.json
new file mode 100644
index 00000000..c51d7fbb
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf2-consent-through-query.json
@@ -0,0 +1,90 @@
+{
+  "description": "TCF2 consent type, valid consent_string, gdpr_applies set to true: expect regs.ext.gdpr to be set to 1 and user.ext.consent equal to the consent_string",
+  "query": "tag_id=101&consent_type=2&gdpr_applies=true&consent_string=CPdECS0PdECS0ACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
+  "config": {
+    "mockBidders": [
+      {"bidderName": "appnexus", "currency": "USD", "price": 15}
+    ]
+  },
+  "mockBidRequest": {
+    "id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
+    "site": {"page": "prebid.org"},
+    "imp": [
+      {
+        "id": "/19968336/header-bid-tag-0",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ]
+  },
+  "expectedValidatedBidRequest": {
+    "id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
+    "site": {
+      "page": "prebid.org",
+      "publisher": {"cattax":0},
+      "ext": {"amp":1}
+    },
+    "device": {
+      "ip":"192.0.2.1",
+      "privacypolicy":0
+    },
+    "at": 1,
+    "imp": [
+      {
+        "id": "/19968336/header-bid-tag-0",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "secure": 1,
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ],
+    "regs": {
+      "ext": {"gdpr":1}
+    },
+    "user": {
+      "ext": {"consent":"CPdECS0PdECS0ACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA"}
+    },
+    "ext": {"prebid":{"cache":{"bids":{"returnCreative":null},"vastxml":null},"channel":{"name":"amp","version":""},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true,"includebrandcategory":null,"includeformat":false,"durationrangesec":null,"preferdeals":false}}}
+  },
+  "expectedAmpResponse": {
+    "targeting": {
+      "hb_bidder": "appnexus",
+      "hb_bidder_appnexus": "appnexus",
+      "hb_cache_host": "www.pbcserver.com",
+      "hb_cache_host_appnex": "www.pbcserver.com",
+      "hb_cache_id": "0",
+      "hb_cache_id_appnexus": "0",
+      "hb_cache_path": "/pbcache/endpoint",
+      "hb_cache_path_appnex": "/pbcache/endpoint",
+      "hb_pb": "15.00",
+      "hb_pb_appnexus": "15.00"
+    },
+    "warnings": {
+      "general": [
+        {"code": 10002, "message": "debug turned off for account"}
+      ]
+    }
+  },
+  "expectedReturnCode": 200
+}
diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/aliased-buyeruids.json b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/aliased-buyeruids.json
similarity index 98%
rename from endpoints/openrtb2/sample-requests/valid-whole/supplementary/aliased-buyeruids.json
rename to endpoints/openrtb2/sample-requests/amp/valid-supplementary/aliased-buyeruids.json
index bf0f7a04..b881b490 100644
--- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/aliased-buyeruids.json
+++ b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/aliased-buyeruids.json
@@ -1,5 +1,6 @@
 {
   "description": "Amp request where root.user.ext.prebid.buyeruids field makes use of alias defined in root.ext.prebid.aliases",
+  "query": "tag_id=101",
   "config": {
     "mockBidders": [
       {"bidderName": "appnexus", "currency": "USD", "price": 2}
diff --git a/endpoints/openrtb2/sample-requests/amp/valid-supplementary/aliases.json b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/aliases.json
new file mode 100644
index 00000000..ddc61640
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/aliases.json
@@ -0,0 +1,55 @@
+{
+  "description": "Amp request impression requests a bid from a bidder alias defined in root.ext.prebid.aliases",
+  "query": "tag_id=101",
+  "config": {
+    "mockBidders": [
+      {"bidderName": "appnexus", "currency": "USD", "price": 2}
+    ]
+  },
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "site": {"page": "test.somepage.com"},
+    "imp": [
+      {
+        "id": "my-imp-id",
+        "banner": {
+          "format": [
+            {"w": 300, "h": 600}
+          ]
+        },
+        "ext": {
+          "unknown": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ],
+    "ext": {
+      "prebid": {
+        "aliases": {
+          "unknown": "appnexus"
+        }
+      }
+    }
+  },
+  "expectedAmpResponse": {
+    "targeting": {
+      "hb_bidder": "unknown",
+      "hb_bidder_unknown": "unknown",
+      "hb_cache_host": "www.pbcserver.com",
+      "hb_cache_host_unknow": "www.pbcserver.com",
+      "hb_cache_id": "0",
+      "hb_cache_id_unknown": "0",
+      "hb_cache_path": "/pbcache/endpoint",
+      "hb_cache_path_unknow": "/pbcache/endpoint",
+      "hb_pb": "2.00",
+      "hb_pb_unknown": "2.00"
+    },
+    "warnings": {
+      "general": [
+        {"code": 10002, "message": "debug turned off for account"}
+      ]
+    }
+  },
+  "expectedReturnCode": 200
+}
diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/gdpr-no-consentstring.json b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/gdpr-no-consentstring.json
similarity index 98%
rename from endpoints/openrtb2/sample-requests/valid-whole/supplementary/gdpr-no-consentstring.json
rename to endpoints/openrtb2/sample-requests/amp/valid-supplementary/gdpr-no-consentstring.json
index fe7fb53f..3f511265 100644
--- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/gdpr-no-consentstring.json
+++ b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/gdpr-no-consentstring.json
@@ -1,5 +1,6 @@
 {
   "description": "Amp request comes with a root.regs.ext.gdpr value but is missing GDPR consent string",
+  "query": "tag_id=101",
   "config": {
     "mockBidders": [
       {"bidderName": "appnexus", "currency": "USD", "price": 3}
diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/gdpr.json b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/gdpr.json
similarity index 98%
rename from endpoints/openrtb2/sample-requests/valid-whole/supplementary/gdpr.json
rename to endpoints/openrtb2/sample-requests/amp/valid-supplementary/gdpr.json
index 1792d0e6..216f2286 100644
--- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/gdpr.json
+++ b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/gdpr.json
@@ -1,5 +1,6 @@
 {
   "description": "Amp request comes with a root.regs.ext.gdpr and a GDPR consent string found in root.user.ext.consent",
+  "query": "tag_id=101",
   "config": {
     "mockBidders": [
       {"bidderName": "appnexus", "currency": "USD", "price": 15}
diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-resp.json b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/imp-with-stored-resp.json
similarity index 98%
rename from endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-resp.json
rename to endpoints/openrtb2/sample-requests/amp/valid-supplementary/imp-with-stored-resp.json
index a8b8eb54..ca17a862 100644
--- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-resp.json
+++ b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/imp-with-stored-resp.json
@@ -1,5 +1,6 @@
 {
   "description": "Amp request where impression makes reference to a valid stored response with a $5.00 bid",
+  "query": "tag_id=101",
   "mockAmpStoredResponse": {
     "6d718149": "[{\"bid\": [{\"id\": \"bid_id\", \"price\": 5.00, \"ext\": {\"prebid\": {\"targeting\": { \"hb_pb\": \"1.20\", \"hb_cat\": \"IAB-20\", \"hb_cache_id\": \"some_id\"}}}}],\"seat\": \"appnexus\"}]"
   },
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index e35a76e7..c3aca47a 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -374,6 +374,10 @@ func handleError(labels *metrics.Labels, w http.ResponseWriter, errL []error, vo
 			status = http.StatusBadRequest
 			labels.RequestStatus = metrics.RequestStatusBadInput
 			break
+		} else if erVal == errortypes.MalformedAcctErrorCode {
+			status = http.StatusInternalServerError
+			labels.RequestStatus = metrics.RequestStatusAccountConfigErr
+			break
 		}
 		errors = fmt.Sprintf("%s %s", errors, er.Error())
 	}
diff --git a/endpoints/setuid.go b/endpoints/setuid.go
index 712178a0..53ad228d 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -89,6 +89,8 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
 			switch err {
 			case errCookieSyncAccountBlocked:
 				metricsEngine.RecordSetUid(metrics.SetUidAccountBlocked)
+			case errCookieSyncAccountConfigMalformed:
+				metricsEngine.RecordSetUid(metrics.SetUidAccountConfigMalformed)
 			case errCookieSyncAccountInvalid:
 				metricsEngine.RecordSetUid(metrics.SetUidAccountInvalid)
 			default:
diff --git a/errortypes/code.go b/errortypes/code.go
index 3974e8bd..d6c421fe 100644
--- a/errortypes/code.go
+++ b/errortypes/code.go
@@ -12,6 +12,7 @@ const (
 	BlacklistedAcctErrorCode
 	AcctRequiredErrorCode
 	NoConversionRateErrorCode
+	MalformedAcctErrorCode
 )
 
 // Defines numeric codes for well-known warnings.
diff --git a/errortypes/errortypes.go b/errortypes/errortypes.go
index 1fed2d7d..d93075b7 100644
--- a/errortypes/errortypes.go
+++ b/errortypes/errortypes.go
@@ -165,6 +165,24 @@ func (err *BidderTemporarilyDisabled) Severity() Severity {
 	return SeverityWarning
 }
 
+// MalformedAcct should be used when the retrieved account config cannot be unmarshaled
+// These errors will be written to http.ResponseWriter before canceling execution
+type MalformedAcct struct {
+	Message string
+}
+
+func (err *MalformedAcct) Error() string {
+	return err.Message
+}
+
+func (err *MalformedAcct) Code() int {
+	return MalformedAcctErrorCode
+}
+
+func (err *MalformedAcct) Severity() Severity {
+	return SeverityFatal
+}
+
 // Warning is a generic non-fatal error.
 type Warning struct {
 	Message     string
diff --git a/go.mod b/go.mod
index 91faf67e..41a40ebd 100644
--- a/go.mod
+++ b/go.mod
@@ -31,10 +31,9 @@ require (
 	github.com/yudai/gojsondiff v1.0.0
 	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
 	github.com/yudai/pp v2.0.1+incompatible // indirect
-	golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2
+	golang.org/x/net v0.0.0-20220909164309-bea034e7d591
 	golang.org/x/text v0.3.7
 	google.golang.org/grpc v1.46.2
-	google.golang.org/protobuf v1.28.0 // indirect
 	gopkg.in/evanphx/json-patch.v4 v4.12.0
 	gopkg.in/yaml.v3 v3.0.1
 )
diff --git a/go.sum b/go.sum
index d55a80cd..0abe4ff3 100644
--- a/go.sum
+++ b/go.sum
@@ -633,8 +633,9 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y=
 golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
+golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -755,8 +756,9 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
+golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/metrics/metrics.go b/metrics/metrics.go
index 51faafb1..42bec72f 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -202,12 +202,13 @@ func CookieTypes() []CookieFlag {
 
 // Request/return status
 const (
-	RequestStatusOK           RequestStatus = "ok"
-	RequestStatusBadInput     RequestStatus = "badinput"
-	RequestStatusErr          RequestStatus = "err"
-	RequestStatusNetworkErr   RequestStatus = "networkerr"
-	RequestStatusBlacklisted  RequestStatus = "blacklistedacctorapp"
-	RequestStatusQueueTimeout RequestStatus = "queuetimeout"
+	RequestStatusOK               RequestStatus = "ok"
+	RequestStatusBadInput         RequestStatus = "badinput"
+	RequestStatusErr              RequestStatus = "err"
+	RequestStatusNetworkErr       RequestStatus = "networkerr"
+	RequestStatusBlacklisted      RequestStatus = "blacklistedacctorapp"
+	RequestStatusQueueTimeout     RequestStatus = "queuetimeout"
+	RequestStatusAccountConfigErr RequestStatus = "acctconfigerr"
 )
 
 func RequestStatuses() []RequestStatus {
@@ -218,6 +219,7 @@ func RequestStatuses() []RequestStatus {
 		RequestStatusNetworkErr,
 		RequestStatusBlacklisted,
 		RequestStatusQueueTimeout,
+		RequestStatusAccountConfigErr,
 	}
 }
 
@@ -300,12 +302,13 @@ func TCFVersionToValue(version int) TCFVersionValue {
 type CookieSyncStatus string
 
 const (
-	CookieSyncOK                    CookieSyncStatus = "ok"
-	CookieSyncBadRequest            CookieSyncStatus = "bad_request"
-	CookieSyncOptOut                CookieSyncStatus = "opt_out"
-	CookieSyncGDPRHostCookieBlocked CookieSyncStatus = "gdpr_blocked_host_cookie"
-	CookieSyncAccountBlocked        CookieSyncStatus = "acct_blocked"
-	CookieSyncAccountInvalid        CookieSyncStatus = "acct_invalid"
+	CookieSyncOK                     CookieSyncStatus = "ok"
+	CookieSyncBadRequest             CookieSyncStatus = "bad_request"
+	CookieSyncOptOut                 CookieSyncStatus = "opt_out"
+	CookieSyncGDPRHostCookieBlocked  CookieSyncStatus = "gdpr_blocked_host_cookie"
+	CookieSyncAccountBlocked         CookieSyncStatus = "acct_blocked"
+	CookieSyncAccountConfigMalformed CookieSyncStatus = "acct_config_malformed"
+	CookieSyncAccountInvalid         CookieSyncStatus = "acct_invalid"
 )
 
 // CookieSyncStatuses returns possible cookie sync statuses.
@@ -316,6 +319,7 @@ func CookieSyncStatuses() []CookieSyncStatus {
 		CookieSyncOptOut,
 		CookieSyncGDPRHostCookieBlocked,
 		CookieSyncAccountBlocked,
+		CookieSyncAccountConfigMalformed,
 		CookieSyncAccountInvalid,
 	}
 }
@@ -345,13 +349,14 @@ type SetUidStatus string
 
 // /setuid action labels
 const (
-	SetUidOK                    SetUidStatus = "ok"
-	SetUidBadRequest            SetUidStatus = "bad_request"
-	SetUidOptOut                SetUidStatus = "opt_out"
-	SetUidGDPRHostCookieBlocked SetUidStatus = "gdpr_blocked_host_cookie"
-	SetUidAccountBlocked        SetUidStatus = "acct_blocked"
-	SetUidAccountInvalid        SetUidStatus = "acct_invalid"
-	SetUidSyncerUnknown         SetUidStatus = "syncer_unknown"
+	SetUidOK                     SetUidStatus = "ok"
+	SetUidBadRequest             SetUidStatus = "bad_request"
+	SetUidOptOut                 SetUidStatus = "opt_out"
+	SetUidGDPRHostCookieBlocked  SetUidStatus = "gdpr_blocked_host_cookie"
+	SetUidAccountBlocked         SetUidStatus = "acct_blocked"
+	SetUidAccountConfigMalformed SetUidStatus = "acct_config_malformed"
+	SetUidAccountInvalid         SetUidStatus = "acct_invalid"
+	SetUidSyncerUnknown          SetUidStatus = "syncer_unknown"
 )
 
 // SetUidStatuses returns possible setuid statuses.
@@ -362,6 +367,7 @@ func SetUidStatuses() []SetUidStatus {
 		SetUidOptOut,
 		SetUidGDPRHostCookieBlocked,
 		SetUidAccountBlocked,
+		SetUidAccountConfigMalformed,
 		SetUidAccountInvalid,
 		SetUidSyncerUnknown,
 	}
diff --git a/openrtb_ext/imp_yieldlab.go b/openrtb_ext/imp_yieldlab.go
index 604b7e8c..8a93f457 100644
--- a/openrtb_ext/imp_yieldlab.go
+++ b/openrtb_ext/imp_yieldlab.go
@@ -4,7 +4,6 @@ package openrtb_ext
 type ExtImpYieldlab struct {
 	AdslotID  string            `json:"adslotId"`
 	SupplyID  string            `json:"supplyId"`
-	AdSize    string            `json:"adSize"`
 	Targeting map[string]string `json:"targeting"`
 	ExtId     string            `json:"extId"`
 }
diff --git a/openrtb_ext/request_wrapper.go b/openrtb_ext/request_wrapper.go
index 380d14d1..9bbe0833 100644
--- a/openrtb_ext/request_wrapper.go
+++ b/openrtb_ext/request_wrapper.go
@@ -877,8 +877,8 @@ func (re *RegExt) GetUSPrivacy() string {
 	return uSPrivacy
 }
 
-func (re *RegExt) SetUSPrivacy(uSPrivacy string) {
-	re.usPrivacy = uSPrivacy
+func (re *RegExt) SetUSPrivacy(usPrivacy string) {
+	re.usPrivacy = usPrivacy
 	re.usPrivacyDirty = true
 }
 
diff --git a/privacy/gdpr/consentwriter.go b/privacy/gdpr/consentwriter.go
index 18fd519a..98fba874 100644
--- a/privacy/gdpr/consentwriter.go
+++ b/privacy/gdpr/consentwriter.go
@@ -9,11 +9,24 @@ import (
 
 // ConsentWriter implements the PolicyWriter interface for GDPR TCF.
 type ConsentWriter struct {
-	Consent string
+	Consent    string
+	RegExtGDPR *int8
 }
 
 // Write mutates an OpenRTB bid request with the GDPR TCF consent.
 func (c ConsentWriter) Write(req *openrtb2.BidRequest) error {
+	if c.RegExtGDPR != nil {
+		reqWrap := &openrtb_ext.RequestWrapper{BidRequest: req}
+		if regsExt, err := reqWrap.GetRegExt(); err == nil {
+			regsExt.SetGDPR(c.RegExtGDPR)
+		} else {
+			return err
+		}
+		if err := reqWrap.RebuildRequest(); err != nil {
+			return err
+		}
+	}
+
 	if c.Consent == "" {
 		return nil
 	}
diff --git a/static/bidder-info/dianomi.yaml b/static/bidder-info/dianomi.yaml
index 64ce5287..d4a8d2f8 100644
--- a/static/bidder-info/dianomi.yaml
+++ b/static/bidder-info/dianomi.yaml
@@ -1,5 +1,5 @@
 maintainer:
-  email: 'eng@dianomi.com'
+  email: 'prebid-maintainer@dianomi.com'
 gvlVendorID: 885
 capabilities:
   app:
@@ -16,7 +16,6 @@ userSync:
   iframe:
     url: 'https://www-prebid.dianomi.com/prebid/usersync/index.html?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}'
     userMacro: '$UID'
-  # Dianomi supports user syncing, but requires configuration by the host. contact this
-  # bidder directly at the email address in this file to ask about enabling user sync.
-  supports:
-    - iframe
+  redirect:
+    url: 'https://data.dianomi.com/frontend/usync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}'
+    userMacro: '$UID'
diff --git a/static/bidder-params/yieldlab.json b/static/bidder-params/yieldlab.json
index 900d65da..9d0fd0e8 100644
--- a/static/bidder-params/yieldlab.json
+++ b/static/bidder-params/yieldlab.json
@@ -12,10 +12,6 @@
       "type": "string",
       "description": "Yieldlab ID of the supply"
     },
-    "adSize": {
-      "type": "string",
-      "description": "Size of the adslot in pixel, e.g. 200x50"
-    },
     "extId": {
       "type": "string",
       "description": "External ID used for reporting"
@@ -27,7 +23,6 @@
   },
   "required": [
     "adslotId",
-    "supplyId",
-    "adSize"
+    "supplyId"
   ]
 }


Test patch:
diff --git a/account/account_test.go b/account/account_test.go
index 68522462..244c4318 100644
--- a/account/account_test.go
+++ b/account/account_test.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/buger/jsonparser"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/metrics"
@@ -15,8 +16,10 @@ import (
 )
 
 var mockAccountData = map[string]json.RawMessage{
-	"valid_acct":    json.RawMessage(`{"disabled":false}`),
-	"disabled_acct": json.RawMessage(`{"disabled":true}`),
+	"valid_acct":        json.RawMessage(`{"disabled":false}`),
+	"disabled_acct":     json.RawMessage(`{"disabled":true}`),
+	"malformed_acct":    json.RawMessage(`{"disabled":"invalid type"}`),
+	"gdpr_convert_acct": json.RawMessage(`{"disabled":false,"gdpr":{"purpose5":{"enforce_purpose":"full"}}}`),
 }
 
 type mockAccountFetcher struct {
@@ -67,6 +70,18 @@ func TestGetAccount(t *testing.T) {
 		{accountID: "disabled_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}},
 		{accountID: "disabled_acct", required: true, disabled: true, err: &errortypes.BlacklistedAcct{}},
 
+		// pubID given and matches a host account with Disabled: false and GDPR purpose data to convert
+		{accountID: "gdpr_convert_acct", required: false, disabled: false, err: nil},
+		{accountID: "gdpr_convert_acct", required: true, disabled: false, err: nil},
+		{accountID: "gdpr_convert_acct", required: false, disabled: true, err: nil},
+		{accountID: "gdpr_convert_acct", required: true, disabled: true, err: nil},
+
+		// pubID given and matches a host account that has a malformed config
+		{accountID: "malformed_acct", required: false, disabled: false, err: &errortypes.MalformedAcct{}},
+		{accountID: "malformed_acct", required: true, disabled: false, err: &errortypes.MalformedAcct{}},
+		{accountID: "malformed_acct", required: false, disabled: true, err: &errortypes.MalformedAcct{}},
+		{accountID: "malformed_acct", required: true, disabled: true, err: &errortypes.MalformedAcct{}},
+
 		// account not provided (does not exist)
 		{accountID: "", required: false, disabled: false, err: nil},
 		{accountID: "", required: true, disabled: false, err: nil},
@@ -180,3 +195,137 @@ func TestSetDerivedConfig(t *testing.T) {
 		assert.ElementsMatch(t, basicEnforcementMapKeys, tt.basicEnforcementVendors, tt.description)
 	}
 }
+
+func TestConvertGDPREnforcePurposeFields(t *testing.T) {
+	enforcePurposeNo := `{"enforce_purpose":"no"}`
+	enforcePurposeNoMapped := `{"enforce_algo":"full", "enforce_purpose":false}`
+	enforcePurposeFull := `{"enforce_purpose":"full"}`
+	enforcePurposeFullMapped := `{"enforce_algo":"full", "enforce_purpose":true}`
+
+	tests := []struct {
+		description string
+		giveConfig  []byte
+		wantConfig  []byte
+		wantErr     error
+	}{
+		{
+			description: "config is nil",
+			giveConfig:  nil,
+			wantConfig:  nil,
+			wantErr:     nil,
+		},
+		{
+			description: "config is empty - no gdpr key",
+			giveConfig:  []byte(``),
+			wantConfig:  []byte(``),
+			wantErr:     nil,
+		},
+		{
+			description: "gdpr present but empty",
+			giveConfig:  []byte(`{"gdpr": {}}`),
+			wantConfig:  []byte(`{"gdpr": {}}`),
+			wantErr:     nil,
+		},
+		{
+			description: "gdpr present but invalid",
+			giveConfig:  []byte(`{"gdpr": {`),
+			wantConfig:  nil,
+			wantErr:     jsonparser.MalformedJsonError,
+		},
+		{
+			description: "gdpr.purpose1 present but empty",
+			giveConfig:  []byte(`{"gdpr":{"purpose1":{}}}`),
+			wantConfig:  []byte(`{"gdpr":{"purpose1":{}}}`),
+			wantErr:     nil,
+		},
+		{
+			description: "gdpr.purpose1.enforce_purpose is set to bool",
+			giveConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_purpose":true}}}`),
+			wantConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_purpose":true}}}`),
+			wantErr:     nil,
+		},
+		{
+			description: "gdpr.purpose1.enforce_purpose is set to string full",
+			giveConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_purpose":"full"}}}`),
+			wantConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":true}}}`),
+			wantErr:     nil,
+		},
+		{
+			description: "gdpr.purpose1.enforce_purpose is set to string no",
+			giveConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_purpose":"no"}}}`),
+			wantConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":false}}}`),
+			wantErr:     nil,
+		},
+		{
+			description: "gdpr.purpose1.enforce_purpose is set to string no and other fields are untouched during conversion",
+			giveConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_purpose":"no", "enforce_vendors":true}}}`),
+			wantConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":false, "enforce_vendors":true}}}`),
+			wantErr:     nil,
+		},
+		{
+			description: "gdpr.purpose1.enforce_purpose is set but invalid",
+			giveConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_purpose":}}}`),
+			wantConfig:  nil,
+			wantErr:     jsonparser.MalformedJsonError,
+		},
+		{
+			description: "gdpr.purpose1.enforce_algo is set",
+			giveConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full"}}}`),
+			wantConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full"}}}`),
+			wantErr:     nil,
+		},
+		{
+			description: "gdpr.purpose1.enforce_purpose is set to string and enforce_algo is set",
+			giveConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":"full"}}}`),
+			wantConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":"full"}}}`),
+			wantErr:     nil,
+		},
+		{
+			description: "gdpr.purpose1.enforce_purpose is set to string and enforce_algo is set but invalid",
+			giveConfig:  []byte(`{"gdpr":{"purpose1":{"enforce_algo":, "enforce_purpose":"full"}}}`),
+			wantConfig:  nil,
+			wantErr:     jsonparser.MalformedJsonError,
+		},
+		{
+			description: "gdpr.purpose{1-10}.enforce_purpose are set to strings no and full alternating",
+			giveConfig: []byte(`{"gdpr":{` +
+				`"purpose1":` + enforcePurposeNo +
+				`,"purpose2":` + enforcePurposeFull +
+				`,"purpose3":` + enforcePurposeNo +
+				`,"purpose4":` + enforcePurposeFull +
+				`,"purpose5":` + enforcePurposeNo +
+				`,"purpose6":` + enforcePurposeFull +
+				`,"purpose7":` + enforcePurposeNo +
+				`,"purpose8":` + enforcePurposeFull +
+				`,"purpose9":` + enforcePurposeNo +
+				`,"purpose10":` + enforcePurposeFull +
+				`}}`),
+			wantConfig: []byte(`{"gdpr":{` +
+				`"purpose1":` + enforcePurposeNoMapped +
+				`,"purpose2":` + enforcePurposeFullMapped +
+				`,"purpose3":` + enforcePurposeNoMapped +
+				`,"purpose4":` + enforcePurposeFullMapped +
+				`,"purpose5":` + enforcePurposeNoMapped +
+				`,"purpose6":` + enforcePurposeFullMapped +
+				`,"purpose7":` + enforcePurposeNoMapped +
+				`,"purpose8":` + enforcePurposeFullMapped +
+				`,"purpose9":` + enforcePurposeNoMapped +
+				`,"purpose10":` + enforcePurposeFullMapped +
+				`}}`),
+			wantErr: nil,
+		},
+	}
+
+	for _, tt := range tests {
+		newConfig, err := ConvertGDPREnforcePurposeFields(tt.giveConfig)
+		if tt.wantErr != nil {
+			assert.Error(t, err, tt.description)
+		}
+
+		if len(tt.wantConfig) == 0 {
+			assert.Equal(t, tt.wantConfig, newConfig, tt.description)
+		} else {
+			assert.JSONEq(t, string(tt.wantConfig), string(newConfig), tt.description)
+		}
+	}
+}
diff --git a/adapters/pubmatic/pubmatic_test.go b/adapters/pubmatic/pubmatic_test.go
index 1c99d994..a32be7c7 100644
--- a/adapters/pubmatic/pubmatic_test.go
+++ b/adapters/pubmatic/pubmatic_test.go
@@ -2,6 +2,7 @@ package pubmatic
 
 import (
 	"encoding/json"
+	"net/http"
 	"testing"
 
 	"github.com/mxmCherry/openrtb/v16/openrtb2"
@@ -264,3 +265,97 @@ func TestPubmaticAdapter_MakeRequests(t *testing.T) {
 		})
 	}
 }
+
+func TestPubmaticAdapter_MakeBids(t *testing.T) {
+	type fields struct {
+		URI string
+	}
+	type args struct {
+		internalRequest *openrtb2.BidRequest
+		externalRequest *adapters.RequestData
+		response        *adapters.ResponseData
+	}
+	tests := []struct {
+		name     string
+		fields   fields
+		args     args
+		wantErr  []error
+		wantResp *adapters.BidderResponse
+	}{
+		{
+			name: "happy path, valid response with all bid params",
+			args: args{
+				response: &adapters.ResponseData{
+					StatusCode: http.StatusOK,
+					Body:       []byte(`{"id": "test-request-id", "seatbid":[{"seat": "958", "bid":[{"id": "7706636740145184841", "impid": "test-imp-id", "price": 0.500000, "adid": "29681110", "adm": "some-test-ad", "adomain":["pubmatic.com"], "crid": "29681110", "h": 250, "w": 300, "dealid": "testdeal", "ext":{"dspid": 6, "deal_channel": 1, "prebiddealpriority": 1}}]}], "bidid": "5778926625248726496", "cur": "USD"}`),
+				},
+			},
+			wantErr: nil,
+			wantResp: &adapters.BidderResponse{
+				Bids: []*adapters.TypedBid{
+					{
+						Bid: &openrtb2.Bid{
+							ID:      "7706636740145184841",
+							ImpID:   "test-imp-id",
+							Price:   0.500000,
+							AdID:    "29681110",
+							AdM:     "some-test-ad",
+							ADomain: []string{"pubmatic.com"},
+							CrID:    "29681110",
+							H:       250,
+							W:       300,
+							DealID:  "testdeal",
+							Ext:     json.RawMessage(`{"dspid": 6, "deal_channel": 1, "prebiddealpriority": 1}`),
+						},
+						DealPriority: 1,
+						BidType:      openrtb_ext.BidTypeBanner,
+						BidVideo:     &openrtb_ext.ExtBidPrebidVideo{},
+					},
+				},
+				Currency: "USD",
+			},
+		},
+		{
+			name: "ignore invalid prebiddealpriority",
+			args: args{
+				response: &adapters.ResponseData{
+					StatusCode: http.StatusOK,
+					Body:       []byte(`{"id": "test-request-id", "seatbid":[{"seat": "958", "bid":[{"id": "7706636740145184841", "impid": "test-imp-id", "price": 0.500000, "adid": "29681110", "adm": "some-test-ad", "adomain":["pubmatic.com"], "crid": "29681110", "h": 250, "w": 300, "dealid": "testdeal", "ext":{"dspid": 6, "deal_channel": 1, "prebiddealpriority": -1}}]}], "bidid": "5778926625248726496", "cur": "USD"}`),
+				},
+			},
+			wantErr: nil,
+			wantResp: &adapters.BidderResponse{
+				Bids: []*adapters.TypedBid{
+					{
+						Bid: &openrtb2.Bid{
+							ID:      "7706636740145184841",
+							ImpID:   "test-imp-id",
+							Price:   0.500000,
+							AdID:    "29681110",
+							AdM:     "some-test-ad",
+							ADomain: []string{"pubmatic.com"},
+							CrID:    "29681110",
+							H:       250,
+							W:       300,
+							DealID:  "testdeal",
+							Ext:     json.RawMessage(`{"dspid": 6, "deal_channel": 1, "prebiddealpriority": -1}`),
+						},
+						BidType:  openrtb_ext.BidTypeBanner,
+						BidVideo: &openrtb_ext.ExtBidPrebidVideo{},
+					},
+				},
+				Currency: "USD",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			a := &PubmaticAdapter{
+				URI: tt.fields.URI,
+			}
+			gotResp, gotErr := a.MakeBids(tt.args.internalRequest, tt.args.externalRequest, tt.args.response)
+			assert.Equal(t, tt.wantErr, gotErr, gotErr)
+			assert.Equal(t, tt.wantResp, gotResp)
+		})
+	}
+}
diff --git a/adapters/pubmatic/pubmatictest/exemplary/banner.json b/adapters/pubmatic/pubmatictest/exemplary/banner.json
index 5012192a..ba38d843 100644
--- a/adapters/pubmatic/pubmatictest/exemplary/banner.json
+++ b/adapters/pubmatic/pubmatictest/exemplary/banner.json
@@ -113,7 +113,8 @@
                   "dealid":"test deal",
                   "ext": {
                     "dspid": 6,
-                    "deal_channel": 1
+                    "deal_channel": 1,
+                    "prebiddealpriority": 1
                   }
                 }]
               }
@@ -143,7 +144,8 @@
               "dealid":"test deal",
               "ext": {
                 "dspid": 6,
-                "deal_channel": 1
+                "deal_channel": 1,
+                "prebiddealpriority": 1
               }
             },
             "type": "banner"
diff --git a/adapters/yieldlab/params_test.go b/adapters/yieldlab/params_test.go
index 8c230c15..5414a21c 100644
--- a/adapters/yieldlab/params_test.go
+++ b/adapters/yieldlab/params_test.go
@@ -40,22 +40,17 @@ func TestInvalidParams(t *testing.T) {
 }
 
 var validParams = []string{
-	`{"adslotId": "123","supplyId":"23456","adSize":"100x100"}`,
-	`{"adslotId": "123","supplyId":"23456","adSize":"100x100","extId":"asdf"}`,
-	`{"adslotId": "123","supplyId":"23456","adSize":"100x100","extId":"asdf","targeting":{"a":"b"}}`,
-	`{"adslotId": "123","supplyId":"23456","adSize":"100x100","targeting":{"a":"b"}}`,
-	`{"adslotId": "123","supplyId":"23456","adSize":"100x100","targeting":{"a":"b"}}`,
+	`{"adslotId": "123","supplyId":"23456"}`,
+	`{"adslotId": "123","supplyId":"23456","extId":"asdf"}`,
+	`{"adslotId": "123","supplyId":"23456","extId":"asdf","targeting":{"a":"b"}}`,
+	`{"adslotId": "123","supplyId":"23456","targeting":{"a":"b"}}`,
 }
 
 var invalidParams = []string{
-	`{"supplyId":"23456","adSize":"100x100"}`,
-	`{"adslotId": "123","adSize":"100x100","extId":"asdf"}`,
-	`{"adslotId": "123","supplyId":"23456","extId":"asdf","targeting":{"a":"b"}}`,
-	`{"adslotId": "123","supplyId":"23456"}`,
-	`{"adSize":"100x100","supplyId":"23456"}`,
-	`{"adslotId": "123","adSize":"100x100"}`,
 	`{"supplyId":"23456"}`,
+	`{"adslotId": "123","extId":"asdf"}`,
 	`{"adslotId": "123"}`,
+	`{"supplyId":"23456"}`,
 	`{}`,
 	`[]`,
 	`{"a":"b"}`,
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/banner.json b/adapters/yieldlab/yieldlabtest/exemplary/banner.json
index 8dd94404..9780ba01 100644
--- a/adapters/yieldlab/yieldlabtest/exemplary/banner.json
+++ b/adapters/yieldlab/yieldlabtest/exemplary/banner.json
@@ -9,6 +9,10 @@
             {
               "w": 728,
               "h": 90
+            },
+            {
+              "w": 800,
+              "h": 300
             }
           ]
         },
@@ -16,7 +20,6 @@
           "bidder": {
             "adslotId": "12345",
             "supplyId": "123456789",
-            "adSize": "728x90",
             "targeting": {
               "key1": "value1",
               "key2": "value2"
@@ -70,7 +73,7 @@
             "169.254.13.37"
           ]
         },
-        "uri": "https://ad.yieldlab.net/testing/12345?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+        "uri": "https://ad.yieldlab.net/testing/12345?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&sizes=12345%3A728%7C90%2C12345%3A800%7C300&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
       },
       "mockResponse": {
         "status": 200,
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/gdpr.json b/adapters/yieldlab/yieldlabtest/exemplary/gdpr.json
index 381ba688..5197efba 100644
--- a/adapters/yieldlab/yieldlabtest/exemplary/gdpr.json
+++ b/adapters/yieldlab/yieldlabtest/exemplary/gdpr.json
@@ -16,7 +16,6 @@
           "bidder": {
             "adslotId": "12345",
             "supplyId": "123456789",
-            "adSize": "728x90",
             "targeting": {
               "key1": "value1",
               "key2": "value2"
@@ -78,7 +77,7 @@
             "169.254.13.37"
           ]
         },
-        "uri": "https://ad.yieldlab.net/testing/12345?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&content=json&gdpr=1&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+        "uri": "https://ad.yieldlab.net/testing/12345?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&content=json&gdpr=1&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&sizes=12345%3A728%7C90&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
       },
       "mockResponse": {
         "status": 200,
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/mixed_types.json b/adapters/yieldlab/yieldlabtest/exemplary/mixed_types.json
new file mode 100644
index 00000000..00f21852
--- /dev/null
+++ b/adapters/yieldlab/yieldlabtest/exemplary/mixed_types.json
@@ -0,0 +1,140 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 728,
+              "h": 90
+            },
+            {
+              "w": 800,
+              "h": 300
+            }
+          ]
+        },
+        "video": {
+          "context": "instream",
+          "mimes": [
+            "video/mp4"
+          ],
+          "playerSize": [
+            [
+              400,
+              600
+            ]
+          ],
+          "minduration": 1,
+          "maxduration": 2,
+          "protocols": [
+            1,
+            2
+          ],
+          "w": 1,
+          "h": 2,
+          "startdelay": 1,
+          "placement": 1,
+          "playbackmethod": [
+            2
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adslotId": "12345",
+            "supplyId": "123456789",
+            "targeting": {
+              "key1": "value1",
+              "key2": "value2"
+            },
+            "extId": "abc"
+          }
+        }
+      }
+    ],
+    "user": {
+      "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c"
+    },
+    "device": {
+      "ifa": "hello-ads",
+      "devicetype": 4,
+      "connectiontype": 6,
+      "geo": {
+        "lat": 51.499488,
+        "lon": -0.128953
+      },
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
+      "ip": "169.254.13.37",
+      "h": 1098,
+      "w": 814
+    },
+    "site": {
+      "id": "fake-site-id",
+      "publisher": {
+        "id": "1"
+      },
+      "page": "http://localhost:9090/gdpr.html"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Cookie": [
+            "id=34a53e82-0dc3-4815-8b7e-b725ede0361c"
+          ],
+          "Referer": [
+            "http://localhost:9090/gdpr.html"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "169.254.13.37"
+          ]
+        },
+        "uri": "https://ad.yieldlab.net/testing/12345?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "id": 12345,
+            "price": 201,
+            "advertiser": "yieldlab",
+            "adsize": "728x90",
+            "pid": 1234,
+            "did": 5678,
+            "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5"
+          }
+        ]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "crid": "12345123433",
+            "dealid": "1234",
+            "id": "12345",
+            "impid": "test-imp-id",
+            "nurl": "https://ad.yieldlab.net/d/12345/123456789/728x90?id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing",
+            "adm": "<VAST version=\"2.0\"><Ad id=\"12345\"><Wrapper><AdSystem>Yieldlab</AdSystem><VASTAdTagURI><![CDATA[ https://ad.yieldlab.net/d/12345/123456789/728x90?id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing ]]></VASTAdTagURI><Impression></Impression><Creatives></Creatives></Wrapper></Ad></VAST>",
+            "price": 2.01,
+            "w": 728,
+            "h": 90
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/multiple_impressions.json b/adapters/yieldlab/yieldlabtest/exemplary/multiple_impressions.json
index c68748e3..bf3fbd72 100644
--- a/adapters/yieldlab/yieldlabtest/exemplary/multiple_impressions.json
+++ b/adapters/yieldlab/yieldlabtest/exemplary/multiple_impressions.json
@@ -16,7 +16,6 @@
           "bidder": {
             "adslotId": "12345",
             "supplyId": "123456789",
-            "adSize": "728x90",
             "targeting": {
               "key1": "value1",
               "key2": "value2"
@@ -39,7 +38,6 @@
           "bidder": {
             "adslotId": "67890",
             "supplyId": "123456789",
-            "adSize": "300x250",
             "targeting": {
               "key1": "value1",
               "key2": "value2"
@@ -93,7 +91,7 @@
             "169.254.13.37"
           ]
         },
-        "uri": "https://ad.yieldlab.net/testing/12345,67890?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+        "uri": "https://ad.yieldlab.net/testing/12345,67890?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&sizes=12345%3A728%7C90%2C67890%3A300%7C250&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
       },
       "mockResponse": {
         "status": 200,
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/video.json b/adapters/yieldlab/yieldlabtest/exemplary/video.json
index b15d63a9..3d545a41 100644
--- a/adapters/yieldlab/yieldlabtest/exemplary/video.json
+++ b/adapters/yieldlab/yieldlabtest/exemplary/video.json
@@ -4,19 +4,10 @@
     "imp": [
       {
         "id": "test-imp-id",
-        "banner": {
-          "format": [
-            {
-              "w": 728,
-              "h": 90
-            }
-          ]
-        },
         "ext": {
           "bidder": {
             "adslotId": "12345",
             "supplyId": "123456789",
-            "adSize": "728x90",
             "targeting": {
               "key1": "value1",
               "key2": "value2"
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/video_app.json b/adapters/yieldlab/yieldlabtest/exemplary/video_app.json
index 17d96ddc..d9980e22 100644
--- a/adapters/yieldlab/yieldlabtest/exemplary/video_app.json
+++ b/adapters/yieldlab/yieldlabtest/exemplary/video_app.json
@@ -4,19 +4,10 @@
     "imp": [
       {
         "id": "test-imp-id",
-        "banner": {
-          "format": [
-            {
-              "w": 728,
-              "h": 90
-            }
-          ]
-        },
         "ext": {
           "bidder": {
             "adslotId": "12345",
             "supplyId": "123456789",
-            "adSize": "728x90",
             "targeting": {
               "key1": "value1",
               "key2": "value2"
diff --git a/amp/parse_test.go b/amp/parse_test.go
index 7a589bb4..2799d05e 100644
--- a/amp/parse_test.go
+++ b/amp/parse_test.go
@@ -5,6 +5,10 @@ import (
 	"testing"
 
 	"github.com/mxmCherry/openrtb/v16/openrtb2"
+	"github.com/prebid/prebid-server/errortypes"
+	"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"
 )
 
@@ -94,6 +98,397 @@ func TestParseParams(t *testing.T) {
 	}
 }
 
+func TestParseIntPtr(t *testing.T) {
+	var boolZero uint64 = 0
+	var boolOne uint64 = 1
+
+	type testResults struct {
+		intPtr *uint64
+		err    bool
+	}
+
+	testCases := []struct {
+		desc     string
+		input    string
+		expected testResults
+	}{
+		{
+			desc:  "Input is an empty string: expect nil pointer and error",
+			input: "",
+			expected: testResults{
+				intPtr: nil,
+				err:    true,
+			},
+		},
+		{
+			desc:  "Input is negative number: expect a nil pointer and error",
+			input: "-1",
+			expected: testResults{
+				intPtr: nil,
+				err:    true,
+			},
+		},
+		{
+			desc:  "Input is a string depicting a zero value: expect a reference pointing to zero value, no error",
+			input: "0",
+			expected: testResults{
+				intPtr: &boolZero,
+				err:    false,
+			},
+		},
+		{
+			desc:  "Input is a string depicting a value of 1: expect a reference pointing to the value of 1 and no error",
+			input: "1",
+			expected: testResults{
+				intPtr: &boolOne,
+				err:    false,
+			},
+		},
+	}
+	for _, tc := range testCases {
+		resultingIntPtr, resultingErr := parseIntPtr(tc.input)
+
+		assert.Equal(t, tc.expected.intPtr, resultingIntPtr, tc.desc)
+		if tc.expected.err {
+			assert.Error(t, resultingErr, tc.desc)
+		} else {
+			assert.NoError(t, resultingErr, tc.desc)
+		}
+	}
+}
+
+func TestParseBoolPtr(t *testing.T) {
+	boolTrue := true
+	boolFalse := false
+
+	type testResults struct {
+		boolPtr *bool
+		err     bool
+	}
+
+	testCases := []struct {
+		desc     string
+		input    string
+		expected testResults
+	}{
+		{
+			desc:  "Input is an empty string: expect nil pointer and error",
+			input: "",
+			expected: testResults{
+				boolPtr: nil,
+				err:     true,
+			},
+		},
+		{
+			desc:  "Input is neither true nor false: expect a nil pointer and error",
+			input: "other",
+			expected: testResults{
+				boolPtr: nil,
+				err:     true,
+			},
+		},
+		{
+			desc:  "Input is the word 'false', expect a reference pointing to false value",
+			input: "false",
+			expected: testResults{
+				boolPtr: &boolFalse,
+				err:     false,
+			},
+		},
+		{
+			desc:  "Input is the word 'true', expect a reference pointing to true value",
+			input: "true",
+			expected: testResults{
+				boolPtr: &boolTrue,
+				err:     false,
+			},
+		},
+	}
+	for _, tc := range testCases {
+		resultingBoolPtr, resultingErr := parseBoolPtr(tc.input)
+
+		assert.Equal(t, tc.expected.boolPtr, resultingBoolPtr, tc.desc)
+		if tc.expected.err {
+			assert.Error(t, resultingErr, tc.desc)
+		} else {
+			assert.NoError(t, resultingErr, tc.desc)
+		}
+	}
+}
+
+// TestPrivacyReader asserts the ReadPolicy scenarios
+func TestPrivacyReader(t *testing.T) {
+	int8Zero := int8(0)
+	int8One := int8(1)
+	boolTrue := true
+	boolFalse := false
+
+	type testInput struct {
+		ampParams Params
+	}
+	type expectedResults struct {
+		policyWriter privacy.PolicyWriter
+		warning      error
+	}
+	type testCase struct {
+		desc     string
+		in       testInput
+		expected expectedResults
+	}
+
+	testGroups := []struct {
+		groupDesc string
+		tests     []testCase
+	}{
+		{
+			groupDesc: "No consent string",
+			tests: []testCase{
+				{
+					desc:     "Params comes with an empty consent string, expect nil policy writer. No warning returned",
+					expected: expectedResults{policyWriter: privacy.NilPolicyWriter{}, warning: nil},
+				},
+			},
+		},
+		{
+			groupDesc: "TCF1",
+			tests: []testCase{
+				{
+					desc: "Consent type TCF1: expect nil policy writer. Warning is returned",
+					in: testInput{
+						ampParams: Params{Consent: "VALID_TCF1_CONSENT", ConsentType: ConsentTCF1},
+					},
+					expected: expectedResults{
+						policyWriter: privacy.NilPolicyWriter{},
+						warning:      &errortypes.Warning{Message: "TCF1 consent is deprecated and no longer supported.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+					},
+				},
+			},
+		},
+		{
+			groupDesc: "ConsentNone. In order to be backwards compatible, we'll guess what consent string this is",
+			tests: []testCase{
+				{
+					desc: "No consent type was specified and invalid consent string provided: expect nil policy writer and a warning",
+					in: testInput{
+						ampParams: Params{Consent: "NOT_CCPA_NOR_GDPR_TCF2"},
+					},
+					expected: expectedResults{
+						policyWriter: privacy.NilPolicyWriter{},
+						warning:      &errortypes.Warning{Message: "Consent string 'NOT_CCPA_NOR_GDPR_TCF2' is not recognized as one of the supported formats CCPA or TCF2.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+					},
+				},
+				{
+					desc: "No consent type specified but query params come with a valid CCPA consent string: expect a CCPA consent writer and no error nor warning",
+					in: testInput{
+						ampParams: Params{Consent: "1YYY"},
+					},
+					expected: expectedResults{
+						policyWriter: ccpa.ConsentWriter{"1YYY"},
+						warning:      nil,
+					},
+				},
+				{
+					desc: "No consent type, valid CCPA consent string and gdpr_applies set to true: expect a CCPA consent writer and a warning",
+					in: testInput{
+						ampParams: Params{Consent: "1YYY", GdprApplies: &boolTrue},
+					},
+					expected: expectedResults{
+						policyWriter: ccpa.ConsentWriter{"1YYY"},
+						warning:      &errortypes.Warning{Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+					},
+				},
+				{
+					desc: "No consent type, valid GDPR consent string and gdpr_applies not set: expect a GDPR consent writer and no error nor warning",
+					in: testInput{
+						ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA"},
+					},
+					expected: expectedResults{
+						policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One},
+						warning:      nil,
+					},
+				},
+			},
+		},
+		{
+			groupDesc: "Unrecognized consent type. In order to be backwards compatible, we'll guess what consent string type it is",
+			tests: []testCase{
+				{
+					desc: "Unrecognized consent type was specified and invalid consent string provided: expect nil policy writer and a warning",
+					in: testInput{
+						ampParams: Params{ConsentType: 101, Consent: "NOT_CCPA_NOR_GDPR_TCF2"},
+					},
+					expected: expectedResults{
+						policyWriter: privacy.NilPolicyWriter{},
+						warning:      &errortypes.Warning{Message: "Consent string 'NOT_CCPA_NOR_GDPR_TCF2' is not recognized as one of the supported formats CCPA or TCF2.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+					},
+				},
+				{
+					desc: "Unrecognized consent type specified but query params come with a valid CCPA consent string: expect a CCPA consent writer and no error nor warning",
+					in: testInput{
+						ampParams: Params{ConsentType: 101, Consent: "1YYY"},
+					},
+					expected: expectedResults{
+						policyWriter: ccpa.ConsentWriter{"1YYY"},
+						warning:      nil,
+					},
+				},
+				{
+					desc: "Unrecognized consent type, valid CCPA consent string and gdpr_applies set to true: expect a CCPA consent writer and a warning",
+					in: testInput{
+						ampParams: Params{ConsentType: 101, Consent: "1YYY", GdprApplies: &boolTrue},
+					},
+					expected: expectedResults{
+						policyWriter: ccpa.ConsentWriter{"1YYY"},
+						warning:      &errortypes.Warning{Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+					},
+				},
+				{
+					desc: "Unrecognized consent type, valid TCF2 consent string and gdpr_applies not set: expect GDPR consent writer and no error nor warning",
+					in: testInput{
+						ampParams: Params{ConsentType: 101, Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA"},
+					},
+					expected: expectedResults{
+						policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One},
+						warning:      nil,
+					},
+				},
+			},
+		},
+		{
+			groupDesc: "consent type TCF2. Return a valid GDPR consent writer in all scenarios.",
+			tests: []testCase{
+				{
+					desc: "GDPR consent string is invalid, but consent type is TCF2: return a valid GDPR writer and warn about the GDPR string being invalid",
+					in: testInput{
+						ampParams: Params{Consent: "INVALID_GDPR", ConsentType: ConsentTCF2, GdprApplies: nil},
+					},
+					expected: expectedResults{
+						policyWriter: gdpr.ConsentWriter{"INVALID_GDPR", &int8One},
+						warning:      &errortypes.Warning{Message: "Consent string 'INVALID_GDPR' is not a valid TCF2 consent string.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+					},
+				},
+				{
+					desc: "GDPR consent string is invalid, consent type is TCF2, gdpr_applies is set to true: return a valid GDPR writer and warn about the GDPR string being invalid",
+					in: testInput{
+						ampParams: Params{Consent: "INVALID_GDPR", ConsentType: ConsentTCF2, GdprApplies: &boolFalse},
+					},
+					expected: expectedResults{
+						policyWriter: gdpr.ConsentWriter{"INVALID_GDPR", &int8Zero},
+						warning:      &errortypes.Warning{Message: "Consent string 'INVALID_GDPR' is not a valid TCF2 consent string.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+					},
+				},
+				{
+					desc: "Valid GDPR consent string, gdpr_applies is set to false, return a valid GDPR writer, no warning",
+					in: testInput{
+						ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", ConsentType: ConsentTCF2, GdprApplies: &boolFalse},
+					},
+					expected: expectedResults{
+						policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8Zero},
+						warning:      nil,
+					},
+				},
+				{
+					desc: "Valid GDPR consent string, gdpr_applies is set to true, return a valid GDPR writer and no warning",
+					in: testInput{
+						ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", ConsentType: ConsentTCF2, GdprApplies: &boolTrue},
+					},
+					expected: expectedResults{
+						policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One},
+						warning:      nil,
+					},
+				},
+				{
+					desc: "Valid GDPR consent string, return a valid GDPR writer and no warning",
+					in: testInput{
+						ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", ConsentType: ConsentTCF2},
+					},
+					expected: expectedResults{
+						policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One},
+						warning:      nil,
+					},
+				},
+			},
+		},
+		{
+			groupDesc: "consent type CCPA. Return a valid CCPA consent writer in all scenarios.",
+			tests: []testCase{
+				{
+					desc: "CCPA consent string is invalid: return a valid writer a warning about the string being invalid",
+					in: testInput{
+						ampParams: Params{Consent: "XXXX", ConsentType: ConsentUSPrivacy},
+					},
+					expected: expectedResults{
+						policyWriter: ccpa.ConsentWriter{"XXXX"},
+						warning:      &errortypes.Warning{Message: "Consent string 'XXXX' is not a valid CCPA consent string.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+					},
+				},
+				{
+					desc: "Valid CCPA consent string, gdpr_applies is set to true: return a valid GDPR writer and warn about the gdpr_applies value.",
+					in: testInput{
+						ampParams: Params{Consent: "1YYY", ConsentType: ConsentUSPrivacy, GdprApplies: &boolTrue},
+					},
+					expected: expectedResults{
+						policyWriter: ccpa.ConsentWriter{"1YYY"},
+						warning:      &errortypes.Warning{Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", WarningCode: errortypes.InvalidPrivacyConsentWarningCode},
+					},
+				},
+				{
+					desc: "Valid CCPA consent string, return a valid GDPR writer and no warning",
+					in: testInput{
+						ampParams: Params{Consent: "1YYY", ConsentType: ConsentUSPrivacy},
+					},
+					expected: expectedResults{
+						policyWriter: ccpa.ConsentWriter{"1YYY"},
+						warning:      nil,
+					},
+				},
+			},
+		},
+	}
+	for _, group := range testGroups {
+		for _, tc := range group.tests {
+			actualPolicyWriter, actualErr := ReadPolicy(tc.in.ampParams, true)
+
+			assert.Equal(t, tc.expected.policyWriter, actualPolicyWriter, tc.desc)
+			assert.Equal(t, tc.expected.warning, actualErr, tc.desc)
+		}
+	}
+}
+
+func TestBuildGdprTCF2ConsentWriter(t *testing.T) {
+	int8Zero := int8(0)
+	int8One := int8(1)
+	boolTrue := true
+	boolFalse := false
+	consentString := "CONSENT"
+
+	testCases := []struct {
+		desc           string
+		inParams       Params
+		expectedWriter gdpr.ConsentWriter
+	}{
+		{
+			desc:           "gdpr_applies not set",
+			inParams:       Params{Consent: consentString},
+			expectedWriter: gdpr.ConsentWriter{consentString, &int8One},
+		},
+		{
+			desc:           "gdpr_applies set to false",
+			inParams:       Params{Consent: consentString, GdprApplies: &boolFalse},
+			expectedWriter: gdpr.ConsentWriter{consentString, &int8Zero},
+		},
+		{
+			desc:           "gdpr_applies set to true",
+			inParams:       Params{Consent: consentString, GdprApplies: &boolTrue},
+			expectedWriter: gdpr.ConsentWriter{consentString, &int8One},
+		},
+	}
+	for _, tc := range testCases {
+		actualPolicyWriter := buildGdprTCF2ConsentWriter(tc.inParams)
+		assert.Equal(t, tc.expectedWriter, actualPolicyWriter, tc.desc)
+	}
+}
+
 func TestParseMultisize(t *testing.T) {
 	testCases := []struct {
 		description     string
@@ -168,3 +563,33 @@ func TestParseMultisize(t *testing.T) {
 		assert.ElementsMatch(t, test.expectedFormats, result, test.description)
 	}
 }
+
+func TestParseGdprApplies(t *testing.T) {
+	gdprAppliesFalse := false
+	gdprAppliesTrue := true
+
+	testCases := []struct {
+		desc              string
+		inGdprApplies     *bool
+		expectRegsExtGdpr int8
+	}{
+		{
+			desc:              "gdprApplies was not set and defaulted to nil, expect 0",
+			inGdprApplies:     nil,
+			expectRegsExtGdpr: int8(0),
+		},
+		{
+			desc:              "gdprApplies isn't nil and is set to false, expect a value of 0",
+			inGdprApplies:     &gdprAppliesFalse,
+			expectRegsExtGdpr: int8(0),
+		},
+		{
+			desc:              "gdprApplies isn't nil and is set to true, expect a value of 1",
+			inGdprApplies:     &gdprAppliesTrue,
+			expectRegsExtGdpr: int8(1),
+		},
+	}
+	for _, tc := range testCases {
+		assert.Equal(t, tc.expectRegsExtGdpr, parseGdprApplies(tc.inGdprApplies), tc.desc)
+	}
+}
diff --git a/config/accounts_test.go b/config/accounts_test.go
index c4566329..be9f69d2 100644
--- a/config/accounts_test.go
+++ b/config/accounts_test.go
@@ -246,11 +246,14 @@ func TestAccountIntegrationGetByIntegrationType(t *testing.T) {
 }
 
 func TestPurposeEnforced(t *testing.T) {
+	True := true
+	False := false
+
 	tests := []struct {
 		description          string
 		givePurposeConfigNil bool
-		givePurpose1Enforced string
-		givePurpose2Enforced string
+		givePurpose1Enforced *bool
+		givePurpose2Enforced *bool
 		givePurpose          consentconstants.Purpose
 		wantEnforced         bool
 		wantEnforcedSet      bool
@@ -264,28 +267,28 @@ func TestPurposeEnforced(t *testing.T) {
 		},
 		{
 			description:          "Purpose 1 Enforced not set",
-			givePurpose1Enforced: "",
+			givePurpose1Enforced: nil,
 			givePurpose:          1,
 			wantEnforced:         true,
 			wantEnforcedSet:      false,
 		},
 		{
 			description:          "Purpose 1 Enforced set to full enforcement",
-			givePurpose1Enforced: TCF2FullEnforcement,
+			givePurpose1Enforced: &True,
 			givePurpose:          1,
 			wantEnforced:         true,
 			wantEnforcedSet:      true,
 		},
 		{
 			description:          "Purpose 1 Enforced set to no enforcement",
-			givePurpose1Enforced: TCF2NoEnforcement,
+			givePurpose1Enforced: &False,
 			givePurpose:          1,
 			wantEnforced:         false,
 			wantEnforcedSet:      true,
 		},
 		{
 			description:          "Purpose 2 Enforced set to full enforcement",
-			givePurpose2Enforced: TCF2FullEnforcement,
+			givePurpose2Enforced: &True,
 			givePurpose:          2,
 			wantEnforced:         true,
 			wantEnforcedSet:      true,
diff --git a/config/config_test.go b/config/config_test.go
index ca02550e..61c790de 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -160,70 +160,80 @@ func TestDefaults(t *testing.T) {
 		Enabled: true,
 		Purpose1: TCF2Purpose{
 			Enabled:            true,
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     true,
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 		},
 		Purpose2: TCF2Purpose{
 			Enabled:            true,
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     true,
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 		},
 		Purpose3: TCF2Purpose{
 			Enabled:            true,
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     true,
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 		},
 		Purpose4: TCF2Purpose{
 			Enabled:            true,
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     true,
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 		},
 		Purpose5: TCF2Purpose{
 			Enabled:            true,
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     true,
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 		},
 		Purpose6: TCF2Purpose{
 			Enabled:            true,
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     true,
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 		},
 		Purpose7: TCF2Purpose{
 			Enabled:            true,
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     true,
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 		},
 		Purpose8: TCF2Purpose{
 			Enabled:            true,
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     true,
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 		},
 		Purpose9: TCF2Purpose{
 			Enabled:            true,
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     true,
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
 		},
 		Purpose10: TCF2Purpose{
 			Enabled:            true,
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     true,
 			VendorExceptions:   []openrtb_ext.BidderName{},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
@@ -268,7 +278,8 @@ gdpr:
       vendor_exceptions: ["foo1a", "foo1b"]
     purpose2:
       enabled: false
-      enforce_purpose: "no"
+      enforce_algo: "full"
+      enforce_purpose: false
       enforce_vendors: false
       vendor_exceptions: ["foo2"]
     purpose3:
@@ -526,70 +537,80 @@ func TestFullConfig(t *testing.T) {
 		Enabled: true,
 		Purpose1: TCF2Purpose{
 			Enabled:            true, // true by default
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     false,
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo1a"), openrtb_ext.BidderName("foo1b")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo1a"): {}, openrtb_ext.BidderName("foo1b"): {}},
 		},
 		Purpose2: TCF2Purpose{
 			Enabled:            false,
-			EnforcePurpose:     TCF2NoEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     false,
 			EnforceVendors:     false,
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo2")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo2"): {}},
 		},
 		Purpose3: TCF2Purpose{
 			Enabled:            true, // true by default
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     false,
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo3")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo3"): {}},
 		},
 		Purpose4: TCF2Purpose{
 			Enabled:            true, // true by default
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     false,
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo4")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo4"): {}},
 		},
 		Purpose5: TCF2Purpose{
 			Enabled:            true, // true by default
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     false,
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo5")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo5"): {}},
 		},
 		Purpose6: TCF2Purpose{
 			Enabled:            true, // true by default
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     false,
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo6")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo6"): {}},
 		},
 		Purpose7: TCF2Purpose{
 			Enabled:            true, // true by default
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     false,
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo7")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo7"): {}},
 		},
 		Purpose8: TCF2Purpose{
 			Enabled:            true, // true by default
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     false,
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo8")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo8"): {}},
 		},
 		Purpose9: TCF2Purpose{
 			Enabled:            true, // true by default
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     false,
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo9")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo9"): {}},
 		},
 		Purpose10: TCF2Purpose{
 			Enabled:            true, // true by default
-			EnforcePurpose:     TCF2FullEnforcement,
+			EnforceAlgo:        TCF2FullEnforcement,
+			EnforcePurpose:     true,
 			EnforceVendors:     false,
 			VendorExceptions:   []openrtb_ext.BidderName{openrtb_ext.BidderName("foo10")},
 			VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo10"): {}},
@@ -704,16 +725,16 @@ func TestValidateConfig(t *testing.T) {
 		GDPR: GDPR{
 			DefaultValue: "1",
 			TCF2: TCF2{
-				Purpose1:  TCF2Purpose{EnforcePurpose: TCF2FullEnforcement},
-				Purpose2:  TCF2Purpose{EnforcePurpose: TCF2FullEnforcement},
-				Purpose3:  TCF2Purpose{EnforcePurpose: TCF2FullEnforcement},
-				Purpose4:  TCF2Purpose{EnforcePurpose: TCF2FullEnforcement},
-				Purpose5:  TCF2Purpose{EnforcePurpose: TCF2FullEnforcement},
-				Purpose6:  TCF2Purpose{EnforcePurpose: TCF2FullEnforcement},
-				Purpose7:  TCF2Purpose{EnforcePurpose: TCF2FullEnforcement},
-				Purpose8:  TCF2Purpose{EnforcePurpose: TCF2FullEnforcement},
-				Purpose9:  TCF2Purpose{EnforcePurpose: TCF2FullEnforcement},
-				Purpose10: TCF2Purpose{EnforcePurpose: TCF2FullEnforcement},
+				Purpose1:  TCF2Purpose{EnforceAlgo: TCF2FullEnforcement},
+				Purpose2:  TCF2Purpose{EnforceAlgo: TCF2FullEnforcement},
+				Purpose3:  TCF2Purpose{EnforceAlgo: TCF2FullEnforcement},
+				Purpose4:  TCF2Purpose{EnforceAlgo: TCF2FullEnforcement},
+				Purpose5:  TCF2Purpose{EnforceAlgo: TCF2FullEnforcement},
+				Purpose6:  TCF2Purpose{EnforceAlgo: TCF2FullEnforcement},
+				Purpose7:  TCF2Purpose{EnforceAlgo: TCF2FullEnforcement},
+				Purpose8:  TCF2Purpose{EnforceAlgo: TCF2FullEnforcement},
+				Purpose9:  TCF2Purpose{EnforceAlgo: TCF2FullEnforcement},
+				Purpose10: TCF2Purpose{EnforceAlgo: TCF2FullEnforcement},
 			},
 		},
 		StoredRequests: StoredRequests{
@@ -985,16 +1006,16 @@ func TestMigrateConfigTCF2PurposeEnabledFlags(t *testing.T) {
                   purpose10:
                     enabled: true
             `),
-			wantPurpose1EnforcePurpose:  TCF2NoEnforcement,
-			wantPurpose2EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose3EnforcePurpose:  TCF2NoEnforcement,
-			wantPurpose4EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose5EnforcePurpose:  TCF2NoEnforcement,
-			wantPurpose6EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose7EnforcePurpose:  TCF2NoEnforcement,
-			wantPurpose8EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose9EnforcePurpose:  TCF2NoEnforcement,
-			wantPurpose10EnforcePurpose: TCF2FullEnforcement,
+			wantPurpose1EnforcePurpose:  falseStr,
+			wantPurpose2EnforcePurpose:  trueStr,
+			wantPurpose3EnforcePurpose:  falseStr,
+			wantPurpose4EnforcePurpose:  trueStr,
+			wantPurpose5EnforcePurpose:  falseStr,
+			wantPurpose6EnforcePurpose:  trueStr,
+			wantPurpose7EnforcePurpose:  falseStr,
+			wantPurpose8EnforcePurpose:  trueStr,
+			wantPurpose9EnforcePurpose:  falseStr,
+			wantPurpose10EnforcePurpose: trueStr,
 			wantPurpose1Enabled:         falseStr,
 			wantPurpose2Enabled:         trueStr,
 			wantPurpose3Enabled:         falseStr,
@@ -1012,36 +1033,36 @@ func TestMigrateConfigTCF2PurposeEnabledFlags(t *testing.T) {
               gdpr:
                 tcf2:
                   purpose1:
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose2:
-                    enforce_purpose: "no"
+                    enforce_purpose: false
                   purpose3:
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose4:
-                    enforce_purpose: "no"
+                    enforce_purpose: false
                   purpose5:
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose6:
-                    enforce_purpose: "no"
+                    enforce_purpose: false
                   purpose7:
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose8:
-                    enforce_purpose: "no"
+                    enforce_purpose: false
                   purpose9:
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose10:
-                    enforce_purpose: "no"
+                    enforce_purpose: false
             `),
-			wantPurpose1EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose2EnforcePurpose:  TCF2NoEnforcement,
-			wantPurpose3EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose4EnforcePurpose:  TCF2NoEnforcement,
-			wantPurpose5EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose6EnforcePurpose:  TCF2NoEnforcement,
-			wantPurpose7EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose8EnforcePurpose:  TCF2NoEnforcement,
-			wantPurpose9EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose10EnforcePurpose: TCF2NoEnforcement,
+			wantPurpose1EnforcePurpose:  trueStr,
+			wantPurpose2EnforcePurpose:  falseStr,
+			wantPurpose3EnforcePurpose:  trueStr,
+			wantPurpose4EnforcePurpose:  falseStr,
+			wantPurpose5EnforcePurpose:  trueStr,
+			wantPurpose6EnforcePurpose:  falseStr,
+			wantPurpose7EnforcePurpose:  trueStr,
+			wantPurpose8EnforcePurpose:  falseStr,
+			wantPurpose9EnforcePurpose:  trueStr,
+			wantPurpose10EnforcePurpose: falseStr,
 			wantPurpose1Enabled:         trueStr,
 			wantPurpose2Enabled:         falseStr,
 			wantPurpose3Enabled:         trueStr,
@@ -1060,45 +1081,45 @@ func TestMigrateConfigTCF2PurposeEnabledFlags(t *testing.T) {
                 tcf2:
                   purpose1:
                     enabled: false
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose2:
                     enabled: false
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose3:
                     enabled: false
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose4:
                     enabled: false
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose5:
                     enabled: false
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose6:
                     enabled: false
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose7:
                     enabled: false
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose8:
                     enabled: false
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose9:
                     enabled: false
-                    enforce_purpose: "full"
+                    enforce_purpose: true
                   purpose10:
                     enabled: false
-                    enforce_purpose: "full"
+                    enforce_purpose: true
               `),
-			wantPurpose1EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose2EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose3EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose4EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose5EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose6EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose7EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose8EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose9EnforcePurpose:  TCF2FullEnforcement,
-			wantPurpose10EnforcePurpose: TCF2FullEnforcement,
+			wantPurpose1EnforcePurpose:  trueStr,
+			wantPurpose2EnforcePurpose:  trueStr,
+			wantPurpose3EnforcePurpose:  trueStr,
+			wantPurpose4EnforcePurpose:  trueStr,
+			wantPurpose5EnforcePurpose:  trueStr,
+			wantPurpose6EnforcePurpose:  trueStr,
+			wantPurpose7EnforcePurpose:  trueStr,
+			wantPurpose8EnforcePurpose:  trueStr,
+			wantPurpose9EnforcePurpose:  trueStr,
+			wantPurpose10EnforcePurpose: trueStr,
 			wantPurpose1Enabled:         trueStr,
 			wantPurpose2Enabled:         trueStr,
 			wantPurpose3Enabled:         trueStr,
@@ -1165,6 +1186,355 @@ func TestMigrateConfigTCF2PurposeEnabledFlags(t *testing.T) {
 	}
 }
 
+func TestMigrateConfigTCF2PurposeFlags(t *testing.T) {
+	tests := []struct {
+		description                string
+		config                     []byte
+		wantPurpose1EnforceAlgo    string
+		wantPurpose1EnforcePurpose bool
+		wantPurpose1Enabled        bool
+	}{
+		{
+			description: "enforce_purpose does not set enforce_algo but sets enabled",
+			config: []byte(`
+              gdpr:
+                tcf2:
+                  purpose1:
+                    enforce_algo: "off"
+                    enforce_purpose: "full"
+                    enabled: false
+                  purpose2:
+                    enforce_purpose: "full"
+                    enabled: false
+                  purpose3:
+                    enabled: false
+            `),
+			wantPurpose1EnforceAlgo:    "off",
+			wantPurpose1EnforcePurpose: true,
+			wantPurpose1Enabled:        true,
+		},
+		{
+			description: "enforce_purpose sets enforce_algo and enabled",
+			config: []byte(`
+              gdpr:
+                tcf2:
+                  purpose1:
+                    enforce_purpose: "full"
+                    enabled: false
+            `),
+			wantPurpose1EnforceAlgo:    "full",
+			wantPurpose1EnforcePurpose: true,
+			wantPurpose1Enabled:        true,
+		},
+		{
+			description: "enforce_purpose does not set enforce_algo or enabled",
+			config: []byte(`
+              gdpr:
+                tcf2:
+                  purpose1:
+                    enabled: false
+            `),
+			wantPurpose1EnforceAlgo:    "",
+			wantPurpose1EnforcePurpose: false,
+			wantPurpose1Enabled:        false,
+		},
+	}
+
+	for _, tt := range tests {
+		v := viper.New()
+		v.SetConfigType("yaml")
+		v.ReadConfig(bytes.NewBuffer(tt.config))
+
+		migrateConfigTCF2PurposeFlags(v)
+
+		assert.Equal(t, tt.wantPurpose1EnforceAlgo, v.GetString("gdpr.tcf2.purpose1.enforce_algo"), tt.description)
+		assert.Equal(t, tt.wantPurpose1EnforcePurpose, v.GetBool("gdpr.tcf2.purpose1.enforce_purpose"), tt.description)
+		assert.Equal(t, tt.wantPurpose1Enabled, v.GetBool("gdpr.tcf2.purpose1.enabled"), tt.description)
+	}
+
+}
+
+func TestMigrateConfigTCF2EnforcePurposeFlags(t *testing.T) {
+	trueStr := "true"
+	falseStr := "false"
+
+	tests := []struct {
+		description                 string
+		config                      []byte
+		wantEnforceAlgosSet         bool
+		wantPurpose1EnforceAlgo     string
+		wantPurpose2EnforceAlgo     string
+		wantPurpose3EnforceAlgo     string
+		wantPurpose4EnforceAlgo     string
+		wantPurpose5EnforceAlgo     string
+		wantPurpose6EnforceAlgo     string
+		wantPurpose7EnforceAlgo     string
+		wantPurpose8EnforceAlgo     string
+		wantPurpose9EnforceAlgo     string
+		wantPurpose10EnforceAlgo    string
+		wantEnforcePurposesSet      bool
+		wantPurpose1EnforcePurpose  string
+		wantPurpose2EnforcePurpose  string
+		wantPurpose3EnforcePurpose  string
+		wantPurpose4EnforcePurpose  string
+		wantPurpose5EnforcePurpose  string
+		wantPurpose6EnforcePurpose  string
+		wantPurpose7EnforcePurpose  string
+		wantPurpose8EnforcePurpose  string
+		wantPurpose9EnforcePurpose  string
+		wantPurpose10EnforcePurpose string
+	}{
+		{
+			description:            "enforce_algo and enforce_purpose are not set",
+			config:                 []byte{},
+			wantEnforceAlgosSet:    false,
+			wantEnforcePurposesSet: false,
+		},
+		{
+			description: "enforce_algo not set; set it based on enforce_purpose string value",
+			config: []byte(`
+              gdpr:
+                tcf2:
+                  purpose1:
+                    enforce_purpose: "full"
+                  purpose2:
+                    enforce_purpose: "no"
+                  purpose3:
+                    enforce_purpose: "full"
+                  purpose4:
+                    enforce_purpose: "no"
+                  purpose5:
+                    enforce_purpose: "full"
+                  purpose6:
+                    enforce_purpose: "no"
+                  purpose7:
+                    enforce_purpose: "full"
+                  purpose8:
+                    enforce_purpose: "no"
+                  purpose9:
+                    enforce_purpose: "full"
+                  purpose10:
+                    enforce_purpose: "no"
+            `),
+			wantEnforceAlgosSet:         true,
+			wantPurpose1EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose2EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose3EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose4EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose5EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose6EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose7EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose8EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose9EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose10EnforceAlgo:    TCF2FullEnforcement,
+			wantEnforcePurposesSet:      true,
+			wantPurpose1EnforcePurpose:  trueStr,
+			wantPurpose2EnforcePurpose:  falseStr,
+			wantPurpose3EnforcePurpose:  trueStr,
+			wantPurpose4EnforcePurpose:  falseStr,
+			wantPurpose5EnforcePurpose:  trueStr,
+			wantPurpose6EnforcePurpose:  falseStr,
+			wantPurpose7EnforcePurpose:  trueStr,
+			wantPurpose8EnforcePurpose:  falseStr,
+			wantPurpose9EnforcePurpose:  trueStr,
+			wantPurpose10EnforcePurpose: falseStr,
+		},
+		{
+			description: "enforce_algo not set; don't set it based on enforce_purpose bool value",
+			config: []byte(`
+              gdpr:
+                tcf2:
+                  purpose1:
+                    enforce_purpose: true
+                  purpose2:
+                    enforce_purpose: false
+                  purpose3:
+                    enforce_purpose: true
+                  purpose4:
+                    enforce_purpose: false
+                  purpose5:
+                    enforce_purpose: true
+                  purpose6:
+                    enforce_purpose: false
+                  purpose7:
+                    enforce_purpose: true
+                  purpose8:
+                    enforce_purpose: false
+                  purpose9:
+                    enforce_purpose: true
+                  purpose10:
+                    enforce_purpose: false
+            `),
+			wantEnforceAlgosSet:         false,
+			wantEnforcePurposesSet:      true,
+			wantPurpose1EnforcePurpose:  trueStr,
+			wantPurpose2EnforcePurpose:  falseStr,
+			wantPurpose3EnforcePurpose:  trueStr,
+			wantPurpose4EnforcePurpose:  falseStr,
+			wantPurpose5EnforcePurpose:  trueStr,
+			wantPurpose6EnforcePurpose:  falseStr,
+			wantPurpose7EnforcePurpose:  trueStr,
+			wantPurpose8EnforcePurpose:  falseStr,
+			wantPurpose9EnforcePurpose:  trueStr,
+			wantPurpose10EnforcePurpose: falseStr,
+		},
+		{
+			description: "enforce_algo is set and enforce_purpose is not; enforce_algo is unchanged",
+			config: []byte(`
+              gdpr:
+                tcf2:
+                  purpose1:
+                    enforce_algo: "full"
+                  purpose2:
+                    enforce_algo: "full"
+                  purpose3:
+                    enforce_algo: "full"
+                  purpose4:
+                    enforce_algo: "full"
+                  purpose5:
+                    enforce_algo: "full"
+                  purpose6:
+                    enforce_algo: "full"
+                  purpose7:
+                    enforce_algo: "full"
+                  purpose8:
+                    enforce_algo: "full"
+                  purpose9:
+                    enforce_algo: "full"
+                  purpose10:
+                    enforce_algo: "full"
+            `),
+			wantEnforceAlgosSet:      true,
+			wantPurpose1EnforceAlgo:  TCF2FullEnforcement,
+			wantPurpose2EnforceAlgo:  TCF2FullEnforcement,
+			wantPurpose3EnforceAlgo:  TCF2FullEnforcement,
+			wantPurpose4EnforceAlgo:  TCF2FullEnforcement,
+			wantPurpose5EnforceAlgo:  TCF2FullEnforcement,
+			wantPurpose6EnforceAlgo:  TCF2FullEnforcement,
+			wantPurpose7EnforceAlgo:  TCF2FullEnforcement,
+			wantPurpose8EnforceAlgo:  TCF2FullEnforcement,
+			wantPurpose9EnforceAlgo:  TCF2FullEnforcement,
+			wantPurpose10EnforceAlgo: TCF2FullEnforcement,
+			wantEnforcePurposesSet:   false,
+		},
+		{
+			description: "enforce_algo and enforce_purpose are set; enforce_algo is unchanged",
+			config: []byte(`
+              gdpr:
+                tcf2:
+                  purpose1:
+                    enforce_algo: "full"
+                    enforce_purpose: "no"
+                  purpose2:
+                    enforce_algo: "full"
+                    enforce_purpose: "no"
+                  purpose3:
+                    enforce_algo: "full"
+                    enforce_purpose: "no"
+                  purpose4:
+                    enforce_algo: "full"
+                    enforce_purpose: "no"
+                  purpose5:
+                    enforce_algo: "full"
+                    enforce_purpose: "no"
+                  purpose6:
+                    enforce_algo: "full"
+                    enforce_purpose: "no"
+                  purpose7:
+                    enforce_algo: "full"
+                    enforce_purpose: "no"
+                  purpose8:
+                    enforce_algo: "full"
+                    enforce_purpose: "no"
+                  purpose9:
+                    enforce_algo: "full"
+                    enforce_purpose: "no"
+                  purpose10:
+                    enforce_algo: "full"
+                    enforce_purpose: "no"
+            `),
+			wantEnforceAlgosSet:         true,
+			wantPurpose1EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose2EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose3EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose4EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose5EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose6EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose7EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose8EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose9EnforceAlgo:     TCF2FullEnforcement,
+			wantPurpose10EnforceAlgo:    TCF2FullEnforcement,
+			wantEnforcePurposesSet:      true,
+			wantPurpose1EnforcePurpose:  falseStr,
+			wantPurpose2EnforcePurpose:  falseStr,
+			wantPurpose3EnforcePurpose:  falseStr,
+			wantPurpose4EnforcePurpose:  falseStr,
+			wantPurpose5EnforcePurpose:  falseStr,
+			wantPurpose6EnforcePurpose:  falseStr,
+			wantPurpose7EnforcePurpose:  falseStr,
+			wantPurpose8EnforcePurpose:  falseStr,
+			wantPurpose9EnforcePurpose:  falseStr,
+			wantPurpose10EnforcePurpose: falseStr,
+		},
+	}
+
+	for _, tt := range tests {
+		v := viper.New()
+		v.SetConfigType("yaml")
+		v.ReadConfig(bytes.NewBuffer(tt.config))
+
+		migrateConfigTCF2EnforcePurposeFlags(v)
+
+		if tt.wantEnforceAlgosSet {
+			assert.Equal(t, tt.wantPurpose1EnforceAlgo, v.GetString("gdpr.tcf2.purpose1.enforce_algo"), tt.description)
+			assert.Equal(t, tt.wantPurpose2EnforceAlgo, v.GetString("gdpr.tcf2.purpose2.enforce_algo"), tt.description)
+			assert.Equal(t, tt.wantPurpose3EnforceAlgo, v.GetString("gdpr.tcf2.purpose3.enforce_algo"), tt.description)
+			assert.Equal(t, tt.wantPurpose4EnforceAlgo, v.GetString("gdpr.tcf2.purpose4.enforce_algo"), tt.description)
+			assert.Equal(t, tt.wantPurpose5EnforceAlgo, v.GetString("gdpr.tcf2.purpose5.enforce_algo"), tt.description)
+			assert.Equal(t, tt.wantPurpose6EnforceAlgo, v.GetString("gdpr.tcf2.purpose6.enforce_algo"), tt.description)
+			assert.Equal(t, tt.wantPurpose7EnforceAlgo, v.GetString("gdpr.tcf2.purpose7.enforce_algo"), tt.description)
+			assert.Equal(t, tt.wantPurpose8EnforceAlgo, v.GetString("gdpr.tcf2.purpose8.enforce_algo"), tt.description)
+			assert.Equal(t, tt.wantPurpose9EnforceAlgo, v.GetString("gdpr.tcf2.purpose9.enforce_algo"), tt.description)
+			assert.Equal(t, tt.wantPurpose10EnforceAlgo, v.GetString("gdpr.tcf2.purpose10.enforce_algo"), tt.description)
+		} else {
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose1.enforce_algo"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose2.enforce_algo"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose3.enforce_algo"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose4.enforce_algo"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose5.enforce_algo"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose6.enforce_algo"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose7.enforce_algo"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose8.enforce_algo"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose9.enforce_algo"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose10.enforce_algo"), tt.description)
+		}
+
+		if tt.wantEnforcePurposesSet {
+			assert.Equal(t, tt.wantPurpose1EnforcePurpose, v.GetString("gdpr.tcf2.purpose1.enforce_purpose"), tt.description)
+			assert.Equal(t, tt.wantPurpose2EnforcePurpose, v.GetString("gdpr.tcf2.purpose2.enforce_purpose"), tt.description)
+			assert.Equal(t, tt.wantPurpose3EnforcePurpose, v.GetString("gdpr.tcf2.purpose3.enforce_purpose"), tt.description)
+			assert.Equal(t, tt.wantPurpose4EnforcePurpose, v.GetString("gdpr.tcf2.purpose4.enforce_purpose"), tt.description)
+			assert.Equal(t, tt.wantPurpose5EnforcePurpose, v.GetString("gdpr.tcf2.purpose5.enforce_purpose"), tt.description)
+			assert.Equal(t, tt.wantPurpose6EnforcePurpose, v.GetString("gdpr.tcf2.purpose6.enforce_purpose"), tt.description)
+			assert.Equal(t, tt.wantPurpose7EnforcePurpose, v.GetString("gdpr.tcf2.purpose7.enforce_purpose"), tt.description)
+			assert.Equal(t, tt.wantPurpose8EnforcePurpose, v.GetString("gdpr.tcf2.purpose8.enforce_purpose"), tt.description)
+			assert.Equal(t, tt.wantPurpose9EnforcePurpose, v.GetString("gdpr.tcf2.purpose9.enforce_purpose"), tt.description)
+			assert.Equal(t, tt.wantPurpose10EnforcePurpose, v.GetString("gdpr.tcf2.purpose10.enforce_purpose"), tt.description)
+		} else {
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose1.enforce_purpose"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose2.enforce_purpose"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose3.enforce_purpose"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose4.enforce_purpose"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose5.enforce_purpose"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose6.enforce_purpose"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose7.enforce_purpose"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose8.enforce_purpose"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose9.enforce_purpose"), tt.description)
+			assert.Nil(t, v.Get("gdpr.tcf2.purpose10.enforce_purpose"), tt.description)
+		}
+	}
+}
+
 func TestInvalidAdapterEndpointConfig(t *testing.T) {
 	v := viper.New()
 	SetupViper(v, "")
@@ -1241,26 +1611,26 @@ func TestMissingGDPRDefaultValue(t *testing.T) {
 
 func TestInvalidEnforcePurpose(t *testing.T) {
 	cfg, v := newDefaultConfig(t)
-	cfg.GDPR.TCF2.Purpose1.EnforcePurpose = ""
-	cfg.GDPR.TCF2.Purpose2.EnforcePurpose = TCF2NoEnforcement
-	cfg.GDPR.TCF2.Purpose3.EnforcePurpose = TCF2NoEnforcement
-	cfg.GDPR.TCF2.Purpose4.EnforcePurpose = TCF2NoEnforcement
-	cfg.GDPR.TCF2.Purpose5.EnforcePurpose = "invalid1"
-	cfg.GDPR.TCF2.Purpose6.EnforcePurpose = "invalid2"
-	cfg.GDPR.TCF2.Purpose7.EnforcePurpose = TCF2FullEnforcement
-	cfg.GDPR.TCF2.Purpose8.EnforcePurpose = TCF2FullEnforcement
-	cfg.GDPR.TCF2.Purpose9.EnforcePurpose = TCF2FullEnforcement
-	cfg.GDPR.TCF2.Purpose10.EnforcePurpose = "invalid3"
+	cfg.GDPR.TCF2.Purpose1.EnforceAlgo = ""
+	cfg.GDPR.TCF2.Purpose2.EnforceAlgo = TCF2FullEnforcement
+	cfg.GDPR.TCF2.Purpose3.EnforceAlgo = TCF2FullEnforcement
+	cfg.GDPR.TCF2.Purpose4.EnforceAlgo = TCF2FullEnforcement
+	cfg.GDPR.TCF2.Purpose5.EnforceAlgo = "invalid1"
+	cfg.GDPR.TCF2.Purpose6.EnforceAlgo = "invalid2"
+	cfg.GDPR.TCF2.Purpose7.EnforceAlgo = TCF2FullEnforcement
+	cfg.GDPR.TCF2.Purpose8.EnforceAlgo = TCF2FullEnforcement
+	cfg.GDPR.TCF2.Purpose9.EnforceAlgo = TCF2FullEnforcement
+	cfg.GDPR.TCF2.Purpose10.EnforceAlgo = "invalid3"
 
 	errs := cfg.validate(v)
 
 	expectedErrs := []error{
-		errors.New("gdpr.tcf2.purpose1.enforce_purpose must be \"no\" or \"full\". Got "),
-		errors.New("gdpr.tcf2.purpose5.enforce_purpose must be \"no\" or \"full\". Got invalid1"),
-		errors.New("gdpr.tcf2.purpose6.enforce_purpose must be \"no\" or \"full\". Got invalid2"),
-		errors.New("gdpr.tcf2.purpose10.enforce_purpose must be \"no\" or \"full\". Got invalid3"),
+		errors.New("gdpr.tcf2.purpose1.enforce_algo must be \"full\". Got "),
+		errors.New("gdpr.tcf2.purpose5.enforce_algo must be \"full\". Got invalid1"),
+		errors.New("gdpr.tcf2.purpose6.enforce_algo must be \"full\". Got invalid2"),
+		errors.New("gdpr.tcf2.purpose10.enforce_algo must be \"full\". Got invalid3"),
 	}
-	assert.ElementsMatch(t, errs, expectedErrs, "gdpr.tcf2.purposeX.enforce_purpose should prevent invalid values but it doesn't")
+	assert.ElementsMatch(t, errs, expectedErrs, "gdpr.tcf2.purposeX.enforce_algo should prevent invalid values but it doesn't")
 }
 
 func TestNegativeCurrencyConverterFetchInterval(t *testing.T) {
@@ -1513,8 +1883,8 @@ func TestTCF2PurposeEnforced(t *testing.T) {
 	tests := []struct {
 		description          string
 		givePurposeConfigNil bool
-		givePurpose1Enforced string
-		givePurpose2Enforced string
+		givePurpose1Enforced bool
+		givePurpose2Enforced bool
 		givePurpose          consentconstants.Purpose
 		wantEnforced         bool
 	}{
@@ -1525,26 +1895,20 @@ func TestTCF2PurposeEnforced(t *testing.T) {
 			wantEnforced:         false,
 		},
 		{
-			description:          "Purpose 1 Enforced not set",
-			givePurpose1Enforced: "",
-			givePurpose:          1,
-			wantEnforced:         false,
-		},
-		{
-			description:          "Purpose 1 Enforced set to full enforcement",
-			givePurpose1Enforced: TCF2FullEnforcement,
+			description:          "Purpose 1 Enforced set to true",
+			givePurpose1Enforced: true,
 			givePurpose:          1,
 			wantEnforced:         true,
 		},
 		{
-			description:          "Purpose 1 Enforced set to no enforcement",
-			givePurpose1Enforced: TCF2NoEnforcement,
+			description:          "Purpose 1 Enforced set to false",
+			givePurpose1Enforced: false,
 			givePurpose:          1,
 			wantEnforced:         false,
 		},
 		{
-			description:          "Purpose 2 Enforced set to full enforcement",
-			givePurpose2Enforced: TCF2FullEnforcement,
+			description:          "Purpose 2 Enforced set to true",
+			givePurpose2Enforced: true,
 			givePurpose:          2,
 			wantEnforced:         true,
 		},
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index 171b1b24..35cf4f1e 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -1090,6 +1090,13 @@ func TestCookieSyncWriteParseRequestErrorMetrics(t *testing.T) {
 				m.On("RecordCookieSync", metrics.CookieSyncAccountInvalid).Once()
 			},
 		},
+		{
+			description: "Account Malformed",
+			err:         errCookieSyncAccountConfigMalformed,
+			setExpectations: func(m *metrics.MetricsEngineMock) {
+				m.On("RecordCookieSync", metrics.CookieSyncAccountConfigMalformed).Once()
+			},
+		},
 		{
 			description: "No Special Case",
 			err:         errors.New("any error"),
@@ -1706,9 +1713,14 @@ func TestCombineErrors(t *testing.T) {
 			givenErrorList: []error{&errortypes.AcctRequired{}},
 			expectedError:  errCookieSyncAccountInvalid,
 		},
+		{
+			description:    "Special Case: malformed account config",
+			givenErrorList: []error{&errortypes.MalformedAcct{}},
+			expectedError:  errCookieSyncAccountConfigMalformed,
+		},
 		{
 			description:    "Special Case: multiple special cases, first one wins",
-			givenErrorList: []error{&errortypes.BlacklistedAcct{}, &errortypes.AcctRequired{}},
+			givenErrorList: []error{&errortypes.BlacklistedAcct{}, &errortypes.AcctRequired{}, &errortypes.MalformedAcct{}},
 			expectedError:  errCookieSyncAccountBlocked,
 		},
 	}
diff --git a/endpoints/events/account_test.go b/endpoints/events/account_test.go
index b61f29dc..ffe94b81 100644
--- a/endpoints/events/account_test.go
+++ b/endpoints/events/account_test.go
@@ -11,15 +11,17 @@ import (
 
 	"github.com/julienschmidt/httprouter"
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/stored_requests"
 	"github.com/stretchr/testify/assert"
 )
 
 func TestHandleAccountServiceErrors(t *testing.T) {
 	tests := map[string]struct {
-		fetcher *mockAccountsFetcher
-		cfg     *config.Configuration
-		want    struct {
+		fetcher   *mockAccountsFetcher
+		cfg       *config.Configuration
+		accountID string
+		want      struct {
 			code     int
 			response string
 		}
@@ -37,6 +39,7 @@ func TestHandleAccountServiceErrors(t *testing.T) {
 					TimeoutMS: int64(2000), AllowUnknownBidder: false,
 				},
 			},
+			accountID: "testacc",
 			want: struct {
 				code     int
 				response string
@@ -45,6 +48,26 @@ func TestHandleAccountServiceErrors(t *testing.T) {
 				response: "Invalid request: some error\nInvalid request: Prebid-server could not verify the Account ID. Please reach out to the prebid server host.\n",
 			},
 		},
+		"malformedAccountConfig": {
+			fetcher: &mockAccountsFetcher{
+				Fail:  true,
+				Error: &errortypes.MalformedAcct{},
+			},
+			cfg: &config.Configuration{
+				MaxRequestSize: maxSize,
+				VTrack: config.VTrack{
+					TimeoutMS: int64(2000), AllowUnknownBidder: false,
+				},
+			},
+			accountID: "malformed_acct",
+			want: struct {
+				code     int
+				response string
+			}{
+				code:     500,
+				response: "Invalid request: The prebid-server account config for account id \"malformed_acct\" is malformed. Please reach out to the prebid server host.\n",
+			},
+		},
 		"serviceUnavailable": {
 			fetcher: &mockAccountsFetcher{
 				Fail: false,
@@ -56,6 +79,7 @@ func TestHandleAccountServiceErrors(t *testing.T) {
 					TimeoutMS: int64(2000), AllowUnknownBidder: false,
 				},
 			},
+			accountID: "testacc",
 			want: struct {
 				code     int
 				response string
@@ -81,6 +105,7 @@ func TestHandleAccountServiceErrors(t *testing.T) {
 					AllowUnknownBidder: false,
 				},
 			},
+			accountID: "testacc",
 			want: struct {
 				code     int
 				response string
@@ -98,8 +123,8 @@ func TestHandleAccountServiceErrors(t *testing.T) {
 			h    httprouter.Handle
 			r    *http.Request
 		}{
-			vast(t, test.cfg, test.fetcher),
-			event(test.cfg, test.fetcher),
+			vast(t, test.cfg, test.fetcher, test.accountID),
+			event(test.cfg, test.fetcher, test.accountID),
 		}
 
 		for _, handler := range handlers {
@@ -123,7 +148,7 @@ func TestHandleAccountServiceErrors(t *testing.T) {
 	}
 }
 
-func event(cfg *config.Configuration, fetcher stored_requests.AccountFetcher) struct {
+func event(cfg *config.Configuration, fetcher stored_requests.AccountFetcher, accountID string) struct {
 	name string
 	h    httprouter.Handle
 	r    *http.Request
@@ -135,11 +160,11 @@ func event(cfg *config.Configuration, fetcher stored_requests.AccountFetcher) st
 	}{
 		name: "event",
 		h:    NewEventEndpoint(cfg, fetcher, nil),
-		r:    httptest.NewRequest("GET", "/event?t=win&b=test&ts=1234&f=b&x=1&a=testacc", strings.NewReader("")),
+		r:    httptest.NewRequest("GET", "/event?t=win&b=test&ts=1234&f=b&x=1&a="+accountID, strings.NewReader("")),
 	}
 }
 
-func vast(t *testing.T, cfg *config.Configuration, fetcher stored_requests.AccountFetcher) struct {
+func vast(t *testing.T, cfg *config.Configuration, fetcher stored_requests.AccountFetcher, accountID string) struct {
 	name string
 	h    httprouter.Handle
 	r    *http.Request
@@ -156,6 +181,6 @@ func vast(t *testing.T, cfg *config.Configuration, fetcher stored_requests.Accou
 	}{
 		name: "vast",
 		h:    NewVTrackEndpoint(cfg, fetcher, &vtrackMockCacheClient{}, config.BidderInfos{}),
-		r:    httptest.NewRequest("POST", "/vtrack?a=testacc", strings.NewReader(vtrackBody)),
+		r:    httptest.NewRequest("POST", "/vtrack?a="+accountID, strings.NewReader(vtrackBody)),
 	}
 }
diff --git a/endpoints/events/event_test.go b/endpoints/events/event_test.go
index f700c31e..5686fefb 100644
--- a/endpoints/events/event_test.go
+++ b/endpoints/events/event_test.go
@@ -74,6 +74,7 @@ func (e *eventsMockAnalyticsModule) LogNotificationEventObject(ne *analytics.Not
 var mockAccountData = map[string]json.RawMessage{
 	"events_enabled":  json.RawMessage(`{"events_enabled":true}`),
 	"events_disabled": json.RawMessage(`{"events_enabled":false}`),
+	"malformed_acct":  json.RawMessage(`{"events_enabled":"invalid type"}`),
 }
 
 type mockAccountsFetcher struct {
diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go
index 65b736d1..4699a48c 100644
--- a/endpoints/openrtb2/amp_auction_test.go
+++ b/endpoints/openrtb2/amp_auction_test.go
@@ -27,68 +27,159 @@ import (
 
 // TestGoodRequests makes sure that the auction runs properly-formatted stored bids correctly.
 func TestGoodAmpRequests(t *testing.T) {
-	testCases := []struct {
+	testGroups := []struct {
+		desc      string
+		dir       string
+		testFiles []string
+	}{
+		{
+			desc: "Valid whole, tag_id param only",
+			dir:  "sample-requests/amp/valid-supplementary/",
+			testFiles: []string{
+				"aliased-buyeruids.json",
+				"aliases.json",
+				"imp-with-stored-resp.json",
+				"gdpr-no-consentstring.json",
+				"gdpr.json",
+			},
+		},
+		{
+			desc: "Valid, consent handling in query",
+			dir:  "sample-requests/amp/consent-through-query/",
+			testFiles: []string{
+				"gdpr-tcf1-consent-through-query.json",
+				"gdpr-tcf2-consent-through-query.json",
+				"gdpr-legacy-tcf2-consent-through-query.json",
+				"gdpr-ccpa-through-query.json",
+			},
+		},
+	}
+
+	for _, tgroup := range testGroups {
+		for _, filename := range tgroup.testFiles {
+			// Read test case and unmarshal
+			fileJsonData, err := ioutil.ReadFile(tgroup.dir + filename)
+			if !assert.NoError(t, err, "Failed to fetch a valid request: %v. Test file: %s", err, filename) {
+				continue
+			}
+
+			test := testCase{}
+			if !assert.NoError(t, json.Unmarshal(fileJsonData, &test), "Failed to unmarshal data from file: %s. Error: %v", filename, err) {
+				continue
+			}
+
+			// build http request
+			request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?%s", test.Query), nil)
+			recorder := httptest.NewRecorder()
+
+			// build the stored requests and configure endpoint conf
+			query := request.URL.Query()
+			tagID := query.Get("tag_id")
+			if !assert.Greater(t, len(tagID), 0, "AMP test %s file is missing tag_id field", filename) {
+				continue
+			}
+
+			test.storedRequest = map[string]json.RawMessage{tagID: test.BidRequest}
+			test.endpointType = AMP_ENDPOINT
+
+			cfg := &config.Configuration{
+				MaxRequestSize: maxSize,
+				GDPR:           config.GDPR{Enabled: true},
+			}
+			if test.Config != nil {
+				cfg.BlacklistedApps = test.Config.BlacklistedApps
+				cfg.BlacklistedAppMap = test.Config.getBlacklistedAppMap()
+				cfg.BlacklistedAccts = test.Config.BlacklistedAccounts
+				cfg.BlacklistedAcctMap = test.Config.getBlackListedAccountMap()
+				cfg.AccountRequired = test.Config.AccountRequired
+			}
+
+			// Set test up
+			ampEndpoint, ex, mockBidServers, mockCurrencyRatesServer, err := buildTestEndpoint(test, cfg)
+			if !assert.NoError(t, err) {
+				continue
+			}
+
+			// runTestCase
+			ampEndpoint(recorder, request, nil)
+
+			// Close servers
+			for _, mockBidServer := range mockBidServers {
+				mockBidServer.Close()
+			}
+			mockCurrencyRatesServer.Close()
+
+			// Assertions
+			if assert.Equal(t, test.ExpectedReturnCode, recorder.Code, "Expected status %d. Got %d. Amp test file: %s", http.StatusOK, recorder.Code, filename) {
+				if test.ExpectedReturnCode == http.StatusOK {
+					var ampResponse AmpResponse
+					assert.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &ampResponse), "Error unmarshalling ampResponse: %v", err)
+					assert.Equal(t, test.ExpectedAmpResponse, ampResponse, "Not the expected response. Test file: %s", filename)
+				} else {
+					assert.Equal(t, test.ExpectedErrorMessage, recorder.Body.String(), filename)
+				}
+			}
+			if test.ExpectedValidatedBidReq != nil {
+				assert.Equal(t, test.ExpectedValidatedBidReq, ex.actualValidatedBidReq, "Not the expected validated request. Test file: %s", filename)
+			}
+		}
+	}
+}
+
+func TestAccountErrors(t *testing.T) {
+	tests := []struct {
+		description string
 		storedReqID string
 		filename    string
 	}{
-		{"1", "aliased-buyeruids.json"},
-		{"2", "aliases.json"},
-		{"3", "imp-with-stored-resp.json"},
-		{"5", "gdpr-no-consentstring.json"},
-		{"6", "gdpr.json"},
+		{
+			description: "Malformed account config",
+			storedReqID: "1",
+			filename:    "account-malformed/malformed-acct.json",
+		},
+		{
+			description: "Blocked account",
+			storedReqID: "1",
+			filename:    "blacklisted/blacklisted-site-publisher.json",
+		},
 	}
 
-	for _, tc := range testCases {
-		// Read test case and unmarshal
-		fileJsonData, err := ioutil.ReadFile("sample-requests/valid-whole/supplementary/" + tc.filename)
-		if !assert.NoError(t, err, "Failed to fetch a valid request: %v. Test file: %s", err, tc.filename) {
+	for _, tt := range tests {
+		fileJsonData, err := ioutil.ReadFile("sample-requests/" + tt.filename)
+		if !assert.NoError(t, err, "Failed to fetch a valid request: %v. Test file: %s", err, tt.filename) {
 			continue
 		}
 
 		test := testCase{}
-		if !assert.NoError(t, json.Unmarshal(fileJsonData, &test), "Failed to unmarshal data from file: %s. Error: %v", tc.filename, err) {
+		if !assert.NoError(t, json.Unmarshal(fileJsonData, &test), "Failed to unmarshal data from file: %s. Error: %v", tt.filename, err) {
 			continue
 		}
-		test.storedRequest = map[string]json.RawMessage{tc.storedReqID: test.BidRequest}
+		test.storedRequest = map[string]json.RawMessage{tt.storedReqID: test.BidRequest}
 		test.endpointType = AMP_ENDPOINT
 
-		cfg := &config.Configuration{MaxRequestSize: maxSize}
-		if test.Config != nil {
-			cfg.BlacklistedApps = test.Config.BlacklistedApps
-			cfg.BlacklistedAppMap = test.Config.getBlacklistedAppMap()
-			cfg.BlacklistedAccts = test.Config.BlacklistedAccounts
-			cfg.BlacklistedAcctMap = test.Config.getBlackListedAccountMap()
-			cfg.AccountRequired = test.Config.AccountRequired
+		cfg := &config.Configuration{
+			BlacklistedAccts:   []string{"bad_acct"},
+			BlacklistedAcctMap: map[string]bool{"bad_acct": true},
+			MaxRequestSize:     maxSize,
 		}
+		cfg.MarshalAccountDefaults()
 
-		// Set test up
-		ampEndpoint, mockBidServers, mockCurrencyRatesServer, err := buildTestEndpoint(test, cfg)
+		ampEndpoint, _, mockBidServers, mockCurrencyRatesServer, err := buildTestEndpoint(test, cfg)
 		if !assert.NoError(t, err) {
 			continue
 		}
 
-		request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=%s", tc.storedReqID), nil)
+		request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=%s&", tt.storedReqID), nil)
 		recorder := httptest.NewRecorder()
-
-		// runTestCase
 		ampEndpoint(recorder, request, nil)
 
-		// Close servers
 		for _, mockBidServer := range mockBidServers {
 			mockBidServer.Close()
 		}
 		mockCurrencyRatesServer.Close()
 
-		// Assertions
-		if assert.Equal(t, test.ExpectedReturnCode, recorder.Code, "Expected status %d. Got %d. Amp test file: %s", http.StatusOK, recorder.Code, tc.filename) {
-			if test.ExpectedReturnCode == http.StatusOK {
-				var ampResponse AmpResponse
-				assert.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &ampResponse), "Error unmarshalling ampResponse: %v", err)
-				assert.Equal(t, test.ExpectedAmpResponse, ampResponse, "Test file: %s", tc.filename)
-			} else {
-				assert.Equal(t, test.ExpectedErrorMessage, recorder.Body.String(), tc.filename)
-			}
-		}
+		assert.Equal(t, test.ExpectedReturnCode, recorder.Code, "%s: %s", tt.description, tt.filename)
+		assert.Equal(t, test.ExpectedErrorMessage, recorder.Body.String(), "%s: %s", tt.description, tt.filename)
 	}
 }
 
@@ -129,7 +220,7 @@ func TestAMPPageInfo(t *testing.T) {
 }
 
 func TestGDPRConsent(t *testing.T) {
-	consent := "BOu5On0Ou5On0ADACHENAO7pqzAAppY"
+	consent := "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA"
 	existingConsent := "BONV8oqONXwgmADACHENAO7pqzAAppY"
 
 	testCases := []struct {
@@ -205,7 +296,10 @@ func TestGDPRConsent(t *testing.T) {
 			newParamsValidator(t),
 			&mockAmpStoredReqFetcher{stored},
 			empty_fetcher.EmptyFetcher{},
-			&config.Configuration{MaxRequestSize: maxSize},
+			&config.Configuration{
+				MaxRequestSize: maxSize,
+				GDPR:           config.GDPR{Enabled: true},
+			},
 			&metricsConfig.NilMetricsEngine{},
 			analyticsConf.NewPBSAnalytics(&config.Analytics{}),
 			map[string]string{},
@@ -215,7 +309,7 @@ func TestGDPRConsent(t *testing.T) {
 		)
 
 		// Invoke Endpoint
-		request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&consent_string=%s", test.consent), nil)
+		request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&consent_type=2&consent_string=%s", test.consent), nil)
 		responseRecorder := httptest.NewRecorder()
 		endpoint(responseRecorder, request, nil)
 
@@ -246,7 +340,7 @@ func TestGDPRConsent(t *testing.T) {
 		assert.Empty(t, response.Warnings, test.description+":warnings")
 
 		// Invoke Endpoint With Legacy Param
-		requestLegacy := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&gdpr_consent=%s", test.consent), nil)
+		requestLegacy := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&consent_type=2&gdpr_consent=%s", test.consent), nil)
 		responseRecorderLegacy := httptest.NewRecorder()
 		endpoint(responseRecorderLegacy, requestLegacy, nil)
 
@@ -369,7 +463,7 @@ func TestCCPAConsent(t *testing.T) {
 		)
 
 		// Invoke Endpoint
-		request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&consent_string=%s", test.consent), nil)
+		request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&consent_type=3&consent_string=%s", test.consent), nil)
 		responseRecorder := httptest.NewRecorder()
 		endpoint(responseRecorder, request, nil)
 
@@ -415,7 +509,7 @@ func TestConsentWarnings(t *testing.T) {
 	}
 	invalidCCPAWarning := openrtb_ext.ExtBidderMessage{
 		Code:    10001,
-		Message: "Consent '" + invalidConsent + "' is not recognized as either CCPA or GDPR TCF.",
+		Message: "Consent string '" + invalidConsent + "' is not a valid CCPA consent string.",
 	}
 	invalidConsentWarning := openrtb_ext.ExtBidderMessage{
 		Code:    10001,
@@ -484,7 +578,7 @@ func TestConsentWarnings(t *testing.T) {
 		var request *http.Request
 
 		if testCase.invalidConsentURL {
-			request = httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1&consent_string="+invalidConsent, nil)
+			request = httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1&consent_type=3&consent_string="+invalidConsent, nil)
 
 		} else {
 			request = httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1", nil)
@@ -519,8 +613,8 @@ func TestConsentWarnings(t *testing.T) {
 }
 
 func TestNewAndLegacyConsentBothProvided(t *testing.T) {
-	validConsentGDPR1 := "BOu5On0Ou5On0ADACHENAO7pqzAAppY"
-	validConsentGDPR2 := "BONV8oqONXwgmADACHENAO7pqzAAppY"
+	validConsentGDPR1 := "COwGVJOOwGVJOADACHENAOCAAO6as_-AAAhoAFNLAAoAAAA"
+	validConsentGDPR2 := "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA"
 
 	testCases := []struct {
 		description     string
@@ -565,7 +659,10 @@ func TestNewAndLegacyConsentBothProvided(t *testing.T) {
 			newParamsValidator(t),
 			&mockAmpStoredReqFetcher{stored},
 			empty_fetcher.EmptyFetcher{},
-			&config.Configuration{MaxRequestSize: maxSize},
+			&config.Configuration{
+				MaxRequestSize: maxSize,
+				GDPR:           config.GDPR{Enabled: true},
+			},
 			&metricsConfig.NilMetricsEngine{},
 			analyticsConf.NewPBSAnalytics(&config.Analytics{}),
 			map[string]string{},
@@ -575,7 +672,7 @@ func TestNewAndLegacyConsentBothProvided(t *testing.T) {
 		)
 
 		// Invoke Endpoint
-		request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&consent_string=%s&gdpr_consent=%s", test.consent, test.consentLegacy), nil)
+		request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&consent_type=2&consent_string=%s&gdpr_consent=%s", test.consent, test.consentLegacy), nil)
 		responseRecorder := httptest.NewRecorder()
 		endpoint(responseRecorder, request, nil)
 
diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go
index 9c83b24b..95fd7842 100644
--- a/endpoints/openrtb2/auction_benchmark_test.go
+++ b/endpoints/openrtb2/auction_benchmark_test.go
@@ -3,7 +3,6 @@ package openrtb2
 import (
 	"bytes"
 	"fmt"
-	"github.com/prebid/prebid-server/experiment/adscert"
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
@@ -11,6 +10,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/prebid/prebid-server/experiment/adscert"
+
 	analyticsConf "github.com/prebid/prebid-server/analytics/config"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/currency"
@@ -155,7 +156,7 @@ func BenchmarkValidWholeExemplary(b *testing.B) {
 				AccountRequired:    test.Config.AccountRequired,
 			}
 
-			auctionEndpointHandler, mockBidServers, mockCurrencyRatesServer, err := buildTestEndpoint(test, cfg)
+			auctionEndpointHandler, _, mockBidServers, mockCurrencyRatesServer, err := buildTestEndpoint(test, cfg)
 			if err != nil {
 				b.Fatal(err.Error())
 			}
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index 54d326c4..4b16379a 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -59,6 +59,10 @@ func TestJsonSampleRequests(t *testing.T) {
 			"Makes sure we handle (default) aliased bidders properly",
 			"aliased",
 		},
+		{
+			"Asserts we return 500s on requests referencing accounts with malformed configs.",
+			"account-malformed",
+		},
 		{
 			"Asserts we return 503s on requests with blacklisted accounts and apps.",
 			"blacklisted",
@@ -122,9 +126,10 @@ func TestJsonSampleRequests(t *testing.T) {
 						cfg.BlacklistedAcctMap = test.Config.getBlackListedAccountMap()
 						cfg.AccountRequired = test.Config.AccountRequired
 					}
+					cfg.MarshalAccountDefaults()
 					test.endpointType = OPENRTB_ENDPOINT
 
-					auctionEndpointHandler, mockBidServers, mockCurrencyRatesServer, err := buildTestEndpoint(test, cfg)
+					auctionEndpointHandler, _, mockBidServers, mockCurrencyRatesServer, err := buildTestEndpoint(test, cfg)
 					if assert.NoError(t, err) {
 						runTestCase(t, auctionEndpointHandler, test, fileData, testFile)
 					}
diff --git a/endpoints/openrtb2/test_utils.go b/endpoints/openrtb2/test_utils.go
index c5410cbd..db70852c 100644
--- a/endpoints/openrtb2/test_utils.go
+++ b/endpoints/openrtb2/test_utils.go
@@ -58,12 +58,14 @@ const (
 
 type testCase struct {
 	// Common
-	endpointType         int
-	Description          string            `json:"description"`
-	Config               *testConfigValues `json:"config"`
-	BidRequest           json.RawMessage   `json:"mockBidRequest"`
-	ExpectedReturnCode   int               `json:"expectedReturnCode,omitempty"`
-	ExpectedErrorMessage string            `json:"expectedErrorMessage"`
+	endpointType            int
+	Description             string               `json:"description"`
+	Config                  *testConfigValues    `json:"config"`
+	BidRequest              json.RawMessage      `json:"mockBidRequest"`
+	ExpectedValidatedBidReq *openrtb2.BidRequest `json:"expectedValidatedBidRequest"`
+	ExpectedReturnCode      int                  `json:"expectedReturnCode,omitempty"`
+	ExpectedErrorMessage    string               `json:"expectedErrorMessage"`
+	Query                   string               `json:"query"`
 
 	// "/openrtb2/auction" endpoint JSON test info
 	ExpectedBidResponse json.RawMessage `json:"expectedBidResponse"`
@@ -1175,6 +1177,26 @@ func (tc *testConfigValues) getAdaptersConfigMap() map[string]config.Adapter {
 	return adaptersConfig
 }
 
+// exchangeTestWrapper is a wrapper that asserts the openrtb2 bid request just before the HoldAuction call
+type exchangeTestWrapper struct {
+	ex                    exchange.Exchange
+	actualValidatedBidReq *openrtb2.BidRequest
+}
+
+func (te *exchangeTestWrapper) HoldAuction(ctx context.Context, r exchange.AuctionRequest, debugLog *exchange.DebugLog) (*openrtb2.BidResponse, error) {
+
+	// rebuild/resync the request in the request wrapper.
+	if err := r.BidRequestWrapper.RebuildRequest(); err != nil {
+		return nil, err
+	}
+
+	// Save the validated bidRequest that we are about to feed HoldAuction
+	te.actualValidatedBidReq = r.BidRequestWrapper.BidRequest
+
+	// Call HoldAuction() implementation as written in the exchange package
+	return te.ex.HoldAuction(ctx, r, debugLog)
+}
+
 // buildTestExchange returns an exchange with mock bidder servers and mock currency convertion server
 func buildTestExchange(testCfg *testConfigValues, adapterMap map[openrtb_ext.BidderName]exchange.AdaptedBidder, mockBidServersArray []*httptest.Server, mockCurrencyRatesServer *httptest.Server, bidderInfos config.BidderInfos, cfg *config.Configuration, met metrics.MetricsEngine, mockFetcher stored_requests.CategoryFetcher) (exchange.Exchange, []*httptest.Server) {
 	if len(testCfg.MockBidders) == 0 {
@@ -1199,7 +1221,7 @@ func buildTestExchange(testCfg *testConfigValues, adapterMap map[openrtb_ext.Bid
 		cfg: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
 	}.Builder
 
-	return exchange.NewExchange(adapterMap,
+	testExchange := exchange.NewExchange(adapterMap,
 		&wellBehavedCache{},
 		cfg,
 		nil,
@@ -1210,11 +1232,17 @@ func buildTestExchange(testCfg *testConfigValues, adapterMap map[openrtb_ext.Bid
 		mockCurrencyConverter,
 		mockFetcher,
 		&adscert.NilSigner{},
-	), mockBidServersArray
+	)
+
+	testExchange = &exchangeTestWrapper{
+		ex: testExchange,
+	}
+
+	return testExchange, mockBidServersArray
 }
 
 // buildTestEndpoint instantiates an openrtb2 Auction endpoint designed to test endpoints/openrtb2/auction.go
-func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Handle, []*httptest.Server, *httptest.Server, error) {
+func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Handle, *exchangeTestWrapper, []*httptest.Server, *httptest.Server, error) {
 	if test.Config == nil {
 		test.Config = &testConfigValues{}
 	}
@@ -1224,7 +1252,7 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han
 		var err error
 		paramValidator, err = openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
 		if err != nil {
-			return nil, nil, nil, err
+			return nil, nil, nil, nil, err
 		}
 	} else {
 		paramValidator = mockBidderParamValidator{}
@@ -1248,7 +1276,7 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han
 	}
 	mockCurrencyRatesServer := httptest.NewServer(http.HandlerFunc(mockCurrencyConversionService.handle))
 
-	ex, mockBidServersArray := buildTestExchange(test.Config, adapterMap, mockBidServersArray, mockCurrencyRatesServer, bidderInfos, cfg, met, mockFetcher)
+	testExchange, mockBidServersArray := buildTestExchange(test.Config, adapterMap, mockBidServersArray, mockCurrencyRatesServer, bidderInfos, cfg, met, mockFetcher)
 
 	var storedRequestFetcher stored_requests.Fetcher
 	if len(test.storedRequest) > 0 {
@@ -1264,6 +1292,13 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han
 		storedResponseFetcher = empty_fetcher.EmptyFetcher{}
 	}
 
+	var accountFetcher stored_requests.AccountFetcher
+	accountFetcher = &mockAccountFetcher{
+		data: map[string]json.RawMessage{
+			"malformed_acct": json.RawMessage(`{"disabled":"invalid type"}`),
+		},
+	}
+
 	var endpointBuilder func(uuidutil.UUIDGenerator, exchange.Exchange, openrtb_ext.BidderParamValidator, stored_requests.Fetcher, stored_requests.AccountFetcher, *config.Configuration, metrics.MetricsEngine, analytics.PBSAnalyticsModule, map[string]string, []byte, map[string]openrtb_ext.BidderName, stored_requests.Fetcher) (httprouter.Handle, error)
 
 	switch test.endpointType {
@@ -1275,10 +1310,10 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han
 
 	endpoint, err := endpointBuilder(
 		fakeUUIDGenerator{},
-		ex,
+		testExchange,
 		paramValidator,
 		storedRequestFetcher,
-		mockFetcher,
+		accountFetcher,
 		cfg,
 		met,
 		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
@@ -1288,7 +1323,7 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han
 		storedResponseFetcher,
 	)
 
-	return endpoint, mockBidServersArray, mockCurrencyRatesServer, err
+	return endpoint, testExchange.(*exchangeTestWrapper), mockBidServersArray, mockCurrencyRatesServer, err
 }
 
 type mockBidderParamValidator struct{}
@@ -1298,6 +1333,17 @@ func (v mockBidderParamValidator) Validate(name openrtb_ext.BidderName, ext json
 }
 func (v mockBidderParamValidator) Schema(name openrtb_ext.BidderName) string { return "" }
 
+type mockAccountFetcher struct {
+	data map[string]json.RawMessage
+}
+
+func (af *mockAccountFetcher) FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) {
+	if account, ok := af.data[accountID]; ok {
+		return account, nil
+	}
+	return nil, []error{stored_requests.NotFoundError{ID: accountID, DataType: "Account"}}
+}
+
 type mockAmpStoredReqFetcher struct {
 	data map[string]json.RawMessage
 }
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index 988bf706..f7f1af18 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -14,6 +14,7 @@ import (
 	"github.com/prebid/prebid-server/analytics"
 	analyticsConf "github.com/prebid/prebid-server/analytics/config"
 	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/exchange"
 	"github.com/prebid/prebid-server/metrics"
 	metricsConfig "github.com/prebid/prebid-server/metrics/config"
@@ -810,30 +811,77 @@ func TestMergeOpenRTBToVideoRequest(t *testing.T) {
 }
 
 func TestHandleError(t *testing.T) {
-	vo := analytics.VideoObject{
-		Status: 200,
-		Errors: make([]error, 0),
+	tests := []struct {
+		description       string
+		giveErrors        []error
+		wantCode          int
+		wantMetricsStatus metrics.RequestStatus
+	}{
+		{
+			description: "Blocked account - return 503 with blocked metrics status",
+			giveErrors: []error{
+				&errortypes.BlacklistedAcct{},
+			},
+			wantCode:          503,
+			wantMetricsStatus: metrics.RequestStatusBlacklisted,
+		},
+		{
+			description: "Blocked app - return 503 with blocked metrics status",
+			giveErrors: []error{
+				&errortypes.BlacklistedApp{},
+			},
+			wantCode:          503,
+			wantMetricsStatus: metrics.RequestStatusBlacklisted,
+		},
+		{
+			description: "Account required error - return 400 with bad input metrics status",
+			giveErrors: []error{
+				&errortypes.AcctRequired{},
+			},
+			wantCode:          400,
+			wantMetricsStatus: metrics.RequestStatusBadInput,
+		},
+		{
+			description: "Malformed account config error - return 500 with account config error metrics status",
+			giveErrors: []error{
+				&errortypes.MalformedAcct{},
+			},
+			wantCode:          500,
+			wantMetricsStatus: metrics.RequestStatusAccountConfigErr,
+		},
+		{
+			description: "Multiple generic errors - return 500 with generic error metrics status",
+			giveErrors: []error{
+				errors.New("Error for testing handleError 1"),
+				errors.New("Error for testing handleError 2"),
+			},
+			wantCode:          500,
+			wantMetricsStatus: metrics.RequestStatusErr,
+		},
 	}
 
-	labels := metrics.Labels{
-		Source:        metrics.DemandUnknown,
-		RType:         metrics.ReqTypeVideo,
-		PubID:         metrics.PublisherUnknown,
-		CookieFlag:    metrics.CookieFlagUnknown,
-		RequestStatus: metrics.RequestStatusOK,
-	}
+	for _, tt := range tests {
+		vo := analytics.VideoObject{
+			Status: 200,
+			Errors: make([]error, 0),
+		}
 
-	recorder := httptest.NewRecorder()
-	err1 := errors.New("Error for testing handleError 1")
-	err2 := errors.New("Error for testing handleError 2")
-	handleError(&labels, recorder, []error{err1, err2}, &vo, nil)
+		labels := metrics.Labels{
+			Source:        metrics.DemandUnknown,
+			RType:         metrics.ReqTypeVideo,
+			PubID:         metrics.PublisherUnknown,
+			CookieFlag:    metrics.CookieFlagUnknown,
+			RequestStatus: metrics.RequestStatusOK,
+		}
 
-	assert.Equal(t, metrics.RequestStatusErr, labels.RequestStatus, "labels.RequestStatus should indicate an error")
-	assert.Equal(t, 500, recorder.Code, "Error status should be written to writer")
-	assert.Equal(t, 500, vo.Status, "Analytics object should have error status")
-	assert.Equal(t, 2, len(vo.Errors), "New errors should be appended to Analytics object Errors")
-	assert.Equal(t, "Error for testing handleError 1", vo.Errors[0].Error(), "Error in Analytics object should have test error message for first error")
-	assert.Equal(t, "Error for testing handleError 2", vo.Errors[1].Error(), "Error in Analytics object should have test error message for second error")
+		recorder := httptest.NewRecorder()
+		handleError(&labels, recorder, tt.giveErrors, &vo, nil)
+
+		assert.Equal(t, tt.wantMetricsStatus, labels.RequestStatus, tt.description)
+		assert.Equal(t, tt.wantCode, recorder.Code, tt.description)
+		assert.Equal(t, tt.wantCode, vo.Status, tt.description)
+		assert.ElementsMatch(t, tt.giveErrors, vo.Errors, tt.description)
+	}
 }
 
 func TestHandleErrorMetrics(t *testing.T) {
@@ -1176,7 +1224,7 @@ func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *m
 		mockBidderParamValidator{},
 		&mockVideoStoredReqFetcher{},
 		&mockVideoStoredReqFetcher{},
-		empty_fetcher.EmptyFetcher{},
+		&mockAccountFetcher{data: mockVideoAccountData},
 		&config.Configuration{MaxRequestSize: maxSize},
 		metrics,
 		mockModule,
@@ -1220,7 +1268,7 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 		mockBidderParamValidator{},
 		&mockVideoStoredReqFetcher{},
 		&mockVideoStoredReqFetcher{},
-		empty_fetcher.EmptyFetcher{},
+		&mockAccountFetcher{data: mockVideoAccountData},
 		&config.Configuration{MaxRequestSize: maxSize},
 		&metricsConfig.NilMetricsEngine{},
 		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
@@ -1393,6 +1441,12 @@ func (m *mockExchangeVideoNoBids) HoldAuction(ctx context.Context, r exchange.Au
 	}, nil
 }
 
+var mockVideoAccountData = map[string]json.RawMessage{
+	"valid_acct":     json.RawMessage(`{"disabled":false}`),
+	"disabled_acct":  json.RawMessage(`{"disabled":true}`),
+	"malformed_acct": json.RawMessage(`{"disabled":"invalid type"}`),
+}
+
 var testVideoStoredImpData = map[string]json.RawMessage{
 	"fba10607-0c12-43d1-ad07-b8a513bc75d6": json.RawMessage(`{"ext": {"appnexus": {"placementId": 14997137}}}`),
 	"8b452b41-2681-4a20-9086-6f16ffad7773": json.RawMessage(`{"ext": {"appnexus": {"placementId": 15016213}}}`),
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 295eeaf1..94e4e09b 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -503,6 +503,28 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 			gdprAllowsHostCookies:  true,
 			cfgAccountRequired:     true,
 			expectedResponseCode:   400,
+			expectedMetrics: func(m *metrics.MetricsEngineMock) {
+				m.On("RecordSetUid", metrics.SetUidAccountConfigMalformed).Once()
+			},
+			expectedAnalytics: func(a *MockAnalytics) {
+				expected := analytics.SetUIDObject{
+					Status:  400,
+					Bidder:  "pubmatic",
+					UID:     "",
+					Errors:  []error{errCookieSyncAccountConfigMalformed},
+					Success: false,
+				}
+				a.On("LogSetUIDObject", &expected).Once()
+			},
+		},
+		{
+			description:            "Invalid JSON account",
+			uri:                    "/setuid?bidder=pubmatic&uid=123&account=invalid_json_acct",
+			cookies:                []*usersync.Cookie{},
+			syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"},
+			gdprAllowsHostCookies:  true,
+			cfgAccountRequired:     true,
+			expectedResponseCode:   400,
 			expectedMetrics: func(m *metrics.MetricsEngineMock) {
 				m.On("RecordSetUid", metrics.SetUidBadRequest).Once()
 			},
@@ -511,7 +533,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) {
 					Status:  400,
 					Bidder:  "pubmatic",
 					UID:     "",
-					Errors:  []error{errors.New("json: cannot unmarshal string into Go struct field Account.disabled of type bool")},
+					Errors:  []error{errors.New("Invalid JSON Patch")},
 					Success: false,
 				}
 				a.On("LogSetUIDObject", &expected).Once()
@@ -713,9 +735,10 @@ func doRequest(req *http.Request, analytics analytics.PBSAnalyticsModule, metric
 	}
 
 	fakeAccountsFetcher := FakeAccountsFetcher{AccountData: map[string]json.RawMessage{
-		"valid_acct":     json.RawMessage(`{"disabled":false}`),
-		"disabled_acct":  json.RawMessage(`{"disabled":true}`),
-		"malformed_acct": json.RawMessage(`{"disabled":"malformed"}`),
+		"valid_acct":        json.RawMessage(`{"disabled":false}`),
+		"disabled_acct":     json.RawMessage(`{"disabled":true}`),
+		"malformed_acct":    json.RawMessage(`{"disabled":"malformed"}`),
+		"invalid_json_acct": json.RawMessage(`{"}`),
 	}}
 
 	endpoint := NewSetUIDEndpoint(&cfg, syncersByBidder, gdprPermsBuilder, tcf2ConfigBuilder, analytics, fakeAccountsFetcher, metrics)
diff --git a/gdpr/aggregated_config_test.go b/gdpr/aggregated_config_test.go
index 59de5dae..a2f9945d 100644
--- a/gdpr/aggregated_config_test.go
+++ b/gdpr/aggregated_config_test.go
@@ -89,47 +89,50 @@ func TestIntegrationEnabled(t *testing.T) {
 }
 
 func TestPurposeEnforced(t *testing.T) {
+	False := false
+	True := true
+
 	tests := []struct {
 		description                    string
-		givePurpose1HostEnforcement    string
-		givePurpose1AccountEnforcement string
-		givePurpose2HostEnforcement    string
-		givePurpose2AccountEnforcement string
+		givePurpose1HostEnforcement    bool
+		givePurpose1AccountEnforcement *bool
+		givePurpose2HostEnforcement    bool
+		givePurpose2AccountEnforcement *bool
 		givePurpose                    consentconstants.Purpose
 		wantEnforced                   bool
 	}{
 		{
 			description:                    "Purpose 1 set at account level - use account setting false",
-			givePurpose1HostEnforcement:    config.TCF2FullEnforcement,
-			givePurpose1AccountEnforcement: config.TCF2NoEnforcement,
+			givePurpose1HostEnforcement:    true,
+			givePurpose1AccountEnforcement: &False,
 			givePurpose:                    1,
 			wantEnforced:                   false,
 		},
 		{
 			description:                    "Purpose 1 set at account level - use account setting true",
-			givePurpose1HostEnforcement:    config.TCF2NoEnforcement,
-			givePurpose1AccountEnforcement: config.TCF2FullEnforcement,
+			givePurpose1HostEnforcement:    false,
+			givePurpose1AccountEnforcement: &True,
 			givePurpose:                    1,
 			wantEnforced:                   true,
 		},
 		{
 			description:                    "Purpose 1 not set at account level - use host setting false",
-			givePurpose1HostEnforcement:    config.TCF2NoEnforcement,
-			givePurpose1AccountEnforcement: "",
+			givePurpose1HostEnforcement:    false,
+			givePurpose1AccountEnforcement: nil,
 			givePurpose:                    1,
 			wantEnforced:                   false,
 		},
 		{
 			description:                    "Purpose 1 not set at account level - use host setting true",
-			givePurpose1HostEnforcement:    config.TCF2FullEnforcement,
-			givePurpose1AccountEnforcement: "",
+			givePurpose1HostEnforcement:    true,
+			givePurpose1AccountEnforcement: nil,
 			givePurpose:                    1,
 			wantEnforced:                   true,
 		},
 		{
 			description:                    "Some other purpose set at account level - use account setting true",
-			givePurpose2HostEnforcement:    config.TCF2NoEnforcement,
-			givePurpose2AccountEnforcement: config.TCF2FullEnforcement,
+			givePurpose2HostEnforcement:    false,
+			givePurpose2AccountEnforcement: &True,
 			givePurpose:                    2,
 			wantEnforced:                   true,
 		},
diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go
index 8aa0d1ed..1a4e9c49 100644
--- a/gdpr/impl_test.go
+++ b/gdpr/impl_test.go
@@ -66,7 +66,7 @@ func TestAllowedSyncs(t *testing.T) {
 	tcf2AggConfig := tcf2Config{
 		HostConfig: config.TCF2{
 			Purpose1: config.TCF2Purpose{
-				EnforcePurpose: config.TCF2FullEnforcement,
+				EnforcePurpose: true,
 				EnforceVendors: true,
 			},
 		},
@@ -112,7 +112,7 @@ func TestProhibitedPurposes(t *testing.T) {
 	tcf2AggConfig := tcf2Config{
 		HostConfig: config.TCF2{
 			Purpose1: config.TCF2Purpose{
-				EnforcePurpose: config.TCF2FullEnforcement,
+				EnforcePurpose: true,
 			},
 		},
 	}
@@ -157,7 +157,7 @@ func TestProhibitedVendors(t *testing.T) {
 	tcf2AggConfig := tcf2Config{
 		HostConfig: config.TCF2{
 			Purpose1: config.TCF2Purpose{
-				EnforcePurpose: config.TCF2FullEnforcement,
+				EnforcePurpose: true,
 				EnforceVendors: true,
 			},
 		},
@@ -373,16 +373,16 @@ func allPurposesEnabledTCF2Config() (TCF2AggConfig tcf2Config) {
 	TCF2AggConfig = tcf2Config{
 		HostConfig: config.TCF2{
 			Enabled:         true,
-			Purpose1:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
-			Purpose2:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
-			Purpose3:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
-			Purpose4:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
-			Purpose5:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
-			Purpose6:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
-			Purpose7:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
-			Purpose8:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
-			Purpose9:        config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
-			Purpose10:       config.TCF2Purpose{EnforcePurpose: config.TCF2FullEnforcement, EnforceVendors: true},
+			Purpose1:        config.TCF2Purpose{EnforcePurpose: true, EnforceVendors: true},
+			Purpose2:        config.TCF2Purpose{EnforcePurpose: true, EnforceVendors: true},
+			Purpose3:        config.TCF2Purpose{EnforcePurpose: true, EnforceVendors: true},
+			Purpose4:        config.TCF2Purpose{EnforcePurpose: true, EnforceVendors: true},
+			Purpose5:        config.TCF2Purpose{EnforcePurpose: true, EnforceVendors: true},
+			Purpose6:        config.TCF2Purpose{EnforcePurpose: true, EnforceVendors: true},
+			Purpose7:        config.TCF2Purpose{EnforcePurpose: true, EnforceVendors: true},
+			Purpose8:        config.TCF2Purpose{EnforcePurpose: true, EnforceVendors: true},
+			Purpose9:        config.TCF2Purpose{EnforcePurpose: true, EnforceVendors: true},
+			Purpose10:       config.TCF2Purpose{EnforcePurpose: true, EnforceVendors: true},
 			SpecialFeature1: config.TCF2SpecialFeature{Enforce: true},
 		},
 		AccountConfig: config.AccountGDPR{
@@ -803,7 +803,7 @@ func TestAllowActivitiesBidRequests(t *testing.T) {
 
 	testDefs := []struct {
 		description            string
-		purpose2EnforcePurpose string
+		purpose2EnforcePurpose bool
 		purpose2EnforceVendors bool
 		bidder                 openrtb_ext.BidderName
 		bidderCoreName         openrtb_ext.BidderName
@@ -815,7 +815,7 @@ func TestAllowActivitiesBidRequests(t *testing.T) {
 	}{
 		{
 			description:            "Bid blocked - p2 enabled, user consents to p2 but not vendor, vendor consents to p2",
-			purpose2EnforcePurpose: config.TCF2FullEnforcement,
+			purpose2EnforcePurpose: true,
 			purpose2EnforceVendors: true,
 			bidder:                 openrtb_ext.BidderPubmatic,
 			bidderCoreName:         openrtb_ext.BidderPubmatic,
@@ -826,7 +826,7 @@ func TestAllowActivitiesBidRequests(t *testing.T) {
 		},
 		{
 			description:            "Bid allowed - p2 enabled, user consents to p2 and vendor, alias vendor consents to p2",
-			purpose2EnforcePurpose: config.TCF2FullEnforcement,
+			purpose2EnforcePurpose: true,
 			purpose2EnforceVendors: true,
 			bidder:                 "pubmatic1",
 			bidderCoreName:         openrtb_ext.BidderPubmatic,
@@ -838,7 +838,7 @@ func TestAllowActivitiesBidRequests(t *testing.T) {
 		},
 		{
 			description:            "Bid blocked - p2 enabled, user consents to p2 and vendor, alias vendor does not consent to p2",
-			purpose2EnforcePurpose: config.TCF2FullEnforcement,
+			purpose2EnforcePurpose: true,
 			purpose2EnforceVendors: true,
 			bidder:                 "pubmatic1",
 			bidderCoreName:         openrtb_ext.BidderPubmatic,
@@ -850,7 +850,7 @@ func TestAllowActivitiesBidRequests(t *testing.T) {
 		},
 		{
 			description:            "Bid allowed - p2 enabled not enforcing vendors, user consents to p2 but not vendor, vendor consents to p2",
-			purpose2EnforcePurpose: config.TCF2FullEnforcement,
+			purpose2EnforcePurpose: true,
 			purpose2EnforceVendors: false,
 			bidder:                 openrtb_ext.BidderPubmatic,
 			bidderCoreName:         openrtb_ext.BidderPubmatic,
@@ -861,7 +861,7 @@ func TestAllowActivitiesBidRequests(t *testing.T) {
 		},
 		{
 			description:            "Bid allowed - p2 disabled, user consents to p2 but not vendor, vendor consents to p2",
-			purpose2EnforcePurpose: config.TCF2NoEnforcement,
+			purpose2EnforcePurpose: false,
 			purpose2EnforceVendors: true,
 			bidder:                 openrtb_ext.BidderPubmatic,
 			bidderCoreName:         openrtb_ext.BidderPubmatic,
@@ -872,7 +872,7 @@ func TestAllowActivitiesBidRequests(t *testing.T) {
 		},
 		{
 			description:            "Bid allowed - p2 enabled, user consents to p2 and vendor, vendor consents to p2",
-			purpose2EnforcePurpose: config.TCF2FullEnforcement,
+			purpose2EnforcePurpose: true,
 			purpose2EnforceVendors: true,
 			bidder:                 openrtb_ext.BidderPubmatic,
 			bidderCoreName:         openrtb_ext.BidderPubmatic,
@@ -883,7 +883,7 @@ func TestAllowActivitiesBidRequests(t *testing.T) {
 		},
 		{
 			description:            "Bid blocked - p2 enabled, user consents to p2 LI but not vendor, vendor consents to p2",
-			purpose2EnforcePurpose: config.TCF2FullEnforcement,
+			purpose2EnforcePurpose: true,
 			purpose2EnforceVendors: true,
 			bidder:                 openrtb_ext.BidderRubicon,
 			bidderCoreName:         openrtb_ext.BidderRubicon,
@@ -894,7 +894,7 @@ func TestAllowActivitiesBidRequests(t *testing.T) {
 		},
 		{
 			description:            "Bid allowed - p2 enabled, user consents to p2 LI and vendor, vendor consents to p2",
-			purpose2EnforcePurpose: config.TCF2FullEnforcement,
+			purpose2EnforcePurpose: true,
 			purpose2EnforceVendors: true,
 			bidder:                 openrtb_ext.BidderRubicon,
 			bidderCoreName:         openrtb_ext.BidderRubicon,
@@ -905,7 +905,7 @@ func TestAllowActivitiesBidRequests(t *testing.T) {
 		},
 		{
 			description:            "Bid allowed - p2 enabled not enforcing vendors, user consents to p2 LI but not vendor, vendor consents to p2",
-			purpose2EnforcePurpose: config.TCF2FullEnforcement,
+			purpose2EnforcePurpose: true,
 			purpose2EnforceVendors: false,
 			bidder:                 openrtb_ext.BidderPubmatic,
 			bidderCoreName:         openrtb_ext.BidderPubmatic,
diff --git a/privacy/gdpr/consentwriter_test.go b/privacy/gdpr/consentwriter_test.go
index 01d66f73..c64dcfa7 100644
--- a/privacy/gdpr/consentwriter_test.go
+++ b/privacy/gdpr/consentwriter_test.go
@@ -88,7 +88,7 @@ func TestConsentWriter(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		writer := ConsentWriter{test.consent}
+		writer := ConsentWriter{test.consent, nil}
 		err := writer.Write(test.request)
 
 		if test.expectedError {