Repository: prebid/prebid-server
Pull request #1743 (2021-03-08 06:55:22)

validateNativeContextTypes function test cases
This PR adds test case coverage to function `validateNativeContextTypes` found in `endpoints/openrtb2/auction.go`


Patch:
diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml
new file mode 100644
index 00000000..720e2f04
--- /dev/null
+++ b/.github/release-drafter.yml
@@ -0,0 +1,9 @@
+change-template: '* $TITLE (#$NUMBER)'
+template: |
+  ## Changes
+
+  $CHANGES
+
+  ## Contributors
+  
+  $CONTRIBUTORS
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..fb9b6592
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,26 @@
+name: Release
+
+on:
+  push:
+    tags:
+      - '[0-9]+.[0-9]+.[0-9]+'
+
+jobs:
+  release:
+    name: Create Release
+    if: github.event.base_ref == 'refs/heads/master'
+    runs-on: ubuntu-latest
+    steps:
+      - name: Get Version
+        id: get_version
+        run: echo ::set-output name=version::${GITHUB_REF/refs\/tags\//}
+
+      - name: Create & Publish Release
+        uses: release-drafter/release-drafter@v5.12.1
+        with:
+          name: ${{ steps.get_version.outputs.version }}
+          tag: ${{ steps.get_version.outputs.version }}
+          version: ${{ steps.get_version.outputs.version }}
+          publish: true
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 97d8cea4..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-language: go
-
-go:
-  - '1.14.2'
-  - '1.15'
-
-go_import_path: github.com/prebid/prebid-server
-
-env:
-- GO111MODULE=on
-
-script:
-  - "./validate.sh --nofmt --cov --race 10"
-
-before_deploy:
-  - go get github.com/mitchellh/gox
-  - gox -os="linux" -arch="386" -output="{{.Dir}}_{{.OS}}_{{.Arch}}" -ldflags "-X main.Rev=`git rev-parse --short HEAD`" -verbose ./...;
-
-deploy:
-  provider: releases
-  skip_cleanup: true
-  api_key:
-    secure: TSJcbIpg2zTJuzUXwv0Un5DPztDTeIKQ2BuuO9KiWYY3Td/nKn0flTYE6B5O6iVqE96HKyj2j0W51rhnRTNDReRZv76L+YXLTJOTQEEQY/A+7XaUXRT0KIbr1EHaeU+4uPJe/8YXxq+nFNeqOjj+LY457WbvnQTIbraAmCgi4yNq4JR+J9BoCELkX0SnU7oq+brq9tJNL3V+7EHIVH6ZLa1lWOrapMnbrVils8gwzWR8XpbdaI+Sn30AGOFKZ0WE2ojZkZb8oZxyX0HKarIiykfZUUzRhlXlTJ0D81QOdc5AtPNR/2dqUXsUE8mRav9R3AJM2BCS2pnP29orCRQU/kxS/mRfx2oZhkr+OHPsNbJcGNSbqNKlM13bX2nL1ZJsJ6xL0VrkBFYlI01SWR12CT9DhZSqTmGPNEkt3fdzwuYtkJNfthb9e9obscnmJEHPSiZRv9dv/stP5LVJJHfFdrzM4+Qo3MCxLNOhmc+p93gsZPeuDGDlx8Tqv1KpN7sp0glbmOwyFAwbCVh5can/JPIAKsQi9VRyZAJvn+7sqqZCExN4TvFArq7pe0LjIVHUQZP9g/vS8HobQnPutmGxf8HqzVVEBnjMsXuiY4cVRecXVRM7crfJjLGr2e9ywIkUZMSD+bRkbRUZ0QQQPvWtcgRw5JmLKG9jDklj8BDkON8=
-  file:
-    - prebid-server_linux_386
-  on:
-    repo: prebid/prebid-server
-    tags: true
-    branch:
-      - master
diff --git a/README.md b/README.md
index 32bf4575..a619533e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
-[![Build Status](https://travis-ci.org/prebid/prebid-server.svg?branch=master)](https://travis-ci.org/prebid/prebid-server)
+[![Build](https://img.shields.io/github/workflow/status/prebid/prebid-server/Validate/master?style=flat-square)](https://github.com/prebid/prebid-server/actions/workflows/validate.yml)
 [![Go Report Card](https://goreportcard.com/badge/github.com/prebid/prebid-server?style=flat-square)](https://goreportcard.com/report/github.com/prebid/prebid-server)
+![Go Version](https://img.shields.io/github/go-mod/go-version/prebid/prebid-server?style=flat-square)
 
 # Prebid Server
 
diff --git a/adapters/33across/usersync.go b/adapters/33across/usersync.go
index 7bc9ae45..df26f3b6 100644
--- a/adapters/33across/usersync.go
+++ b/adapters/33across/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func New33AcrossSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("33across", 58, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("33across", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/acuityads/usersync.go b/adapters/acuityads/usersync.go
index 7eedf78d..e2fc1f41 100644
--- a/adapters/acuityads/usersync.go
+++ b/adapters/acuityads/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewAcuityAdsSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("acuityads", 231, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("acuityads", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/adform/usersync.go b/adapters/adform/usersync.go
index 32b4e3a9..6a237f79 100644
--- a/adapters/adform/usersync.go
+++ b/adapters/adform/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewAdformSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("adform", 50, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("adform", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/adkernel/usersync.go b/adapters/adkernel/usersync.go
index 2e2ebdb9..2de82a00 100644
--- a/adapters/adkernel/usersync.go
+++ b/adapters/adkernel/usersync.go
@@ -7,8 +7,6 @@ import (
 	"github.com/prebid/prebid-server/usersync"
 )
 
-const adkernelGDPRVendorID = uint16(14)
-
 func NewAdkernelSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("adkernel", 14, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("adkernel", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/adkernelAdn/usersync.go b/adapters/adkernelAdn/usersync.go
index 0bedac38..5a890e15 100644
--- a/adapters/adkernelAdn/usersync.go
+++ b/adapters/adkernelAdn/usersync.go
@@ -7,8 +7,6 @@ import (
 	"github.com/prebid/prebid-server/usersync"
 )
 
-const adkernelGDPRVendorID = uint16(14)
-
 func NewAdkernelAdnSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("adkernelAdn", 14, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("adkernelAdn", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/adman/usersync.go b/adapters/adman/usersync.go
index aae6afcd..2cb62fe7 100644
--- a/adapters/adman/usersync.go
+++ b/adapters/adman/usersync.go
@@ -9,5 +9,5 @@ import (
 
 // NewAdmanSyncer returns adman syncer
 func NewAdmanSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("adman", 149, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("adman", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/admixer/usersync.go b/adapters/admixer/usersync.go
index 0a7f50ab..89e162df 100644
--- a/adapters/admixer/usersync.go
+++ b/adapters/admixer/usersync.go
@@ -1,11 +1,12 @@
 package admixer
 
 import (
+	"text/template"
+
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/usersync"
-	"text/template"
 )
 
 func NewAdmixerSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("admixer", 511, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("admixer", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/adocean/usersync.go b/adapters/adocean/usersync.go
index 650e517a..b189f822 100644
--- a/adapters/adocean/usersync.go
+++ b/adapters/adocean/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewAdOceanSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("adocean", 328, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("adocean", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/adpone/usersync.go b/adapters/adpone/usersync.go
index 67d4c998..38dec19b 100644
--- a/adapters/adpone/usersync.go
+++ b/adapters/adpone/usersync.go
@@ -7,13 +7,11 @@ import (
 	"github.com/prebid/prebid-server/usersync"
 )
 
-const adponeGDPRVendorID = uint16(799)
 const adponeFamilyName = "adpone"
 
 func NewadponeSyncer(urlTemplate *template.Template) usersync.Usersyncer {
 	return adapters.NewSyncer(
 		adponeFamilyName,
-		adponeGDPRVendorID,
 		urlTemplate,
 		adapters.SyncTypeRedirect,
 	)
diff --git a/adapters/adtarget/usersync.go b/adapters/adtarget/usersync.go
index 20bced25..d720f110 100644
--- a/adapters/adtarget/usersync.go
+++ b/adapters/adtarget/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewAdtargetSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("adtarget", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("adtarget", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/adtelligent/usersync.go b/adapters/adtelligent/usersync.go
index 087b5bdd..613c8e29 100644
--- a/adapters/adtelligent/usersync.go
+++ b/adapters/adtelligent/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewAdtelligentSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("adtelligent", 410, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("adtelligent", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/advangelists/usersync.go b/adapters/advangelists/usersync.go
index 10c46d18..83930774 100644
--- a/adapters/advangelists/usersync.go
+++ b/adapters/advangelists/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewAdvangelistsSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("advangelists", 0, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("advangelists", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/adyoulike/usersync.go b/adapters/adyoulike/usersync.go
index 0ecc4e40..ffea6f69 100644
--- a/adapters/adyoulike/usersync.go
+++ b/adapters/adyoulike/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewAdyoulikeSyncer(urlTemplate *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("adyoulike", 259, urlTemplate, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("adyoulike", urlTemplate, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/aja/usersync.go b/adapters/aja/usersync.go
index c54405db..6a9fad74 100644
--- a/adapters/aja/usersync.go
+++ b/adapters/aja/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewAJASyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("aja", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("aja", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/amx/usersync.go b/adapters/amx/usersync.go
index 28e6ac0e..17ad04d5 100644
--- a/adapters/amx/usersync.go
+++ b/adapters/amx/usersync.go
@@ -9,5 +9,5 @@ import (
 
 // NewAMXSyncer produces an AMX RTB usersyncer
 func NewAMXSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("amx", 737, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("amx", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/appnexus/usersync.go b/adapters/appnexus/usersync.go
index 22f46f1e..d29f0e3c 100644
--- a/adapters/appnexus/usersync.go
+++ b/adapters/appnexus/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewAppnexusSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("adnxs", 32, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("adnxs", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/audienceNetwork/usersync.go b/adapters/audienceNetwork/usersync.go
index 45c1281d..4dd0b68c 100644
--- a/adapters/audienceNetwork/usersync.go
+++ b/adapters/audienceNetwork/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewFacebookSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("audienceNetwork", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("audienceNetwork", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/avocet/usersync.go b/adapters/avocet/usersync.go
index f1075ab3..0cfa055a 100644
--- a/adapters/avocet/usersync.go
+++ b/adapters/avocet/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewAvocetSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("avocet", 63, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("avocet", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/beachfront/beachfront.go b/adapters/beachfront/beachfront.go
index 3a431c9c..58554577 100644
--- a/adapters/beachfront/beachfront.go
+++ b/adapters/beachfront/beachfront.go
@@ -76,6 +76,7 @@ type beachfrontBannerRequest struct {
 	AdapterVersion string           `json:"adapterVersion"`
 	IP             string           `json:"ip"`
 	RequestID      string           `json:"requestId"`
+	Real204        bool             `json:"real204"`
 }
 
 type beachfrontSlot struct {
@@ -367,6 +368,7 @@ func getBannerRequest(request *openrtb.BidRequest) (beachfrontBannerRequest, []e
 	if request.Imp[0].Secure != nil {
 		bfr.Secure = *request.Imp[0].Secure
 	}
+	bfr.Real204 = true
 
 	return bfr, errs
 }
@@ -465,7 +467,7 @@ func getVideoRequests(request *openrtb.BidRequest) ([]beachfrontVideoRequest, []
 		}
 
 		bfReqs[i].Request.Imp = nil
-		bfReqs[i].Request.Imp = make([]openrtb.Imp, 1, 1)
+		bfReqs[i].Request.Imp = make([]openrtb.Imp, 1)
 		bfReqs[i].Request.Imp[0] = imp
 
 	}
@@ -481,18 +483,12 @@ func getVideoRequests(request *openrtb.BidRequest) ([]beachfrontVideoRequest, []
 }
 
 func (a *BeachfrontAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
-	var bids []openrtb.Bid
-
-	// The case of response status == 200 and response body length == 2 below covers the case of the banner endpoint returning
-	// an empty JSON array ('[]'), which is functionally no content.
-	if response.StatusCode == http.StatusNoContent || (response.StatusCode == http.StatusOK && len(response.Body) <= 2) {
-		return nil, []error{&errortypes.BadInput{
-			Message: fmt.Sprintf("no content or truncated content received from server. status code %d from %s. Run with request.debug = 1 for more info", response.StatusCode, externalRequest.Uri),
-		}}
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
 	}
 
 	if response.StatusCode >= http.StatusInternalServerError {
-		return nil, []error{&errortypes.BadInput{
+		return nil, []error{&errortypes.BadServerResponse{
 			Message: fmt.Sprintf("server error status code %d from %s. Run with request.debug = 1 for more info", response.StatusCode, externalRequest.Uri),
 		}}
 	}
@@ -507,8 +503,9 @@ func (a *BeachfrontAdapter) MakeBids(internalRequest *openrtb.BidRequest, extern
 		return nil, []error{fmt.Errorf("unexpected status code %d from %s. Run with request.debug = 1 for more info", response.StatusCode, externalRequest.Uri)}
 	}
 
-	var xtrnal openrtb.BidRequest
+	var bids []openrtb.Bid
 	var errs = make([]error, 0)
+	var xtrnal openrtb.BidRequest
 
 	// For video, which uses RTB for the external request, this will unmarshal as expected. For banner, it will
 	// only get the User struct and everything else will be nil
@@ -524,6 +521,7 @@ func (a *BeachfrontAdapter) MakeBids(internalRequest *openrtb.BidRequest, extern
 
 	var dur beachfrontVideoBidExtension
 	bidResponse := adapters.NewBidderResponseWithBidsCapacity(BidCapacity)
+
 	for i := 0; i < len(bids); i++ {
 
 		// If we unmarshal without an error, this is an AdM video
diff --git a/adapters/beachfront/usersync.go b/adapters/beachfront/usersync.go
index a6502331..72466b59 100644
--- a/adapters/beachfront/usersync.go
+++ b/adapters/beachfront/usersync.go
@@ -7,12 +7,9 @@ import (
 	"github.com/prebid/prebid-server/usersync"
 )
 
-var VENDOR_ID uint16 = 335
-
 func NewBeachfrontSyncer(temp *template.Template) usersync.Usersyncer {
 	return adapters.NewSyncer(
 		"beachfront",
-		VENDOR_ID,
 		temp,
 		adapters.SyncTypeIframe)
 }
diff --git a/adapters/beintoo/usersync.go b/adapters/beintoo/usersync.go
index a225a2b1..fb60c6ab 100644
--- a/adapters/beintoo/usersync.go
+++ b/adapters/beintoo/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewBeintooSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("Beintoo", 618, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("Beintoo", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/between/usersync.go b/adapters/between/usersync.go
index a34521fe..14228273 100644
--- a/adapters/between/usersync.go
+++ b/adapters/between/usersync.go
@@ -9,5 +9,5 @@ import (
 
 // NewBetweenSyncer returns "between" syncer
 func NewBetweenSyncer(template *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("between", 724, template, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("between", template, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/brightroll/usersync.go b/adapters/brightroll/usersync.go
index b33cc5a2..099303fd 100644
--- a/adapters/brightroll/usersync.go
+++ b/adapters/brightroll/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewBrightrollSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("brightroll", 25, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("brightroll", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/colossus/usersync.go b/adapters/colossus/usersync.go
index a4e82ee3..bb25b187 100644
--- a/adapters/colossus/usersync.go
+++ b/adapters/colossus/usersync.go
@@ -9,5 +9,5 @@ import (
 
 // NewColossusSyncer returns colossus syncer
 func NewColossusSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("colossus", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("colossus", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/connectad/usersync.go b/adapters/connectad/usersync.go
index 5661cb5d..bae96656 100644
--- a/adapters/connectad/usersync.go
+++ b/adapters/connectad/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewConnectAdSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("connectad", 138, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("connectad", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/consumable/usersync.go b/adapters/consumable/usersync.go
index 0e0938c7..0cb25ca9 100644
--- a/adapters/consumable/usersync.go
+++ b/adapters/consumable/usersync.go
@@ -7,13 +7,9 @@ import (
 	"github.com/prebid/prebid-server/usersync"
 )
 
-var VENDOR_ID uint16 = 591
-
 func NewConsumableSyncer(temp *template.Template) usersync.Usersyncer {
-
 	return adapters.NewSyncer(
 		"consumable",
-		VENDOR_ID,
 		temp,
 		adapters.SyncTypeRedirect)
 }
diff --git a/adapters/conversant/usersync.go b/adapters/conversant/usersync.go
index c2676df6..a38631f2 100644
--- a/adapters/conversant/usersync.go
+++ b/adapters/conversant/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewConversantSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("conversant", 24, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("conversant", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/cpmstar/usersync.go b/adapters/cpmstar/usersync.go
index 9c864e24..d3086f65 100644
--- a/adapters/cpmstar/usersync.go
+++ b/adapters/cpmstar/usersync.go
@@ -9,5 +9,5 @@ import (
 
 //NewCpmstarSyncer :
 func NewCpmstarSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("cpmstar", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("cpmstar", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/datablocks/usersync.go b/adapters/datablocks/usersync.go
index 2b47b259..2c69963a 100644
--- a/adapters/datablocks/usersync.go
+++ b/adapters/datablocks/usersync.go
@@ -7,8 +7,6 @@ import (
 	"github.com/prebid/prebid-server/usersync"
 )
 
-const datablocksGDPRVendorID = uint16(0)
-
 func NewDatablocksSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("datablocks", datablocksGDPRVendorID, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("datablocks", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/deepintent/usersync.go b/adapters/deepintent/usersync.go
index 9e803df6..7a17f533 100644
--- a/adapters/deepintent/usersync.go
+++ b/adapters/deepintent/usersync.go
@@ -9,5 +9,5 @@ import (
 
 // NewDeepintentSyncer returns deepintent syncer
 func NewDeepintentSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("deepintent", 541, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("deepintent", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/dmx/usersync.go b/adapters/dmx/usersync.go
index 98e56234..07fedf1c 100644
--- a/adapters/dmx/usersync.go
+++ b/adapters/dmx/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewDmxSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("dmx", 144, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("dmx", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/emx_digital/usersync.go b/adapters/emx_digital/usersync.go
index a453955b..2d3011e0 100644
--- a/adapters/emx_digital/usersync.go
+++ b/adapters/emx_digital/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewEMXDigitalSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("emx_digital", 183, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("emx_digital", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/engagebdr/usersync.go b/adapters/engagebdr/usersync.go
index ae4047aa..205097b0 100644
--- a/adapters/engagebdr/usersync.go
+++ b/adapters/engagebdr/usersync.go
@@ -1,11 +1,12 @@
 package engagebdr
 
 import (
+	"text/template"
+
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/usersync"
-	"text/template"
 )
 
 func NewEngageBDRSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("engagebdr", 62, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("engagebdr", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/eplanning/usersync.go b/adapters/eplanning/usersync.go
index faa7fa82..6e8abf5d 100644
--- a/adapters/eplanning/usersync.go
+++ b/adapters/eplanning/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewEPlanningSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("eplanning", 90, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("eplanning", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/gamma/usersync.go b/adapters/gamma/usersync.go
index f19c522c..4e4412a1 100644
--- a/adapters/gamma/usersync.go
+++ b/adapters/gamma/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewGammaSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("gamma", 0, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("gamma", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/gamoshi/usersync.go b/adapters/gamoshi/usersync.go
index 6b7c43dd..9d6cdd0e 100644
--- a/adapters/gamoshi/usersync.go
+++ b/adapters/gamoshi/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewGamoshiSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("gamoshi", 644, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("gamoshi", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/grid/usersync.go b/adapters/grid/usersync.go
index afdc5db7..31788ea3 100644
--- a/adapters/grid/usersync.go
+++ b/adapters/grid/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewGridSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("grid", 686, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("grid", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/gumgum/usersync.go b/adapters/gumgum/usersync.go
index 5d29c7dc..defef87d 100644
--- a/adapters/gumgum/usersync.go
+++ b/adapters/gumgum/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewGumGumSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("gumgum", 61, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("gumgum", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/improvedigital/improvedigital.go b/adapters/improvedigital/improvedigital.go
index 0d5c043e..b1cc3640 100644
--- a/adapters/improvedigital/improvedigital.go
+++ b/adapters/improvedigital/improvedigital.go
@@ -114,6 +114,10 @@ func getMediaTypeForImp(impID string, imps []openrtb.Imp) (openrtb_ext.BidType,
 				return openrtb_ext.BidTypeVideo, nil
 			}
 
+			if imp.Native != nil {
+				return openrtb_ext.BidTypeNative, nil
+			}
+
 			return "", &errortypes.BadServerResponse{
 				Message: fmt.Sprintf("Unknown impression type for ID: \"%s\"", impID),
 			}
diff --git a/adapters/improvedigital/usersync.go b/adapters/improvedigital/usersync.go
index 72c3ddaf..a58b97c3 100644
--- a/adapters/improvedigital/usersync.go
+++ b/adapters/improvedigital/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewImprovedigitalSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("improvedigital", 253, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("improvedigital", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/invibes/usersync.go b/adapters/invibes/usersync.go
index 468e604c..e6922444 100644
--- a/adapters/invibes/usersync.go
+++ b/adapters/invibes/usersync.go
@@ -10,7 +10,6 @@ import (
 func NewInvibesSyncer(urlTemplate *template.Template) usersync.Usersyncer {
 	return adapters.NewSyncer(
 		"invibes",
-		436,
 		urlTemplate,
 		adapters.SyncTypeIframe,
 	)
diff --git a/adapters/ix/usersync.go b/adapters/ix/usersync.go
index 6f355894..621fa179 100644
--- a/adapters/ix/usersync.go
+++ b/adapters/ix/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewIxSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("ix", 10, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("ix", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/jixie/usersync.go b/adapters/jixie/usersync.go
index 8c1d5c07..137d78f2 100644
--- a/adapters/jixie/usersync.go
+++ b/adapters/jixie/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewJixieSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("jixie", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("jixie", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/krushmedia/usersync.go b/adapters/krushmedia/usersync.go
index 5dc1471f..860cb220 100644
--- a/adapters/krushmedia/usersync.go
+++ b/adapters/krushmedia/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewKrushmediaSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("krushmedia", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("krushmedia", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/lifestreet/usersync.go b/adapters/lifestreet/usersync.go
index 15ffde4d..f5300eba 100644
--- a/adapters/lifestreet/usersync.go
+++ b/adapters/lifestreet/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewLifestreetSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("lifestreet", 67, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("lifestreet", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/lockerdome/usersync.go b/adapters/lockerdome/usersync.go
index fb01f8e2..d5cce168 100644
--- a/adapters/lockerdome/usersync.go
+++ b/adapters/lockerdome/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewLockerDomeSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("lockerdome", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("lockerdome", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/logicad/usersync.go b/adapters/logicad/usersync.go
index d26a197b..e685cc98 100644
--- a/adapters/logicad/usersync.go
+++ b/adapters/logicad/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewLogicadSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("logicad", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("logicad", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/lunamedia/usersync.go b/adapters/lunamedia/usersync.go
index 7ad54e38..39c9a808 100644
--- a/adapters/lunamedia/usersync.go
+++ b/adapters/lunamedia/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewLunaMediaSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("lunamedia", 0, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("lunamedia", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/marsmedia/usersync.go b/adapters/marsmedia/usersync.go
index 50996f32..4ac76d1f 100644
--- a/adapters/marsmedia/usersync.go
+++ b/adapters/marsmedia/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewMarsmediaSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("marsmedia", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("marsmedia", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/mediafuse/usersync.go b/adapters/mediafuse/usersync.go
index 5381bf36..b91a6b50 100644
--- a/adapters/mediafuse/usersync.go
+++ b/adapters/mediafuse/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewMediafuseSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("mediafuse", 411, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("mediafuse", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/mgid/usersync.go b/adapters/mgid/usersync.go
index fbdb95f0..94cf12e1 100644
--- a/adapters/mgid/usersync.go
+++ b/adapters/mgid/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewMgidSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("mgid", 358, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("mgid", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/nanointeractive/usersync.go b/adapters/nanointeractive/usersync.go
index e6227436..6bd9cd1f 100644
--- a/adapters/nanointeractive/usersync.go
+++ b/adapters/nanointeractive/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewNanoInteractiveSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("nanointeractive", 72, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("nanointeractive", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/ninthdecimal/usersync.go b/adapters/ninthdecimal/usersync.go
index 7a8d029c..a01fdb63 100755
--- a/adapters/ninthdecimal/usersync.go
+++ b/adapters/ninthdecimal/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewNinthDecimalSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("ninthdecimal", 0, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("ninthdecimal", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/nobid/usersync.go b/adapters/nobid/usersync.go
index c21413f5..44207564 100644
--- a/adapters/nobid/usersync.go
+++ b/adapters/nobid/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewNoBidSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("nobid", 816, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("nobid", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/onetag/usersync.go b/adapters/onetag/usersync.go
index f36b82a8..9a2b700d 100644
--- a/adapters/onetag/usersync.go
+++ b/adapters/onetag/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewSyncer(template *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("onetag", 241, template, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("onetag", template, adapters.SyncTypeIframe)
 }
diff --git a/adapters/openx/usersync.go b/adapters/openx/usersync.go
index bb4b328c..875b60fb 100644
--- a/adapters/openx/usersync.go
+++ b/adapters/openx/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewOpenxSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("openx", 69, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("openx", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/pubmatic/usersync.go b/adapters/pubmatic/usersync.go
index 7b4d8e86..822f13ce 100644
--- a/adapters/pubmatic/usersync.go
+++ b/adapters/pubmatic/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewPubmaticSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("pubmatic", 76, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("pubmatic", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/pulsepoint/usersync.go b/adapters/pulsepoint/usersync.go
index 58de835b..1b6903f9 100644
--- a/adapters/pulsepoint/usersync.go
+++ b/adapters/pulsepoint/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewPulsepointSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("pulsepoint", 81, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("pulsepoint", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/rhythmone/rhythmone.go b/adapters/rhythmone/rhythmone.go
index 7dd94a49..a507e778 100644
--- a/adapters/rhythmone/rhythmone.go
+++ b/adapters/rhythmone/rhythmone.go
@@ -132,9 +132,9 @@ func (a *RhythmoneAdapter) preProcess(req *openrtb.BidRequest, errors []error) (
 			errors = append(errors, err)
 			return nil, "", errors
 		}
-		bidderExtCopy := openrtb_ext.ExtBid{
-			Bidder: rhythmoneExtCopy,
-		}
+		bidderExtCopy := struct {
+			Bidder json.RawMessage `json:"bidder,omitempty"`
+		}{rhythmoneExtCopy}
 		impExtCopy, err := json.Marshal(&bidderExtCopy)
 		if err != nil {
 			errors = append(errors, err)
diff --git a/adapters/rhythmone/usersync.go b/adapters/rhythmone/usersync.go
index 0f7db388..990fcb06 100644
--- a/adapters/rhythmone/usersync.go
+++ b/adapters/rhythmone/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewRhythmoneSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("rhythmone", 36, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("rhythmone", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/rtbhouse/usersync.go b/adapters/rtbhouse/usersync.go
index 3e38d67e..baa0b994 100644
--- a/adapters/rtbhouse/usersync.go
+++ b/adapters/rtbhouse/usersync.go
@@ -7,13 +7,11 @@ import (
 	"github.com/prebid/prebid-server/usersync"
 )
 
-const rtbHouseGDPRVendorID = uint16(16)
 const rtbHouseFamilyName = "rtbhouse"
 
 func NewRTBHouseSyncer(urlTemplate *template.Template) usersync.Usersyncer {
 	return adapters.NewSyncer(
 		rtbHouseFamilyName,
-		rtbHouseGDPRVendorID,
 		urlTemplate,
 		adapters.SyncTypeRedirect,
 	)
diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go
index 91211a61..32bf971c 100644
--- a/adapters/rubicon/rubicon.go
+++ b/adapters/rubicon/rubicon.go
@@ -93,6 +93,10 @@ type rubiconExtUserTpID struct {
 	UID    string `json:"uid"`
 }
 
+type rubiconUserDataExt struct {
+	TaxonomyName string `json:"taxonomyname"`
+}
+
 type rubiconUserExt struct {
 	Consent     string                        `json:"consent,omitempty"`
 	DigiTrust   *openrtb_ext.ExtUserDigiTrust `json:"digitrust"`
@@ -752,6 +756,11 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap
 			userCopy := *request.User
 			userExtRP := rubiconUserExt{RP: rubiconUserExtRP{Target: rubiconExt.Visitor}}
 
+			if err := updateUserExtWithIabAttribute(&userExtRP, userCopy.Data); err != nil {
+				errs = append(errs, err)
+				continue
+			}
+
 			if request.User.Ext != nil {
 				var userExt *openrtb_ext.ExtUser
 				if err = json.Unmarshal(userCopy.Ext, &userExt); err != nil {
@@ -886,6 +895,43 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap
 	return requestData, errs
 }
 
+func updateUserExtWithIabAttribute(userExtRP *rubiconUserExt, data []openrtb.Data) error {
+	var segmentIdsToCopy = make([]string, 0)
+
+	for _, dataRecord := range data {
+		if dataRecord.Ext != nil {
+			var dataExtObject rubiconUserDataExt
+			err := json.Unmarshal(dataRecord.Ext, &dataExtObject)
+			if err != nil {
+				continue
+			}
+			if strings.EqualFold(dataExtObject.TaxonomyName, "iab") {
+				for _, segment := range dataRecord.Segment {
+					segmentIdsToCopy = append(segmentIdsToCopy, segment.ID)
+				}
+			}
+		}
+	}
+
+	userExtRPTarget := make(map[string]interface{})
+
+	if userExtRP.RP.Target != nil {
+		if err := json.Unmarshal(userExtRP.RP.Target, &userExtRPTarget); err != nil {
+			return &errortypes.BadInput{Message: err.Error()}
+		}
+	}
+
+	userExtRPTarget["iab"] = segmentIdsToCopy
+
+	if target, err := json.Marshal(&userExtRPTarget); err != nil {
+		return &errortypes.BadInput{Message: err.Error()}
+	} else {
+		userExtRP.RP.Target = target
+	}
+
+	return nil
+}
+
 func getTpIdsAndSegments(eids []openrtb_ext.ExtUserEid) (mappedRubiconUidsParam, []error) {
 	rubiconUidsParam := mappedRubiconUidsParam{
 		tpIds:    make([]rubiconExtUserTpID, 0),
diff --git a/adapters/rubicon/usersync.go b/adapters/rubicon/usersync.go
index 08d98825..a4ab464a 100644
--- a/adapters/rubicon/usersync.go
+++ b/adapters/rubicon/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewRubiconSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("rubicon", 52, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("rubicon", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/sharethrough/usersync.go b/adapters/sharethrough/usersync.go
index a951fcd6..f76f41ca 100644
--- a/adapters/sharethrough/usersync.go
+++ b/adapters/sharethrough/usersync.go
@@ -1,11 +1,12 @@
 package sharethrough
 
 import (
+	"text/template"
+
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/usersync"
-	"text/template"
 )
 
 func NewSharethroughSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("sharethrough", 80, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("sharethrough", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/smartadserver/usersync.go b/adapters/smartadserver/usersync.go
index 95b305ff..f7199965 100644
--- a/adapters/smartadserver/usersync.go
+++ b/adapters/smartadserver/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewSmartadserverSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("smartadserver", 45, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("smartadserver", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/smartrtb/usersync.go b/adapters/smartrtb/usersync.go
index 2f7b1dc3..74ef0e99 100644
--- a/adapters/smartrtb/usersync.go
+++ b/adapters/smartrtb/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewSmartRTBSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("smartrtb", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("smartrtb", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/smartyads/usersync.go b/adapters/smartyads/usersync.go
index 839621e9..9075aa9b 100644
--- a/adapters/smartyads/usersync.go
+++ b/adapters/smartyads/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewSmartyAdsSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("smartyads", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("smartyads", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/somoaudience/usersync.go b/adapters/somoaudience/usersync.go
index e1e7e2ef..5d1ddd71 100644
--- a/adapters/somoaudience/usersync.go
+++ b/adapters/somoaudience/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewSomoaudienceSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("somoaudience", 341, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("somoaudience", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/sonobi/usersync.go b/adapters/sonobi/usersync.go
index 6986d0fd..6fedd8bf 100644
--- a/adapters/sonobi/usersync.go
+++ b/adapters/sonobi/usersync.go
@@ -1,11 +1,12 @@
 package sonobi
 
 import (
+	"text/template"
+
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/usersync"
-	"text/template"
 )
 
 func NewSonobiSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("sonobi", 104, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("sonobi", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/sovrn/usersync.go b/adapters/sovrn/usersync.go
index b4de6237..225f2888 100644
--- a/adapters/sovrn/usersync.go
+++ b/adapters/sovrn/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewSovrnSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("sovrn", 13, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("sovrn", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/synacormedia/usersync.go b/adapters/synacormedia/usersync.go
index 988c1b9e..c7fa5c42 100644
--- a/adapters/synacormedia/usersync.go
+++ b/adapters/synacormedia/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewSynacorMediaSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("synacormedia", 0, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("synacormedia", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/syncer.go b/adapters/syncer.go
index 122bcc7e..b9752b50 100644
--- a/adapters/syncer.go
+++ b/adapters/syncer.go
@@ -4,34 +4,21 @@ import (
 	"text/template"
 
 	"github.com/prebid/prebid-server/macros"
-	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/privacy"
 	"github.com/prebid/prebid-server/usersync"
 )
 
-func GDPRAwareSyncerIDs(syncers map[openrtb_ext.BidderName]usersync.Usersyncer) map[openrtb_ext.BidderName]uint16 {
-	gdprAwareSyncers := make(map[openrtb_ext.BidderName]uint16, len(syncers))
-	for bidderName, syncer := range syncers {
-		if syncer.GDPRVendorID() != 0 {
-			gdprAwareSyncers[bidderName] = syncer.GDPRVendorID()
-		}
-	}
-	return gdprAwareSyncers
-}
-
 type Syncer struct {
-	familyName   string
-	gdprVendorID uint16
-	urlTemplate  *template.Template
-	syncType     SyncType
+	familyName  string
+	syncType    SyncType
+	urlTemplate *template.Template
 }
 
-func NewSyncer(familyName string, vendorID uint16, urlTemplate *template.Template, syncType SyncType) *Syncer {
+func NewSyncer(familyName string, urlTemplate *template.Template, syncType SyncType) *Syncer {
 	return &Syncer{
-		familyName:   familyName,
-		gdprVendorID: vendorID,
-		urlTemplate:  urlTemplate,
-		syncType:     syncType,
+		familyName:  familyName,
+		urlTemplate: urlTemplate,
+		syncType:    syncType,
 	}
 }
 
@@ -62,7 +49,3 @@ func (s *Syncer) GetUsersyncInfo(privacyPolicies privacy.Policies) (*usersync.Us
 func (s *Syncer) FamilyName() string {
 	return s.familyName
 }
-
-func (s *Syncer) GDPRVendorID() uint16 {
-	return s.gdprVendorID
-}
diff --git a/adapters/tappx/usersync.go b/adapters/tappx/usersync.go
index 7bf6b9a6..01477c35 100644
--- a/adapters/tappx/usersync.go
+++ b/adapters/tappx/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewTappxSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("tappx", 628, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("tappx", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/telaria/usersync.go b/adapters/telaria/usersync.go
index e3f76f6e..76be6202 100644
--- a/adapters/telaria/usersync.go
+++ b/adapters/telaria/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewTelariaSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("telaria", 202, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("telaria", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/triplelift/usersync.go b/adapters/triplelift/usersync.go
index 5cb524be..4a47615b 100644
--- a/adapters/triplelift/usersync.go
+++ b/adapters/triplelift/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewTripleliftSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("triplelift", 28, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("triplelift", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/triplelift_native/usersync.go b/adapters/triplelift_native/usersync.go
index b7587b77..2a07740a 100644
--- a/adapters/triplelift_native/usersync.go
+++ b/adapters/triplelift_native/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewTripleliftSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("triplelift", 28, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("triplelift", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/trustx/usersync.go b/adapters/trustx/usersync.go
index a617cd71..a738e8e9 100644
--- a/adapters/trustx/usersync.go
+++ b/adapters/trustx/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewTrustXSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("trustx", 686, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("trustx", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/ucfunnel/usersync.go b/adapters/ucfunnel/usersync.go
index 92eba0d7..0ae9948d 100644
--- a/adapters/ucfunnel/usersync.go
+++ b/adapters/ucfunnel/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewUcfunnelSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("ucfunnel", 607, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("ucfunnel", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/unruly/usersync.go b/adapters/unruly/usersync.go
index c90052a4..5dc60c85 100644
--- a/adapters/unruly/usersync.go
+++ b/adapters/unruly/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewUnrulySyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("unruly", 162, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("unruly", temp, adapters.SyncTypeIframe)
 }
diff --git a/adapters/valueimpression/usersync.go b/adapters/valueimpression/usersync.go
index 34addbc0..08490a5e 100644
--- a/adapters/valueimpression/usersync.go
+++ b/adapters/valueimpression/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewValueImpressionSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("valueimpression", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("valueimpression", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/verizonmedia/usersync.go b/adapters/verizonmedia/usersync.go
index 31fb12a2..2c7354d1 100644
--- a/adapters/verizonmedia/usersync.go
+++ b/adapters/verizonmedia/usersync.go
@@ -1,11 +1,12 @@
 package verizonmedia
 
 import (
+	"text/template"
+
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/usersync"
-	"text/template"
 )
 
 func NewVerizonMediaSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("verizonmedia", 25, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("verizonmedia", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/visx/usersync.go b/adapters/visx/usersync.go
index fe1f5a42..dc143570 100644
--- a/adapters/visx/usersync.go
+++ b/adapters/visx/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewVisxSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("visx", 154, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("visx", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/vrtcal/usersync.go b/adapters/vrtcal/usersync.go
index 04e52955..8a6ddc0c 100644
--- a/adapters/vrtcal/usersync.go
+++ b/adapters/vrtcal/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewVrtcalSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("vrtcal", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("vrtcal", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/yieldlab/usersync.go b/adapters/yieldlab/usersync.go
index 3ee9a3fd..90507e31 100644
--- a/adapters/yieldlab/usersync.go
+++ b/adapters/yieldlab/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewYieldlabSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("yieldlab", 70, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("yieldlab", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/yieldmo/usersync.go b/adapters/yieldmo/usersync.go
index 041e7e8f..d06caa90 100644
--- a/adapters/yieldmo/usersync.go
+++ b/adapters/yieldmo/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewYieldmoSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("yieldmo", 173, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("yieldmo", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/yieldone/usersync.go b/adapters/yieldone/usersync.go
index bc9d1b32..4d5d8283 100644
--- a/adapters/yieldone/usersync.go
+++ b/adapters/yieldone/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewYieldoneSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("yieldone", 0, temp, adapters.SyncTypeRedirect)
+	return adapters.NewSyncer("yieldone", temp, adapters.SyncTypeRedirect)
 }
diff --git a/adapters/zeroclickfraud/usersync.go b/adapters/zeroclickfraud/usersync.go
index 833524e4..41c58981 100644
--- a/adapters/zeroclickfraud/usersync.go
+++ b/adapters/zeroclickfraud/usersync.go
@@ -8,5 +8,5 @@ import (
 )
 
 func NewZeroClickFraudSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("zeroclickfraud", 0, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer("zeroclickfraud", temp, adapters.SyncTypeIframe)
 }
diff --git a/config/bidderinfo.go b/config/bidderinfo.go
index 4af8a97c..9eff288f 100644
--- a/config/bidderinfo.go
+++ b/config/bidderinfo.go
@@ -19,6 +19,7 @@ type BidderInfo struct {
 	Capabilities            *CapabilitiesInfo `yaml:"capabilities"`
 	ModifyingVastXmlAllowed bool              `yaml:"modifyingVastXmlAllowed"`
 	Debug                   *DebugInfo        `yaml:"debug,omitempty"`
+	GVLVendorID             uint16            `yaml:"gvlVendorID,omitempty"`
 }
 
 // MaintainerInfo is the support email address for a bidder.
@@ -43,13 +44,13 @@ type DebugInfo struct {
 }
 
 // LoadBidderInfoFromDisk parses all static/bidder-info/{bidder}.yaml files from the file system.
-func LoadBidderInfoFromDisk(path string, adapterConfigs map[string]Adapter, bidders []string) (map[string]BidderInfo, error) {
+func LoadBidderInfoFromDisk(path string, adapterConfigs map[string]Adapter, bidders []string) (BidderInfos, error) {
 	reader := infoReaderFromDisk{path}
 	return loadBidderInfo(reader, adapterConfigs, bidders)
 }
 
-func loadBidderInfo(r infoReader, adapterConfigs map[string]Adapter, bidders []string) (map[string]BidderInfo, error) {
-	infos := map[string]BidderInfo{}
+func loadBidderInfo(r infoReader, adapterConfigs map[string]Adapter, bidders []string) (BidderInfos, error) {
+	infos := BidderInfos{}
 
 	for _, bidder := range bidders {
 		data, err := r.Read(bidder)
@@ -86,3 +87,15 @@ func (r infoReaderFromDisk) Read(bidder string) ([]byte, error) {
 	path := fmt.Sprintf("%v/%v.yaml", r.path, bidder)
 	return ioutil.ReadFile(path)
 }
+
+// ToGVLVendorIDMap transforms a BidderInfos object to a map of bidder names to GVL id. Disabled
+// bidders are omitted from the result.
+func (infos BidderInfos) ToGVLVendorIDMap() map[openrtb_ext.BidderName]uint16 {
+	m := make(map[openrtb_ext.BidderName]uint16, len(infos))
+	for name, info := range infos {
+		if info.Enabled && info.GVLVendorID != 0 {
+			m[openrtb_ext.BidderName(name)] = info.GVLVendorID
+		}
+	}
+	return m
+}
diff --git a/docs/developers/contributing.md b/docs/developers/contributing.md
index 2dafa67f..2a6a574e 100644
--- a/docs/developers/contributing.md
+++ b/docs/developers/contributing.md
@@ -40,7 +40,7 @@ those updates must be submitted in the same Pull Request as the code changes.
 When you're ready, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request/)
 against the `master` branch of [our GitHub repository](https://github.com/prebid/prebid-server/compare).
 
-Pull Requests will be vetted through [Travis CI](https://travis-ci.com/).
+Pull Requests will be vetted through GitHub Actions.
 To reproduce these same tests locally, do:
 
 ```bash
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 8cc8e9bd..1f3d622b 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -35,10 +35,10 @@ import (
 const defaultAmpRequestTimeoutMillis = 900
 
 type AmpResponse struct {
-	Targeting map[string]string                                       `json:"targeting"`
-	Debug     *openrtb_ext.ExtResponseDebug                           `json:"debug,omitempty"`
-	Errors    map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError `json:"errors,omitempty"`
-	Warnings  map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError `json:"warnings,omitempty"`
+	Targeting map[string]string                                         `json:"targeting"`
+	Debug     *openrtb_ext.ExtResponseDebug                             `json:"debug,omitempty"`
+	Errors    map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage `json:"errors,omitempty"`
+	Warnings  map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage `json:"warnings,omitempty"`
 }
 
 // NewAmpEndpoint modifies the OpenRTB endpoint to handle AMP requests. This will basically modify the parsing
@@ -239,9 +239,12 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 		ao.Errors = append(ao.Errors, fmt.Errorf("AMP response: failed to unpack OpenRTB response.ext, debug info cannot be forwarded: %v", eRErr))
 	}
 
-	warnings := make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError)
+	warnings := extResponse.Warnings
+	if warnings == nil {
+		warnings = make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)
+	}
 	for _, v := range errortypes.WarningOnly(errL) {
-		bidderErr := openrtb_ext.ExtBidderError{
+		bidderErr := openrtb_ext.ExtBidderMessage{
 			Code:    errortypes.ReadCode(v),
 			Message: v.Error(),
 		}
@@ -528,8 +531,9 @@ func readPolicy(consent string) (privacy.PolicyWriter, error) {
 		return ccpa.ConsentWriter{consent}, nil
 	}
 
-	return privacy.NilPolicyWriter{}, &errortypes.InvalidPrivacyConsent{
-		Message: fmt.Sprintf("Consent '%s' is not recognized as either CCPA or GDPR TCF.", consent),
+	return privacy.NilPolicyWriter{}, &errortypes.Warning{
+		Message:     fmt.Sprintf("Consent '%s' is not recognized as either CCPA or GDPR TCF.", consent),
+		WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
 	}
 }
 
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 290b06cb..d26623da 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -139,6 +139,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 	if errortypes.ContainsFatalError(errL) && writeError(errL, w, &labels) {
 		return
 	}
+	warnings := errortypes.WarningOnly(errL)
 
 	ctx := context.Background()
 
@@ -179,6 +180,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 		RequestType:  labels.RType,
 		StartTime:    start,
 		LegacyLabels: labels,
+		Warnings:     warnings,
 	}
 
 	response, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
@@ -363,8 +365,10 @@ func (deps *endpointDeps) validateRequest(req *openrtb.BidRequest) []error {
 	if ccpaPolicy, err := ccpa.ReadFromRequest(req); err != nil {
 		return append(errL, err)
 	} else if _, err := ccpaPolicy.Parse(exchange.GetValidBidders(aliases)); err != nil {
-		if _, invalidConsent := err.(*errortypes.InvalidPrivacyConsent); invalidConsent {
-			errL = append(errL, &errortypes.InvalidPrivacyConsent{Message: fmt.Sprintf("CCPA consent is invalid and will be ignored. (%v)", err)})
+		if _, invalidConsent := err.(*errortypes.Warning); invalidConsent {
+			errL = append(errL, &errortypes.Warning{
+				Message:     fmt.Sprintf("CCPA consent is invalid and will be ignored. (%v)", err),
+				WarningCode: errortypes.InvalidPrivacyConsentWarningCode})
 			consentWriter := ccpa.ConsentWriter{Consent: ""}
 			if err := consentWriter.Write(req); err != nil {
 				return append(errL, fmt.Errorf("Unable to remove invalid CCPA consent from the request. (%v)", err))
@@ -830,19 +834,15 @@ func (deps *endpointDeps) validateImpExt(imp *openrtb.Imp, aliases map[string]st
 		return []error{err}
 	}
 
-	// Also accept bidder exts within imp[...].ext.prebid.bidder
-	// NOTE: This is not part of the official API yet, so we are not expecting clients
-	// to migrate from imp[...].ext.${BIDDER} to imp[...].ext.prebid.bidder.${BIDDER}
-	// at this time
-	// https://github.com/prebid/prebid-server/pull/846#issuecomment-476352224
-	if rawPrebidExt, ok := bidderExts[openrtb_ext.PrebidExtKey]; ok {
-		var prebidExt openrtb_ext.ExtImpPrebid
-		if err := json.Unmarshal(rawPrebidExt, &prebidExt); err == nil && prebidExt.Bidder != nil {
-			for bidder, ext := range prebidExt.Bidder {
+	// Prefer bidder params from request.imp.ext.prebid.bidder.BIDDER over request.imp.ext.BIDDER
+	// to avoid confusion beteween prebid specific adapter config and other ext protocols.
+	if extPrebidJSON, ok := bidderExts[openrtb_ext.PrebidExtKey]; ok {
+		var extPrebid openrtb_ext.ExtImpPrebid
+		if err := json.Unmarshal(extPrebidJSON, &extPrebid); err == nil && extPrebid.Bidder != nil {
+			for bidder, ext := range extPrebid.Bidder {
 				if ext == nil {
 					continue
 				}
-
 				bidderExts[bidder] = ext
 			}
 		}
@@ -893,13 +893,18 @@ func (deps *endpointDeps) validateImpExt(imp *openrtb.Imp, aliases map[string]st
 	return errL
 }
 
+// isBidderToValidate determines if the bidder name in request.imp[].prebid should be validated.
 func isBidderToValidate(bidder string) bool {
-	// PrebidExtKey is a special case for the prebid config section and is not considered a bidder.
-
-	// FirstPartyDataContextExtKey is a special case for the first party data context section
-	// and is not considered a bidder.
-
-	return bidder != openrtb_ext.PrebidExtKey && bidder != openrtb_ext.FirstPartyDataContextExtKey
+	switch openrtb_ext.BidderName(bidder) {
+	case openrtb_ext.BidderReservedContext:
+		return false
+	case openrtb_ext.BidderReservedPrebid:
+		return false
+	case openrtb_ext.BidderReservedSKAdN:
+		return false
+	default:
+		return true
+	}
 }
 
 func (deps *endpointDeps) parseBidExt(ext json.RawMessage) (*openrtb_ext.ExtRequest, error) {
@@ -914,12 +919,17 @@ func (deps *endpointDeps) parseBidExt(ext json.RawMessage) (*openrtb_ext.ExtRequ
 }
 
 func (deps *endpointDeps) validateAliases(aliases map[string]string) error {
-	for thisAlias, coreBidder := range aliases {
+	for alias, coreBidder := range aliases {
+		if _, isCoreBidderDisabled := deps.disabledBidders[coreBidder]; isCoreBidderDisabled {
+			return fmt.Errorf("request.ext.prebid.aliases.%s refers to disabled bidder: %s", alias, coreBidder)
+		}
+
 		if _, isCoreBidder := deps.bidderMap[coreBidder]; !isCoreBidder {
-			return fmt.Errorf("request.ext.prebid.aliases.%s refers to unknown bidder: %s", thisAlias, coreBidder)
+			return fmt.Errorf("request.ext.prebid.aliases.%s refers to unknown bidder: %s", alias, coreBidder)
 		}
-		if thisAlias == coreBidder {
-			return fmt.Errorf("request.ext.prebid.aliases.%s defines a no-op alias. Choose a different alias, or remove this entry.", thisAlias)
+
+		if alias == coreBidder {
+			return fmt.Errorf("request.ext.prebid.aliases.%s defines a no-op alias. Choose a different alias, or remove this entry.", alias)
 		}
 	}
 	return nil
diff --git a/endpoints/openrtb2/sample-requests/disabled/bad/bad-alias.json b/endpoints/openrtb2/sample-requests/disabled/bad/bad-alias.json
index f4379dc0..86239308 100644
--- a/endpoints/openrtb2/sample-requests/disabled/bad/bad-alias.json
+++ b/endpoints/openrtb2/sample-requests/disabled/bad/bad-alias.json
@@ -1,7 +1,7 @@
 {
   "description": "Request comes with an alias to a disabled bidder, we should throw error",
   "config": {
-    "disabledAdapters": ["appnexus", "rubicon"]
+    "disabledAdapters": ["appnexus"]
   },
   "mockBidRequest": {
     "id": "some-request-id",
@@ -32,5 +32,5 @@
     }
   },
   "expectedReturnCode": 400,
-  "expectedErrorMessage": "Invalid request: request.ext.prebid.aliases.test1 refers to unknown bidder: appnexus\n"
+  "expectedErrorMessage": "Invalid request: request.ext.prebid.aliases.test1 refers to disabled bidder: appnexus\n"
 }
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/context-type-content-incompatible-subtype.json b/endpoints/openrtb2/sample-requests/invalid-native/context-type-content-incompatible-subtype.json
new file mode 100644
index 00000000..4297aa75
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/invalid-native/context-type-content-incompatible-subtype.json
@@ -0,0 +1,25 @@
+{
+  "description": "Native bid request. Context type content (1) is incompatible with 'social' subcontext types (20~29). Return error",
+  "mockBidRequest": {
+    "id": "req-id",
+    "site": {
+      "page": "some.page.com"
+    },
+    "tmax": 500,
+    "imp": [
+      {
+        "id": "some-imp",
+        "native": {
+          "request": "{\"context\":1,\"contextsubtype\":21,\"plcmttype\":1,\"assets\":[{\"title\":{\"len\":90}},{\"img\":{\"hmin\":30,\"wmin\":20}},{\"video\":{\"mimes\":[\"video/mp4\"],\"minduration\":5,\"maxduration\":10,\"protocols\":[1]}},{\"data\":{\"type\":2}}]}"
+        },
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ]
+  },
+  "expectedReturnCode": 400,
+  "expectedErrorMessage": "Invalid request"
+}
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/context-type-product-incompatible-subtype.json b/endpoints/openrtb2/sample-requests/invalid-native/context-type-product-incompatible-subtype.json
new file mode 100644
index 00000000..31938336
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/invalid-native/context-type-product-incompatible-subtype.json
@@ -0,0 +1,27 @@
+{
+  "description": "Native bid request. Context type product (3) is incompatible with 'social' subcontext types (10~19). Return error",
+  "mockBidRequest": {
+    "id": "req-id",
+    "site": {
+      "page": "some.page.com"
+    },
+    "tmax": 500,
+    "imp": [
+      {
+        "id": "some-imp",
+        "native": {
+          "request": "{\"context\":3,\"contextsubtype\":11,\"plcmttype\":1,\"assets\":[{\"title\":{\"len\":90}},{\"img\":{\"hmin\":30,\"wmin\":20}},{\"video\":{\"mimes\":[\"video/mp4\"],\"minduration\":5,\"maxduration\":10,\"protocols\":[1]}},{\"data\":{\"type\":2}}]}"
+        },
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ]
+  },
+  "expectedReturnCode": 400,
+  "expectedErrorMessage": "Invalid request"
+}
+
+
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/context-type-social-incompatible-subtype.json b/endpoints/openrtb2/sample-requests/invalid-native/context-type-social-incompatible-subtype.json
new file mode 100644
index 00000000..4ecbf449
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/invalid-native/context-type-social-incompatible-subtype.json
@@ -0,0 +1,27 @@
+{
+  "description": "Native bid request. Context type social (2) is incompatible with 'product' subcontext types (30~39). Return error",
+  "mockBidRequest": {
+    "id": "req-id",
+    "site": {
+      "page": "some.page.com"
+    },
+    "tmax": 500,
+    "imp": [
+      {
+        "id": "some-imp",
+        "native": {
+          "request": "{\"context\":2,\"contextsubtype\":31,\"plcmttype\":1,\"assets\":[{\"title\":{\"len\":90}},{\"img\":{\"hmin\":30,\"wmin\":20}},{\"video\":{\"mimes\":[\"video/mp4\"],\"minduration\":5,\"maxduration\":10,\"protocols\":[1]}},{\"data\":{\"type\":2}}]}"
+        },
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ]
+  },
+  "expectedReturnCode": 400,
+  "expectedErrorMessage": "Invalid request"
+}
+
+
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/contextsubtype-greater-than-max.json b/endpoints/openrtb2/sample-requests/invalid-native/contextsubtype-greater-than-max.json
new file mode 100644
index 00000000..9b422380
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/invalid-native/contextsubtype-greater-than-max.json
@@ -0,0 +1,26 @@
+{
+  "description": "Native bid request comes with a subcontext type greater than 500. Return error",
+  "mockBidRequest": {
+    "id": "req-id",
+    "site": {
+      "page": "some.page.com"
+    },
+    "tmax": 500,
+    "imp": [
+      {
+        "id": "some-imp",
+        "native": {
+          "request": "{\"context\":1,\"contextsubtype\":550,\"plcmttype\":1,\"assets\":[{\"title\":{\"len\":90}},{\"img\":{\"hmin\":30,\"wmin\":20}},{\"video\":{\"mimes\":[\"video/mp4\"],\"minduration\":5,\"maxduration\":10,\"protocols\":[1]}},{\"data\":{\"type\":2}}]}"
+        },
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ]
+  },
+  "expectedReturnCode": 400,
+  "expectedErrorMessage": "Invalid request"
+}
+
diff --git a/endpoints/openrtb2/sample-requests/invalid-native/contextsubtype-invalid.json b/endpoints/openrtb2/sample-requests/invalid-native/contextsubtype-invalid.json
index 395e7034..cdf4ef0c 100644
--- a/endpoints/openrtb2/sample-requests/invalid-native/contextsubtype-invalid.json
+++ b/endpoints/openrtb2/sample-requests/invalid-native/contextsubtype-invalid.json
@@ -1,5 +1,5 @@
 {
-  "description": "Bid request with invalid contextsubtype value inside the native.request field in its only imp element",
+  "description": "Native bid request comes with a contextsubtype greater than 32, 'ContextSubTypeProductReview'",
   "mockBidRequest": {
     "id": "req-id",
     "site": {
@@ -10,7 +10,7 @@
       {
         "id": "some-imp",
         "native": {
-          "request": "{\"context\":1,\"contextsubtype\":21,\"plcmttype\":1,\"assets\":[{\"title\":{\"len\":90}},{\"img\":{\"hmin\":30,\"wmin\":20}},{\"video\":{\"mimes\":[\"video/mp4\"],\"minduration\":5,\"maxduration\":10,\"protocols\":[1]}},{\"data\":{\"type\":2}}]}"
+          "request": "{\"context\":1,\"contextsubtype\":41,\"plcmttype\":1,\"assets\":[{\"title\":{\"len\":90}},{\"img\":{\"hmin\":30,\"wmin\":20}},{\"video\":{\"mimes\":[\"video/mp4\"],\"minduration\":5,\"maxduration\":10,\"protocols\":[1]}},{\"data\":{\"type\":2}}]}"
         },
         "ext": {
           "appnexus": {
diff --git a/endpoints/openrtb2/sample-requests/valid-native/context-product-compatible-subtype.json b/endpoints/openrtb2/sample-requests/valid-native/context-product-compatible-subtype.json
new file mode 100644
index 00000000..dbf7b9c5
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/valid-native/context-product-compatible-subtype.json
@@ -0,0 +1,41 @@
+{
+  "description": "Native bid request comes with a context type product (3) and compatible subcontext type",
+  "mockBidRequest": {
+    "id": "req-id",
+    "site": {
+      "page": "some.page.com"
+    },
+    "tmax": 500,
+    "imp": [
+      {
+        "id": "some-imp",
+        "native": {
+          "request": "{\"context\":3,\"contextsubtype\":31,\"plcmttype\":1,\"assets\":[{\"title\":{\"len\":90}},{\"img\":{\"hmin\":30,\"wmin\":20}},{\"video\":{\"mimes\":[\"video/mp4\"],\"minduration\":5,\"maxduration\":10,\"protocols\":[1]}},{\"data\":{\"type\":2}}]}"
+        },
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ]
+  },
+  "expectedBidResponse": {
+    "id": "req-id",
+    "bidid": "test bid id",
+    "nbr": 0,
+      "seatbid": [
+        {
+          "bid": [
+            {
+              "id": "appnexus-bid",
+              "impid": "",
+              "price": 0
+            }
+          ],
+          "seat": "appnexus-bids"
+        }
+      ]
+  },
+  "expectedReturnCode": 200
+}
diff --git a/endpoints/openrtb2/sample-requests/valid-native/context-social-compatible-subtype.json b/endpoints/openrtb2/sample-requests/valid-native/context-social-compatible-subtype.json
new file mode 100644
index 00000000..41fb833d
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/valid-native/context-social-compatible-subtype.json
@@ -0,0 +1,41 @@
+{
+  "description": "Native bid request comes with a context type social (2) and compatible subcontext type",
+  "mockBidRequest": {
+    "id": "req-id",
+    "site": {
+      "page": "some.page.com"
+    },
+    "tmax": 500,
+    "imp": [
+      {
+        "id": "some-imp",
+        "native": {
+          "request": "{\"context\":2,\"contextsubtype\":21,\"plcmttype\":1,\"assets\":[{\"title\":{\"len\":90}},{\"img\":{\"hmin\":30,\"wmin\":20}},{\"video\":{\"mimes\":[\"video/mp4\"],\"minduration\":5,\"maxduration\":10,\"protocols\":[1]}},{\"data\":{\"type\":2}}]}"
+        },
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        }
+      }
+    ]
+  },
+  "expectedBidResponse": {
+    "id": "req-id",
+    "bidid": "test bid id",
+    "nbr": 0,
+      "seatbid": [
+        {
+          "bid": [
+            {
+              "id": "appnexus-bid",
+              "impid": "",
+              "price": 0
+            }
+          ],
+          "seat": "appnexus-bids"
+        }
+      ]
+  },
+  "expectedReturnCode": 200
+}
diff --git a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/skadn.json b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/skadn.json
new file mode 100644
index 00000000..e238f3c0
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/skadn.json
@@ -0,0 +1,48 @@
+{
+    "description": "The imp.ext.skadn field is valid for Apple's SKAdNetwork and should be exempt from bidder name validation.",
+
+    "mockBidRequest": {
+        "id": "some-request-id",
+        "site": {
+            "page": "test.somepage.com"
+        },
+        "imp": [{
+            "id": "some-imp-id",
+            "banner": {
+                "format": [{
+                    "w": 600,
+                    "h": 500
+                }, {
+                    "w": 300,
+                    "h": 600
+                }]
+            },
+            "ext": {
+                "prebid": {
+                    "bidder": {
+                        "appnexus": {
+                            "placementId": 12883451
+                        }
+                    }
+                },
+                "skadn": {
+                    "anyMember": "anyValue"
+                }
+            }
+        }]
+    },
+    "expectedBidResponse": {
+        "id": "some-request-id",
+        "seatbid": [{
+            "bid": [{
+                "id": "appnexus-bid",
+                "impid": "",
+                "price": 0
+            }],
+            "seat": "appnexus-bids"
+        }],
+        "bidid": "test bid id",
+        "nbr": 0
+    },
+    "expectedReturnCode": 200
+}
\ No newline at end of file
diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/us-privacy-invalid.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/us-privacy-invalid.json
new file mode 100644
index 00000000..2ccdfb7c
--- /dev/null
+++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/us-privacy-invalid.json
@@ -0,0 +1,52 @@
+{
+  "description": "Well formed amp request with invalid CCPA consent value",
+  "mockBidRequest": {
+    "id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
+    "site": {
+      "page": "prebid.org",
+      "publisher": {
+        "id": "a3de7af2-a86a-4043-a77b-c7e86744155e"
+      }
+    },
+    "source": {
+      "tid": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5"
+    },
+    "tmax": 1000,
+    "imp": [
+      {
+        "id": "/19968336/header-bid-tag-0",
+        "ext": {
+          "appnexus": {
+            "placementId": 12883451
+          }
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 300
+            }
+          ]
+        }
+      }
+    ],
+    "regs": {
+      "ext": {
+        "us_privacy": "{invalid}"
+      }
+    },
+    "user": {
+      "ext": {}
+    }
+  },
+  "expectedBidResponse": {
+      "id":"b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5",
+      "bidid":"test bid id",
+      "nbr":0
+  },
+  "expectedReturnCode": 200
+}
diff --git a/errortypes/code.go b/errortypes/code.go
index 80a5eb45..2749b978 100644
--- a/errortypes/code.go
+++ b/errortypes/code.go
@@ -17,6 +17,8 @@ const (
 const (
 	UnknownWarningCode               = 10999
 	InvalidPrivacyConsentWarningCode = iota + 10000
+	AccountLevelDebugDisabledWarningCode
+	BidderLevelDebugDisabledWarningCode
 )
 
 // Coder provides an error or warning code with severity.
diff --git a/errortypes/errortypes.go b/errortypes/errortypes.go
index c953f9b7..1fed2d7d 100644
--- a/errortypes/errortypes.go
+++ b/errortypes/errortypes.go
@@ -167,7 +167,8 @@ func (err *BidderTemporarilyDisabled) Severity() Severity {
 
 // Warning is a generic non-fatal error.
 type Warning struct {
-	Message string
+	Message     string
+	WarningCode int
 }
 
 func (err *Warning) Error() string {
@@ -175,26 +176,9 @@ func (err *Warning) Error() string {
 }
 
 func (err *Warning) Code() int {
-	return UnknownWarningCode
+	return err.WarningCode
 }
 
 func (err *Warning) Severity() Severity {
 	return SeverityWarning
 }
-
-// InvalidPrivacyConsent is a warning for when the privacy consent string is invalid and is ignored.
-type InvalidPrivacyConsent struct {
-	Message string
-}
-
-func (err *InvalidPrivacyConsent) Error() string {
-	return err.Message
-}
-
-func (err *InvalidPrivacyConsent) Code() int {
-	return InvalidPrivacyConsentWarningCode
-}
-
-func (err *InvalidPrivacyConsent) Severity() Severity {
-	return SeverityWarning
-}
diff --git a/exchange/bidder.go b/exchange/bidder.go
index f916e205..b0818dfd 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -83,10 +83,6 @@ type pbsOrtbSeatBid struct {
 	// httpCalls is the list of debugging info. It should only be populated if the request.test == 1.
 	// This will become response.ext.debug.httpcalls.{bidder} on the final Response.
 	httpCalls []*openrtb_ext.ExtHttpCall
-	// ext contains the extension for this seatbid.
-	// if len(bids) > 0, this will become response.seatbid[i].ext.{bidder} on the final OpenRTB response.
-	// if len(bids) == 0, this will be ignored because the OpenRTB spec doesn't allow a SeatBid with 0 Bids.
-	ext json.RawMessage
 }
 
 // adaptBidder converts an adapters.Bidder into an exchange.adaptedBidder.
@@ -168,9 +164,17 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.Bi
 		// - debugContextKey (url param) in true
 		// - account debug is allowed
 		// - bidder debug is allowed
-		if accountDebugAllowed && bidder.config.DebugInfo.Allow {
-			if debugInfo := ctx.Value(DebugContextKey); debugInfo != nil && debugInfo.(bool) {
-				seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
+		if debugInfo := ctx.Value(DebugContextKey); debugInfo != nil && debugInfo.(bool) {
+			if accountDebugAllowed {
+				if bidder.config.DebugInfo.Allow {
+					seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
+				} else {
+					debugDisabledWarning := errortypes.Warning{
+						WarningCode: errortypes.BidderLevelDebugDisabledWarningCode,
+						Message:     "debug turned off for bidder",
+					}
+					errs = append(errs, &debugDisabledWarning)
+				}
 			}
 		}
 
diff --git a/exchange/exchange.go b/exchange/exchange.go
index b4fa3a61..d8ac60ba 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -66,7 +66,8 @@ type exchange struct {
 // Container to pass out response ext data from the GetAllBids goroutines back into the main thread
 type seatResponseExtra struct {
 	ResponseTimeMillis int
-	Errors             []openrtb_ext.ExtBidderError
+	Errors             []openrtb_ext.ExtBidderMessage
+	Warnings           []openrtb_ext.ExtBidderMessage
 	// httpCalls is the list of debugging info. It should only be populated if the request.test == 1.
 	// This will become response.ext.debug.httpcalls.{bidder} on the final Response.
 	HttpCalls []*openrtb_ext.ExtHttpCall
@@ -106,6 +107,7 @@ type AuctionRequest struct {
 	UserSyncs   IdFetcher
 	RequestType metrics.RequestType
 	StartTime   time.Time
+	Warnings    []error
 
 	// LegacyLabels is included here for temporary compatability with cleanOpenRTBRequests
 	// in HoldAuction until we get to factoring it away. Do not use for anything new.
@@ -138,9 +140,9 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 		debugLog = &DebugLog{Enabled: false}
 	}
 
-	debugInfo := getDebugInfo(r.BidRequest, requestExt)
+	requestDebugInfo := getDebugInfo(r.BidRequest, requestExt)
 
-	debugInfo = debugInfo && r.Account.DebugAllow
+	debugInfo := requestDebugInfo && r.Account.DebugAllow
 	debugLog.Enabled = debugLog.Enabled && r.Account.DebugAllow
 
 	if debugInfo {
@@ -236,6 +238,22 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 		}
 	}
 
+	if !r.Account.DebugAllow && requestDebugInfo {
+		accountDebugDisabledWarning := openrtb_ext.ExtBidderMessage{
+			Code:    errortypes.AccountLevelDebugDisabledWarningCode,
+			Message: "debug turned off for account",
+		}
+		bidResponseExt.Warnings[openrtb_ext.BidderReservedGeneral] = append(bidResponseExt.Warnings[openrtb_ext.BidderReservedGeneral], accountDebugDisabledWarning)
+	}
+
+	for _, warning := range r.Warnings {
+		generalWarning := openrtb_ext.ExtBidderMessage{
+			Code:    errortypes.ReadCode(warning),
+			Message: warning.Error(),
+		}
+		bidResponseExt.Warnings[openrtb_ext.BidderReservedGeneral] = append(bidResponseExt.Warnings[openrtb_ext.BidderReservedGeneral], generalWarning)
+	}
+
 	// Build the response
 	return e.buildBidResponse(ctx, liveAdapters, adapterBids, r.BidRequest, adapterExtra, auc, bidResponseExt, cacheInstructions.returnCreative, errs)
 }
@@ -397,11 +415,11 @@ func (e *exchange) getAllBids(
 
 			// Timing statistics
 			e.me.RecordAdapterTime(bidderRequest.BidderLabels, time.Since(start))
-			serr := errsToBidderErrors(err)
 			bidderRequest.BidderLabels.AdapterBids = bidsToMetric(brw.adapterBids)
 			bidderRequest.BidderLabels.AdapterErrors = errorsToMetric(err)
 			// Append any bid validation errors to the error list
-			ae.Errors = serr
+			ae.Errors = errsToBidderErrors(err)
+			ae.Warnings = errsToBidderWarnings(err)
 			brw.adapterExtra = ae
 			if bids != nil {
 				for _, bid := range bids.bids {
@@ -494,13 +512,29 @@ func errorsToMetric(errs []error) map[metrics.AdapterError]struct{} {
 	return ret
 }
 
-func errsToBidderErrors(errs []error) []openrtb_ext.ExtBidderError {
-	serr := make([]openrtb_ext.ExtBidderError, len(errs))
-	for i := 0; i < len(errs); i++ {
-		serr[i].Code = errortypes.ReadCode(errs[i])
-		serr[i].Message = errs[i].Error()
+func errsToBidderErrors(errs []error) []openrtb_ext.ExtBidderMessage {
+	sErr := make([]openrtb_ext.ExtBidderMessage, 0)
+	for _, err := range errortypes.FatalOnly(errs) {
+		newErr := openrtb_ext.ExtBidderMessage{
+			Code:    errortypes.ReadCode(err),
+			Message: err.Error(),
+		}
+		sErr = append(sErr, newErr)
 	}
-	return serr
+
+	return sErr
+}
+
+func errsToBidderWarnings(errs []error) []openrtb_ext.ExtBidderMessage {
+	sWarn := make([]openrtb_ext.ExtBidderMessage, 0)
+	for _, warn := range errortypes.WarningOnly(errs) {
+		newErr := openrtb_ext.ExtBidderMessage{
+			Code:    errortypes.ReadCode(warn),
+			Message: warn.Error(),
+		}
+		sWarn = append(sWarn, newErr)
+	}
+	return sWarn
 }
 
 // This piece takes all the bids supplied by the adapters and crafts an openRTB response to send back to the requester
@@ -749,7 +783,8 @@ func getPrimaryAdServer(adServerId int) (string, error) {
 func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, r AuctionRequest, debugInfo bool, errList []error) *openrtb_ext.ExtBidResponse {
 	req := r.BidRequest
 	bidResponseExt := &openrtb_ext.ExtBidResponse{
-		Errors:               make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError, len(adapterBids)),
+		Errors:               make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage, len(adapterBids)),
+		Warnings:             make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage, len(adapterBids)),
 		ResponseTimeMillis:   make(map[openrtb_ext.BidderName]int, len(adapterBids)),
 		RequestTimeoutMillis: req.TMax,
 	}
@@ -771,6 +806,9 @@ func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pb
 		if debugInfo && len(responseExtra.HttpCalls) > 0 {
 			bidResponseExt.Debug.HttpCalls[bidderName] = responseExtra.HttpCalls
 		}
+		if len(responseExtra.Warnings) > 0 {
+			bidResponseExt.Warnings[bidderName] = responseExtra.Warnings
+		}
 		// Only make an entry for bidder errors if the bidder reported any.
 		if len(responseExtra.Errors) > 0 {
 			bidResponseExt.Errors[bidderName] = responseExtra.Errors
@@ -788,25 +826,9 @@ func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pb
 // Return an openrtb seatBid for a bidder
 // BuildBidResponse is responsible for ensuring nil bid seatbids are not included
 func (e *exchange) makeSeatBid(adapterBid *pbsOrtbSeatBid, adapter openrtb_ext.BidderName, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, returnCreative bool) *openrtb.SeatBid {
-	seatBid := new(openrtb.SeatBid)
-	seatBid.Seat = adapter.String()
-	// Prebid cannot support roadblocking
-	seatBid.Group = 0
-
-	if len(adapterBid.ext) > 0 {
-		sbExt := ExtSeatBid{
-			Bidder: adapterBid.ext,
-		}
-
-		ext, err := json.Marshal(sbExt)
-		if err != nil {
-			extError := openrtb_ext.ExtBidderError{
-				Code:    errortypes.ReadCode(err),
-				Message: fmt.Sprintf("Error writing SeatBid.Ext: %s", err.Error()),
-			}
-			adapterExtra[adapter].Errors = append(adapterExtra[adapter].Errors, extError)
-		}
-		seatBid.Ext = ext
+	seatBid := &openrtb.SeatBid{
+		Seat:  adapter.String(),
+		Group: 0, // Prebid cannot support roadblocking
 	}
 
 	var errList []error
@@ -818,39 +840,54 @@ func (e *exchange) makeSeatBid(adapterBid *pbsOrtbSeatBid, adapter openrtb_ext.B
 	return seatBid
 }
 
-// Create the Bid array inside of SeatBid
-func (e *exchange) makeBid(Bids []*pbsOrtbBid, auc *auction, returnCreative bool) ([]openrtb.Bid, []error) {
-	bids := make([]openrtb.Bid, 0, len(Bids))
-	errList := make([]error, 0, 1)
-	for _, thisBid := range Bids {
-		bidExt := &openrtb_ext.ExtBid{
-			Bidder: thisBid.bid.Ext,
-			Prebid: &openrtb_ext.ExtBidPrebid{
-				Targeting:         thisBid.bidTargets,
-				Type:              thisBid.bidType,
-				Video:             thisBid.bidVideo,
-				Events:            thisBid.bidEvents,
-				DealPriority:      thisBid.dealPriority,
-				DealTierSatisfied: thisBid.dealTierSatisfied,
-			},
-		}
-		if cacheInfo, found := e.getBidCacheInfo(thisBid, auc); found {
-			bidExt.Prebid.Cache = &openrtb_ext.ExtBidPrebidCache{
+func (e *exchange) makeBid(bids []*pbsOrtbBid, auc *auction, returnCreative bool) ([]openrtb.Bid, []error) {
+	result := make([]openrtb.Bid, 0, len(bids))
+	errs := make([]error, 0, 1)
+
+	for _, bid := range bids {
+		bidExtPrebid := &openrtb_ext.ExtBidPrebid{
+			DealPriority:      bid.dealPriority,
+			DealTierSatisfied: bid.dealTierSatisfied,
+			Events:            bid.bidEvents,
+			Targeting:         bid.bidTargets,
+			Type:              bid.bidType,
+			Video:             bid.bidVideo,
+		}
+
+		if cacheInfo, found := e.getBidCacheInfo(bid, auc); found {
+			bidExtPrebid.Cache = &openrtb_ext.ExtBidPrebidCache{
 				Bids: &cacheInfo,
 			}
 		}
-		ext, err := json.Marshal(bidExt)
-		if err != nil {
-			errList = append(errList, err)
+
+		if bidExtJSON, err := makeBidExtJSON(bid.bid.Ext, bidExtPrebid); err != nil {
+			errs = append(errs, err)
 		} else {
-			bids = append(bids, *thisBid.bid)
-			bids[len(bids)-1].Ext = ext
+			result = append(result, *bid.bid)
+			resultBid := &result[len(result)-1]
+			resultBid.Ext = bidExtJSON
 			if !returnCreative {
-				bids[len(bids)-1].AdM = ""
+				resultBid.AdM = ""
 			}
 		}
 	}
-	return bids, errList
+	return result, errs
+}
+
+func makeBidExtJSON(ext json.RawMessage, prebid *openrtb_ext.ExtBidPrebid) (json.RawMessage, error) {
+	// no existing bid.ext. generate a bid.ext with just our prebid section populated.
+	if len(ext) == 0 {
+		bidExt := &openrtb_ext.ExtBid{Prebid: prebid}
+		return json.Marshal(bidExt)
+	}
+
+	// update existing bid.ext with our prebid section. if bid.ext.prebid already exists, it will be overwritten.
+	var extMap map[string]interface{}
+	if err := json.Unmarshal(ext, &extMap); err != nil {
+		return nil, err
+	}
+	extMap[openrtb_ext.PrebidExtKey] = prebid
+	return json.Marshal(extMap)
 }
 
 // If bid got cached inside `(a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, targData *targetData, bidRequest *openrtb.BidRequest, ttlBuffer int64, defaultTTLs *config.DefaultTTLs, bidCategory map[string]string)`,
diff --git a/exchange/seatbid.go b/exchange/seatbid.go
deleted file mode 100644
index b6751274..00000000
--- a/exchange/seatbid.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package exchange
-
-import "encoding/json"
-
-// ExtSeatBid defines the contract for bidresponse.seatbid.ext
-type ExtSeatBid struct {
-	Bidder json.RawMessage `json:"bidder,omitempty"`
-}
diff --git a/exchange/utils.go b/exchange/utils.go
index 8d4c0fac..4ca02149 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -59,8 +59,9 @@ func cleanOpenRTBRequests(ctx context.Context,
 	usersyncIfAmbiguous bool,
 	privacyConfig config.Privacy) (bidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) {
 
-	impsByBidder, errs := splitImps(req.BidRequest.Imp)
-	if len(errs) > 0 {
+	impsByBidder, err := splitImps(req.BidRequest.Imp)
+	if err != nil {
+		errs = []error{err}
 		return
 	}
 
@@ -327,106 +328,99 @@ func extractBuyerUIDs(user *openrtb.User) (map[string]string, error) {
 // The "imp.ext" value of the rubicon Imp will only contain the "prebid" values, and "rubicon" value at the "bidder" key.
 //
 // The goal here is so that Bidders only get Imps and Imp.Ext values which are intended for them.
-func splitImps(imps []openrtb.Imp) (map[string][]openrtb.Imp, []error) {
-	impExts, err := parseImpExts(imps)
-	if err != nil {
-		return nil, []error{err}
-	}
+func splitImps(imps []openrtb.Imp) (map[string][]openrtb.Imp, error) {
+	bidderImps := make(map[string][]openrtb.Imp)
 
-	splitImps := make(map[string][]openrtb.Imp, len(imps))
-	var errList []error
+	for i, imp := range imps {
+		var impExt map[string]json.RawMessage
+		if err := json.Unmarshal(imp.Ext, &impExt); err != nil {
+			return nil, fmt.Errorf("invalid json for imp[%d]: %v", i, err)
+		}
 
-	for i := 0; i < len(imps); i++ {
-		imp := imps[i]
-		impExt := impExts[i]
+		var impExtPrebid map[string]json.RawMessage
+		if impExtPrebidJSON, exists := impExt[openrtb_ext.PrebidExtKey]; exists {
+			// validation already performed by impExt unmarshal. no error is possible here, proven by tests.
+			json.Unmarshal(impExtPrebidJSON, &impExtPrebid)
+		}
 
-		var firstPartyDataContext json.RawMessage
-		if context, exists := impExt[openrtb_ext.FirstPartyDataContextExtKey]; exists {
-			firstPartyDataContext = context
+		var impExtPrebidBidder map[string]json.RawMessage
+		if impExtPrebidBidderJSON, exists := impExtPrebid[openrtb_ext.PrebidExtBidderKey]; exists {
+			// validation already performed by impExt unmarshal. no error is possible here, proven by tests.
+			json.Unmarshal(impExtPrebidBidderJSON, &impExtPrebidBidder)
 		}
 
-		rawPrebidExt, ok := impExt[openrtb_ext.PrebidExtKey]
+		sanitizedImpExt, err := createSanitizedImpExt(impExt, impExtPrebid)
+		if err != nil {
+			return nil, fmt.Errorf("unable to remove other bidder fields for imp[%d]: %v", i, err)
+		}
 
-		if ok {
-			var prebidExt openrtb_ext.ExtImpPrebid
+		for bidder, bidderExt := range extractBidderExts(impExt, impExtPrebidBidder) {
+			impCopy := imp
 
-			if err := json.Unmarshal(rawPrebidExt, &prebidExt); err == nil && prebidExt.Bidder != nil {
-				if errs := sanitizedImpCopy(&imp, prebidExt.Bidder, rawPrebidExt, firstPartyDataContext, &splitImps); errs != nil {
-					errList = append(errList, errs...)
-				}
+			sanitizedImpExt[openrtb_ext.PrebidExtBidderKey] = bidderExt
 
-				continue
+			impExtJSON, err := json.Marshal(sanitizedImpExt)
+			if err != nil {
+				return nil, fmt.Errorf("unable to remove other bidder fields for imp[%d]: cannot marshal ext: %v", i, err)
 			}
-		}
+			impCopy.Ext = impExtJSON
 
-		if errs := sanitizedImpCopy(&imp, impExt, rawPrebidExt, firstPartyDataContext, &splitImps); errs != nil {
-			errList = append(errList, errs...)
+			bidderImps[bidder] = append(bidderImps[bidder], impCopy)
 		}
 	}
 
-	return splitImps, nil
+	return bidderImps, nil
 }
 
-// sanitizedImpCopy returns a copy of imp with its ext filtered so that only "prebid", "context", and bidder params exist.
-// It will not mutate the input imp.
-// This function will write the new imps to the output map passed in
-func sanitizedImpCopy(imp *openrtb.Imp,
-	bidderExts map[string]json.RawMessage,
-	rawPrebidExt json.RawMessage,
-	firstPartyDataContext json.RawMessage,
-	out *map[string][]openrtb.Imp) []error {
-
-	var prebidExt map[string]json.RawMessage
-	var errs []error
-
-	if err := json.Unmarshal(rawPrebidExt, &prebidExt); err == nil {
-		// Remove the entire bidder field. We will already have the content we need in bidderExts. We
-		// don't want to include other demand partners' bidder params in the sanitized imp.
-		if _, hasBidderField := prebidExt["bidder"]; hasBidderField {
-			delete(prebidExt, "bidder")
+func createSanitizedImpExt(impExt, impExtPrebid map[string]json.RawMessage) (map[string]json.RawMessage, error) {
+	sanitizedImpExt := make(map[string]json.RawMessage, 3)
 
-			var err error
-			if rawPrebidExt, err = json.Marshal(prebidExt); err != nil {
-				errs = append(errs, err)
-			}
+	delete(impExtPrebid, openrtb_ext.PrebidExtBidderKey)
+	if len(impExtPrebid) > 0 {
+		if impExtPrebidJSON, err := json.Marshal(impExtPrebid); err == nil {
+			sanitizedImpExt[openrtb_ext.PrebidExtKey] = impExtPrebidJSON
+		} else {
+			return nil, fmt.Errorf("cannot marshal ext.prebid: %v", err)
 		}
 	}
 
-	for bidder, ext := range bidderExts {
-		if bidder == openrtb_ext.PrebidExtKey || bidder == openrtb_ext.FirstPartyDataContextExtKey {
-			continue
-		}
+	if v, exists := impExt[openrtb_ext.FirstPartyDataContextExtKey]; exists {
+		sanitizedImpExt[openrtb_ext.FirstPartyDataContextExtKey] = v
+	}
 
-		impCopy := *imp
-		newExt := make(map[string]json.RawMessage, 3)
+	if v, exists := impExt[openrtb_ext.SKAdNExtKey]; exists {
+		sanitizedImpExt[openrtb_ext.SKAdNExtKey] = v
+	}
 
-		newExt["bidder"] = ext
+	return sanitizedImpExt, nil
+}
 
-		if rawPrebidExt != nil {
-			newExt[openrtb_ext.PrebidExtKey] = rawPrebidExt
-		}
+func extractBidderExts(impExt, impExtPrebidBidders map[string]json.RawMessage) map[string]json.RawMessage {
+	bidderExts := make(map[string]json.RawMessage)
 
-		if len(firstPartyDataContext) > 0 {
-			newExt[openrtb_ext.FirstPartyDataContextExtKey] = firstPartyDataContext
-		}
+	// prefer imp.ext.prebid.bidder.BIDDER
+	for bidder, bidderExt := range impExtPrebidBidders {
+		bidderExts[bidder] = bidderExt
+	}
 
-		rawExt, err := json.Marshal(newExt)
-		if err != nil {
-			errs = append(errs, err)
+	// fallback to imp.BIDDER
+	for bidder, bidderExt := range impExt {
+		if isSpecialField(bidder) {
 			continue
 		}
 
-		impCopy.Ext = rawExt
-
-		otherImps, _ := (*out)[bidder]
-		(*out)[bidder] = append(otherImps, impCopy)
+		if _, exists := bidderExts[bidder]; !exists {
+			bidderExts[bidder] = bidderExt
+		}
 	}
 
-	if len(errs) > 0 {
-		return errs
-	}
+	return bidderExts
+}
 
-	return nil
+func isSpecialField(bidder string) bool {
+	return bidder == openrtb_ext.FirstPartyDataContextExtKey ||
+		bidder == openrtb_ext.SKAdNExtKey ||
+		bidder == openrtb_ext.PrebidExtKey
 }
 
 // prepareUser changes req.User so that it's ready for the given bidder.
@@ -564,21 +558,6 @@ func resolveBidder(bidder string, aliases map[string]string) openrtb_ext.BidderN
 	return openrtb_ext.BidderName(bidder)
 }
 
-// parseImpExts does a partial-unmarshal of the imp[].Ext field.
-// The keys in the returned map are expected to be "prebid", "context", CoreBidderNames, or Aliases for this request.
-func parseImpExts(imps []openrtb.Imp) ([]map[string]json.RawMessage, error) {
-	exts := make([]map[string]json.RawMessage, len(imps))
-	// Loop over every impression in the request
-	for i := 0; i < len(imps); i++ {
-		// Unpack each set of extensions found in the Imp array
-		err := json.Unmarshal(imps[i].Ext, &exts[i])
-		if err != nil {
-			return nil, fmt.Errorf("Error unpacking extensions for Imp[%d]: %s", i, err.Error())
-		}
-	}
-	return exts, nil
-}
-
 // parseAliases parses the aliases from the BidRequest
 func parseAliases(orig *openrtb.BidRequest) (map[string]string, []error) {
 	var aliases map[string]string
diff --git a/main.go b/main.go
index 2ae75a2e..ebeabf29 100644
--- a/main.go
+++ b/main.go
@@ -20,7 +20,7 @@ import (
 // Rev holds binary revision string
 // Set manually at build time using:
 //    go build -ldflags "-X main.Rev=`git rev-parse --short HEAD`"
-// Populated automatically at build / release time via .travis.yml
+// Populated automatically at build / releases
 //   `gox -os="linux" -arch="386" -output="{{.Dir}}_{{.OS}}_{{.Arch}}" -ldflags "-X main.Rev=`git rev-parse --short HEAD`" -verbose ./...;`
 // See issue #559
 var Rev string
diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go
index 8f4d7a09..7d787cf8 100644
--- a/openrtb_ext/bid.go
+++ b/openrtb_ext/bid.go
@@ -1,14 +1,12 @@
 package openrtb_ext
 
 import (
-	"encoding/json"
 	"fmt"
 )
 
 // ExtBid defines the contract for bidresponse.seatbid.bid[i].ext
 type ExtBid struct {
-	Prebid *ExtBidPrebid   `json:"prebid,omitempty"`
-	Bidder json.RawMessage `json:"bidder,omitempty"`
+	Prebid *ExtBidPrebid `json:"prebid,omitempty"`
 }
 
 // ExtBidPrebid defines the contract for bidresponse.seatbid.bid[i].ext.prebid
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 57070160..e03c234b 100755
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -35,7 +35,7 @@ const (
 	BidderReservedData    BidderName = "data"    // Reserved for first party data.
 	BidderReservedGeneral BidderName = "general" // Reserved for non-bidder specific messages when using a map keyed on the bidder name.
 	BidderReservedPrebid  BidderName = "prebid"  // Reserved for Prebid Server configuration.
-	BidderReservedSKAdN   BidderName = "skadn"   // Reserved for SKAdNetwork OpenRTB extension.
+	BidderReservedSKAdN   BidderName = "skadn"   // Reserved for Apple's SKAdNetwork OpenRTB extension.
 )
 
 // IsBidderNameReserved returns true if the specified name is a case insensitive match for a reserved bidder name.
diff --git a/openrtb_ext/device.go b/openrtb_ext/device.go
index eb465511..cc06f380 100644
--- a/openrtb_ext/device.go
+++ b/openrtb_ext/device.go
@@ -12,6 +12,9 @@ import (
 // PrebidExtKey represents the prebid extension key used in requests
 const PrebidExtKey = "prebid"
 
+// PrebidExtBidderKey represents the field name within request.imp.ext.prebid reserved for bidder params.
+const PrebidExtBidderKey = "bidder"
+
 // ExtDevice defines the contract for bidrequest.device.ext
 type ExtDevice struct {
 	// Attribute:
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index 124e050a..b2bb25e7 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -5,9 +5,12 @@ import (
 	"errors"
 )
 
-// FirstPartyDataContextExtKey defines the field name within bidrequest.ext reserved
-// for first party data support.
-const FirstPartyDataContextExtKey string = "context"
+// FirstPartyDataContextExtKey defines the field name within request.ext reserved for first party data.
+const FirstPartyDataContextExtKey = "context"
+
+// SKAdNExtKey defines the field name within request.ext reserved for Apple's SKAdNetwork.
+const SKAdNExtKey = "skadn"
+
 const MaxDecimalFigures int = 15
 
 // ExtRequest defines the contract for bidrequest.ext
diff --git a/openrtb_ext/response.go b/openrtb_ext/response.go
index 02370d19..d517704f 100644
--- a/openrtb_ext/response.go
+++ b/openrtb_ext/response.go
@@ -8,7 +8,8 @@ import (
 type ExtBidResponse struct {
 	Debug *ExtResponseDebug `json:"debug,omitempty"`
 	// Errors defines the contract for bidresponse.ext.errors
-	Errors map[BidderName][]ExtBidderError `json:"errors,omitempty"`
+	Errors   map[BidderName][]ExtBidderMessage `json:"errors,omitempty"`
+	Warnings map[BidderName][]ExtBidderMessage `json:"warnings,omitempty"`
 	// ResponseTimeMillis defines the contract for bidresponse.ext.responsetimemillis
 	ResponseTimeMillis map[BidderName]int `json:"responsetimemillis,omitempty"`
 	// RequestTimeoutMillis returns the timeout used in the auction.
@@ -47,8 +48,8 @@ type ExtUserSync struct {
 	Type UserSyncType `json:"type"`
 }
 
-// ExtBidderError defines an error object to be returned, consiting of a machine readable error code, and a human readable error message string.
-type ExtBidderError struct {
+// ExtBidderMessage defines an error object to be returned, consiting of a machine readable error code, and a human readable error message string.
+type ExtBidderMessage struct {
 	Code    int    `json:"code"`
 	Message string `json:"message"`
 }
diff --git a/privacy/ccpa/parsedpolicy.go b/privacy/ccpa/parsedpolicy.go
index 3c934e67..7b9c2d1f 100644
--- a/privacy/ccpa/parsedpolicy.go
+++ b/privacy/ccpa/parsedpolicy.go
@@ -43,7 +43,10 @@ func (p Policy) Parse(validBidders map[string]struct{}) (ParsedPolicy, error) {
 	consentOptOut, err := parseConsent(p.Consent)
 	if err != nil {
 		msg := fmt.Sprintf("request.regs.ext.us_privacy %s", err.Error())
-		return ParsedPolicy{}, &errortypes.InvalidPrivacyConsent{Message: msg}
+		return ParsedPolicy{}, &errortypes.Warning{
+			Message:     msg,
+			WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
+		}
 	}
 
 	noSaleForAllBidders, noSaleSpecificBidders, err := parseNoSaleBidders(p.NoSaleBidders, validBidders)
diff --git a/router/router.go b/router/router.go
index 7a088bbf..0920f1d6 100644
--- a/router/router.go
+++ b/router/router.go
@@ -217,7 +217,6 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	// Metrics engine
 	r.MetricsEngine = metricsConf.NewMetricsEngine(cfg, legacyBidderList)
 	db, shutdown, fetcher, ampFetcher, accounts, categoriesFetcher, videoFetcher := storedRequestsConf.NewStoredRequests(cfg, r.MetricsEngine, generalHttpClient, r.Router)
-
 	// todo(zachbadgett): better shutdown
 	r.Shutdown = shutdown
 	if err := loadDataCache(cfg, db); err != nil {
@@ -246,7 +245,8 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	}
 
 	syncers := usersyncers.NewSyncerMap(cfg)
-	gdprPerms := gdpr.NewPermissions(context.Background(), cfg.GDPR, adapters.GDPRAwareSyncerIDs(syncers), generalHttpClient)
+	gvlVendorIDs := bidderInfos.ToGVLVendorIDMap()
+	gdprPerms := gdpr.NewPermissions(context.Background(), cfg.GDPR, gvlVendorIDs, generalHttpClient)
 
 	exchanges = newExchangeMap(cfg)
 	cacheClient := pbc.NewClient(cacheHttpClient, &cfg.CacheURL, &cfg.ExtCacheURL, r.MetricsEngine)
diff --git a/static/bidder-info/33across.yaml b/static/bidder-info/33across.yaml
index e3eea765..044cfe14 100644
--- a/static/bidder-info/33across.yaml
+++ b/static/bidder-info/33across.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "headerbidding@33across.com"
+gvlVendorID: 58
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/acuityads.yaml b/static/bidder-info/acuityads.yaml
index 9da1446d..9539e36b 100644
--- a/static/bidder-info/acuityads.yaml
+++ b/static/bidder-info/acuityads.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "integrations@acuityads.com"
+gvlVendorID: 231
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/adform.yaml b/static/bidder-info/adform.yaml
index 4dce10b9..461714ac 100644
--- a/static/bidder-info/adform.yaml
+++ b/static/bidder-info/adform.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "scope.sspp@adform.com"
+gvlVendorID: 50
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/adkernel.yaml b/static/bidder-info/adkernel.yaml
index 26f8c322..a78b3cde 100644
--- a/static/bidder-info/adkernel.yaml
+++ b/static/bidder-info/adkernel.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid-dev@adkernel.com"
+gvlVendorID: 14
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/adkernelAdn.yaml b/static/bidder-info/adkernelAdn.yaml
index fd47367d..1f54f8e5 100644
--- a/static/bidder-info/adkernelAdn.yaml
+++ b/static/bidder-info/adkernelAdn.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid-dev@adkernel.com"
+gvlVendorID: 14
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/adman.yaml b/static/bidder-info/adman.yaml
index 932ef2e4..2db7c075 100644
--- a/static/bidder-info/adman.yaml
+++ b/static/bidder-info/adman.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@admanmedia.com"
+gvlVendorID: 149
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/admixer.yaml b/static/bidder-info/admixer.yaml
index 64ad2024..9faf0eb3 100644
--- a/static/bidder-info/admixer.yaml
+++ b/static/bidder-info/admixer.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@admixer.net"
+gvlVendorID: 511
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/adocean.yaml b/static/bidder-info/adocean.yaml
index 2f31fe92..b3a23718 100644
--- a/static/bidder-info/adocean.yaml
+++ b/static/bidder-info/adocean.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "aoteam@gemius.com"
+gvlVendorID: 328
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/adpone.yaml b/static/bidder-info/adpone.yaml
index 969983a1..7c5b4737 100644
--- a/static/bidder-info/adpone.yaml
+++ b/static/bidder-info/adpone.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "tech@adpone.com"
+gvlVendorID: 799
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/adtelligent.yaml b/static/bidder-info/adtelligent.yaml
index 7a20d52b..30f55ea9 100644
--- a/static/bidder-info/adtelligent.yaml
+++ b/static/bidder-info/adtelligent.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "hb@adtelligent.com"
+gvlVendorID: 410
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/adyoulike.yaml b/static/bidder-info/adyoulike.yaml
index 3f35aa5b..d9b23d6c 100644
--- a/static/bidder-info/adyoulike.yaml
+++ b/static/bidder-info/adyoulike.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "core@adyoulike.com"
+gvlVendorID: 259
 modifyingVastXmlAllowed: true
 capabilities:
   site:
diff --git a/static/bidder-info/amx.yaml b/static/bidder-info/amx.yaml
index 3e20d209..f9fdfbb4 100644
--- a/static/bidder-info/amx.yaml
+++ b/static/bidder-info/amx.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@amxrtb.com"
+gvlVendorID: 737
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/appnexus.yaml b/static/bidder-info/appnexus.yaml
index f1e7ca23..f2f4a126 100644
--- a/static/bidder-info/appnexus.yaml
+++ b/static/bidder-info/appnexus.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid-server@xandr.com"
+gvlVendorID: 32
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/avocet.yaml b/static/bidder-info/avocet.yaml
index ea98982d..42147c96 100644
--- a/static/bidder-info/avocet.yaml
+++ b/static/bidder-info/avocet.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "developers@avocet.io"
+gvlVendorID: 63
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/beachfront.yaml b/static/bidder-info/beachfront.yaml
index 335545ff..06991698 100644
--- a/static/bidder-info/beachfront.yaml
+++ b/static/bidder-info/beachfront.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@beachfront.com"
+gvlVendorID: 335
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/beintoo.yaml b/static/bidder-info/beintoo.yaml
index fcca2922..905d89a4 100644
--- a/static/bidder-info/beintoo.yaml
+++ b/static/bidder-info/beintoo.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "adops@beintoo.com"
+gvlVendorID: 618
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/between.yaml b/static/bidder-info/between.yaml
index d317d275..71bd8ba6 100644
--- a/static/bidder-info/between.yaml
+++ b/static/bidder-info/between.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "buying@betweenx.com"
+gvlVendorID: 724
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/brightroll.yaml b/static/bidder-info/brightroll.yaml
index f913be6d..a2ea0c74 100644
--- a/static/bidder-info/brightroll.yaml
+++ b/static/bidder-info/brightroll.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "dsp-supply-prebid@verizonmedia.com"
+gvlVendorID: 25
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/connectad.yaml b/static/bidder-info/connectad.yaml
index 1b3e593d..fa0ae452 100644
--- a/static/bidder-info/connectad.yaml
+++ b/static/bidder-info/connectad.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "support@connectad.io"
+gvlVendorID: 138
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/consumable.yaml b/static/bidder-info/consumable.yaml
index f12ab2b4..e9b5f726 100644
--- a/static/bidder-info/consumable.yaml
+++ b/static/bidder-info/consumable.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "naffis@consumable.com"
+gvlVendorID: 591
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/conversant.yaml b/static/bidder-info/conversant.yaml
index 017f0e0c..aa3d3822 100644
--- a/static/bidder-info/conversant.yaml
+++ b/static/bidder-info/conversant.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "CNVR_PublisherIntegration@conversantmedia.com"
+gvlVendorID: 24
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/deepintent.yaml b/static/bidder-info/deepintent.yaml
index 490e161c..a8f17a55 100644
--- a/static/bidder-info/deepintent.yaml
+++ b/static/bidder-info/deepintent.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebiddev@deepintent.com"
+gvlVendorID: 541
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/dmx.yaml b/static/bidder-info/dmx.yaml
index 3ecb452f..d29d699d 100644
--- a/static/bidder-info/dmx.yaml
+++ b/static/bidder-info/dmx.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@districtm.net"
+gvlVendorID: 144
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/emx_digital.yaml b/static/bidder-info/emx_digital.yaml
index 49a06809..ec0d090f 100644
--- a/static/bidder-info/emx_digital.yaml
+++ b/static/bidder-info/emx_digital.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "adops@emxdigital.com"
+gvlVendorID: 183
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/engagebdr.yaml b/static/bidder-info/engagebdr.yaml
index 57c359e4..fd08367a 100644
--- a/static/bidder-info/engagebdr.yaml
+++ b/static/bidder-info/engagebdr.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "tech@engagebdr.com"
+gvlVendorID: 62
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/eplanning.yaml b/static/bidder-info/eplanning.yaml
index 907523d3..ab0b7609 100644
--- a/static/bidder-info/eplanning.yaml
+++ b/static/bidder-info/eplanning.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "producto@e-planning.net"
+gvlVendorID: 90
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/gamoshi.yaml b/static/bidder-info/gamoshi.yaml
index c3ed3ff1..0cfd4957 100644
--- a/static/bidder-info/gamoshi.yaml
+++ b/static/bidder-info/gamoshi.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "dev@gamoshi.com"
+gvlVendorID: 644
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/grid.yaml b/static/bidder-info/grid.yaml
index 325421a2..31c7b732 100644
--- a/static/bidder-info/grid.yaml
+++ b/static/bidder-info/grid.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "grid-tech@themediagrid.com"
+gvlVendorID: 686
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/gumgum.yaml b/static/bidder-info/gumgum.yaml
index 6ba563b4..bfefe63a 100644
--- a/static/bidder-info/gumgum.yaml
+++ b/static/bidder-info/gumgum.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@gumgum.com"
+gvlVendorID: 61
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/improvedigital.yaml b/static/bidder-info/improvedigital.yaml
index 7983cbc1..f7fea4a8 100644
--- a/static/bidder-info/improvedigital.yaml
+++ b/static/bidder-info/improvedigital.yaml
@@ -1,11 +1,14 @@
 maintainer:
   email: "hb@azerion.com"
+gvlVendorID: 253
 capabilities:
   app:
     mediaTypes:
       - banner
       - video
+      - native
   site:
     mediaTypes:
       - banner
       - video
+      - native
diff --git a/static/bidder-info/invibes.yaml b/static/bidder-info/invibes.yaml
index 14325297..45184fb9 100644
--- a/static/bidder-info/invibes.yaml
+++ b/static/bidder-info/invibes.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "system_operations@invibes.com"
+gvlVendorID: 436
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/ix.yaml b/static/bidder-info/ix.yaml
index 2f00ceb9..1e89c72e 100644
--- a/static/bidder-info/ix.yaml
+++ b/static/bidder-info/ix.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "pdu-supply-prebid@indexexchange.com"
+gvlVendorID: 10
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/lifestreet.yaml b/static/bidder-info/lifestreet.yaml
index 139d5599..34dc4eca 100644
--- a/static/bidder-info/lifestreet.yaml
+++ b/static/bidder-info/lifestreet.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "mobile.tech@lifestreet.com"
+gvlVendorID: 67
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/mediafuse.yaml b/static/bidder-info/mediafuse.yaml
index 112f67fe..98611402 100644
--- a/static/bidder-info/mediafuse.yaml
+++ b/static/bidder-info/mediafuse.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "support@mediafuse.com"
+gvlVendorID: 411
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/mgid.yaml b/static/bidder-info/mgid.yaml
index f8ba6db6..bddb8b85 100644
--- a/static/bidder-info/mgid.yaml
+++ b/static/bidder-info/mgid.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@mgid.com"
+gvlVendorID: 358
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/nanointeractive.yaml b/static/bidder-info/nanointeractive.yaml
index 244e7602..d199e9e8 100644
--- a/static/bidder-info/nanointeractive.yaml
+++ b/static/bidder-info/nanointeractive.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "development@nanointeractive.com"
+gvlVendorID: 72
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/nobid.yaml b/static/bidder-info/nobid.yaml
index 51a55de4..89f2a28a 100644
--- a/static/bidder-info/nobid.yaml
+++ b/static/bidder-info/nobid.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "developers@nobid.io"
+gvlVendorID: 816
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/onetag.yaml b/static/bidder-info/onetag.yaml
index fedaabf9..697ef04d 100644
--- a/static/bidder-info/onetag.yaml
+++ b/static/bidder-info/onetag.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: devops@onetag.com
+gvlVendorID: 241
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/openx.yaml b/static/bidder-info/openx.yaml
index 96ef8616..709f3db0 100644
--- a/static/bidder-info/openx.yaml
+++ b/static/bidder-info/openx.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@openx.com"
+gvlVendorID: 69
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/pubmatic.yaml b/static/bidder-info/pubmatic.yaml
index 4009d439..45c0418a 100644
--- a/static/bidder-info/pubmatic.yaml
+++ b/static/bidder-info/pubmatic.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "header-bidding@pubmatic.com"
+gvlVendorID: 76
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/pulsepoint.yaml b/static/bidder-info/pulsepoint.yaml
index 056a0bf3..bda03efd 100644
--- a/static/bidder-info/pulsepoint.yaml
+++ b/static/bidder-info/pulsepoint.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "ExchangeTeam@pulsepoint.com"
+gvlVendorID: 81
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/rhythmone.yaml b/static/bidder-info/rhythmone.yaml
index 89da6cfe..852344db 100644
--- a/static/bidder-info/rhythmone.yaml
+++ b/static/bidder-info/rhythmone.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "support@rhythmone.com"
+gvlVendorID: 36
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/rtbhouse.yaml b/static/bidder-info/rtbhouse.yaml
index f15af6ca..12744fdc 100644
--- a/static/bidder-info/rtbhouse.yaml
+++ b/static/bidder-info/rtbhouse.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@rtbhouse.com"
+gvlVendorID: 16
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/rubicon.yaml b/static/bidder-info/rubicon.yaml
index 40848e3e..0f19ddb9 100644
--- a/static/bidder-info/rubicon.yaml
+++ b/static/bidder-info/rubicon.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "header-bidding@rubiconproject.com"
+gvlVendorID: 52
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/sharethrough.yaml b/static/bidder-info/sharethrough.yaml
index 09530be5..45dceb94 100644
--- a/static/bidder-info/sharethrough.yaml
+++ b/static/bidder-info/sharethrough.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "pubgrowth.engineering@sharethrough.com"
+gvlVendorID: 80
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/smartadserver.yaml b/static/bidder-info/smartadserver.yaml
index 626b7dac..f22c7149 100644
--- a/static/bidder-info/smartadserver.yaml
+++ b/static/bidder-info/smartadserver.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "support@smartadserver.com"
+gvlVendorID: 45
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/somoaudience.yaml b/static/bidder-info/somoaudience.yaml
index 17156c18..83b64e5c 100644
--- a/static/bidder-info/somoaudience.yaml
+++ b/static/bidder-info/somoaudience.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "publishers@somoaudience.com"
+gvlVendorID: 341
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/sonobi.yaml b/static/bidder-info/sonobi.yaml
index 6d39319a..22a0a158 100644
--- a/static/bidder-info/sonobi.yaml
+++ b/static/bidder-info/sonobi.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "apex.prebid@sonobi.com"
+gvlVendorID: 104
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/sovrn.yaml b/static/bidder-info/sovrn.yaml
index c62d61df..4c6251bd 100644
--- a/static/bidder-info/sovrn.yaml
+++ b/static/bidder-info/sovrn.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "sovrnoss@sovrn.com"
+gvlVendorID: 13
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/tappx.yaml b/static/bidder-info/tappx.yaml
index 527959b7..eb655aa6 100644
--- a/static/bidder-info/tappx.yaml
+++ b/static/bidder-info/tappx.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "tappx@tappx.com"
+gvlVendorID: 628
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/telaria.yaml b/static/bidder-info/telaria.yaml
index 43e8707a..736fb972 100644
--- a/static/bidder-info/telaria.yaml
+++ b/static/bidder-info/telaria.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "github@telaria.com"
+gvlVendorID: 202
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/triplelift.yaml b/static/bidder-info/triplelift.yaml
index 2b9ff8d5..fe0ad8b2 100644
--- a/static/bidder-info/triplelift.yaml
+++ b/static/bidder-info/triplelift.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@triplelift.com"
+gvlVendorID: 28
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/triplelift_native.yaml b/static/bidder-info/triplelift_native.yaml
index abe91659..40d9be8f 100644
--- a/static/bidder-info/triplelift_native.yaml
+++ b/static/bidder-info/triplelift_native.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@triplelift.com"
+gvlVendorID: 28
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/trustx.yaml b/static/bidder-info/trustx.yaml
index 9ce52c11..6cfdd9fa 100644
--- a/static/bidder-info/trustx.yaml
+++ b/static/bidder-info/trustx.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "grid-tech@themediagrid.com"
+gvlVendorID: 686
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/ucfunnel.yaml b/static/bidder-info/ucfunnel.yaml
index 288b0b3f..e6be68a0 100644
--- a/static/bidder-info/ucfunnel.yaml
+++ b/static/bidder-info/ucfunnel.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "support@ucfunnel.com"
+gvlVendorID: 607
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/unruly.yaml b/static/bidder-info/unruly.yaml
index 01ed3774..e31ea600 100644
--- a/static/bidder-info/unruly.yaml
+++ b/static/bidder-info/unruly.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "adspaces@unrulygroup.com"
+gvlVendorID: 162
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/verizonmedia.yaml b/static/bidder-info/verizonmedia.yaml
index c00f2158..0b4642fc 100644
--- a/static/bidder-info/verizonmedia.yaml
+++ b/static/bidder-info/verizonmedia.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "dsp-supply-prebid@verizonmedia.com"
+gvlVendorID: 25
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/visx.yaml b/static/bidder-info/visx.yaml
index b6a16e4c..789ce478 100644
--- a/static/bidder-info/visx.yaml
+++ b/static/bidder-info/visx.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "supply.partners@yoc.com"
+gvlVendorID: 154
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/yieldlab.yaml b/static/bidder-info/yieldlab.yaml
index 654e6c74..3030d8a1 100644
--- a/static/bidder-info/yieldlab.yaml
+++ b/static/bidder-info/yieldlab.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "solutions@yieldlab.de"
+gvlVendorID: 70
 capabilities:
   site:
     mediaTypes:
diff --git a/static/bidder-info/yieldmo.yaml b/static/bidder-info/yieldmo.yaml
index 64cda519..b1385acb 100644
--- a/static/bidder-info/yieldmo.yaml
+++ b/static/bidder-info/yieldmo.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "prebid@yieldmo.com"
+gvlVendorID: 173
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-params/adyoulike.json b/static/bidder-params/adyoulike.json
index f426a092..448de344 100644
--- a/static/bidder-params/adyoulike.json
+++ b/static/bidder-params/adyoulike.json
@@ -26,8 +26,8 @@
     },
     "debug": {
       "type": "string",
-      "description": "Abitrary id used for debug purpose"
+      "description": "Arbitrary id used for debug purpose"
     }
   },
   "required": ["placement"]
-}
\ No newline at end of file
+}
diff --git a/usersync/usersync.go b/usersync/usersync.go
index 236acbe7..dffc1832 100644
--- a/usersync/usersync.go
+++ b/usersync/usersync.go
@@ -14,16 +14,6 @@ type Usersyncer interface {
 	// TODO #362: when the appnexus usersyncer is consistent, delete this and use the key
 	// of NewSyncerMap() here instead.
 	FamilyName() string
-
-	// GDPRVendorID returns the ID in the IAB Global Vendor List which refers to this Bidder.
-	//
-	// The Global Vendor list can be found here: https://vendor-list.consensu.org/vendorlist.json
-	// Bidders can register for the list here: https://register.consensu.org/
-	//
-	// If you're not on the list, this should return 0. If cookie sync requests have GDPR consent info,
-	// or the Prebid Server host company configures its deploy to be "cautious" when no GDPR info exists
-	// in the request, it will _not_ sync user IDs with you.
-	GDPRVendorID() uint16
 }
 
 type UsersyncInfo struct {


Test patch:
diff --git a/adapters/33across/usersync_test.go b/adapters/33across/usersync_test.go
index a9eb4e57..e99b5965 100644
--- a/adapters/33across/usersync_test.go
+++ b/adapters/33across/usersync_test.go
@@ -30,6 +30,5 @@ func Test33AcrossSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://ic.tynt.com/r/d?m=xch&rt=html&gdpr=A&gdpr_consent=B&us_privacy=C&ru=%2Fsetuid%3Fbidder%3D33across%26uid%3D33XUSERID33X&id=zzz000000000002zzz", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 58, syncer.GDPRVendorID())
 	assert.False(t, syncInfo.SupportCORS)
 }
diff --git a/adapters/acuityads/usersync_test.go b/adapters/acuityads/usersync_test.go
index 1f57cea3..b3ad10bd 100644
--- a/adapters/acuityads/usersync_test.go
+++ b/adapters/acuityads/usersync_test.go
@@ -29,6 +29,5 @@ func TestAcuityAdsSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://cs.admanmedia.com/sync/prebid?gdpr=0&gdpr_consent=ANDFJDS&us_privacy=1-YY&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dacuityads%26uid%3D%5BUID%5D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 231, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/adapterstest/test_json.go b/adapters/adapterstest/test_json.go
index a25a4f19..1e3178ed 100644
--- a/adapters/adapterstest/test_json.go
+++ b/adapters/adapterstest/test_json.go
@@ -123,7 +123,7 @@ func runSpec(t *testing.T, filename string, spec *testSpec, bidder adapters.Bidd
 	diffErrorLists(t, fmt.Sprintf("%s: MakeBids", filename), bidsErrs, spec.MakeBidsErrors)
 
 	for i := 0; i < len(spec.BidResponses); i++ {
-		diffBidLists(t, filename, bidResponses[i].Bids, spec.BidResponses[i].Bids)
+		diffBidLists(t, filename, bidResponses[i], spec.BidResponses[i].Bids)
 	}
 }
 
@@ -227,9 +227,24 @@ func diffErrorLists(t *testing.T, description string, actual []error, expected [
 	}
 }
 
-func diffBidLists(t *testing.T, filename string, actual []*adapters.TypedBid, expected []expectedBid) {
+func diffBidLists(t *testing.T, filename string, response *adapters.BidderResponse, expected []expectedBid) {
 	t.Helper()
 
+	if (response == nil || len(response.Bids) == 0) != (len(expected) == 0) {
+		if len(expected) == 0 {
+			t.Fatalf("%s: expectedBidResponses indicated a nil response, but mockResponses supplied a non-nil response", filename)
+		}
+
+		t.Fatalf("%s: mockResponses included unexpected nil or empty response", filename)
+	}
+
+	// Expected nil response - give diffBids something to work with.
+	if response == nil {
+		response = new(adapters.BidderResponse)
+	}
+
+	actual := response.Bids
+
 	if len(actual) != len(expected) {
 		t.Fatalf("%s: MakeBids returned wrong bid count. Expected %d, got %d", filename, len(expected), len(actual))
 	}
diff --git a/adapters/adform/usersync_test.go b/adapters/adform/usersync_test.go
index 855506da..f133be86 100644
--- a/adapters/adform/usersync_test.go
+++ b/adapters/adform/usersync_test.go
@@ -27,6 +27,5 @@ func TestAdformSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//cm.adform.net?return_url=localhost%2Fsetuid%3Fbidder%3Dadform%26gdpr%3D1%26gdpr_consent%3DBONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 50, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/adkernel/usersync_test.go b/adapters/adkernel/usersync_test.go
index aeacf00b..2ff81668 100644
--- a/adapters/adkernel/usersync_test.go
+++ b/adapters/adkernel/usersync_test.go
@@ -30,6 +30,5 @@ func TestAdkernelAdnSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://sync.adkernel.com/user-sync?t=image&gdpr=1&gdpr_consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw&us_privacy=1NYN&r=https%3A%2F%2Flocalhost%3A8888%2Fsetuid%3Fbidder%3Dadkernel%26gdpr%3D1%26gdpr_consent%3DBONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw%26uid%3D%7BUID%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, adkernelGDPRVendorID, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/adkernelAdn/usersync_test.go b/adapters/adkernelAdn/usersync_test.go
index 92d688e6..17cf97ce 100644
--- a/adapters/adkernelAdn/usersync_test.go
+++ b/adapters/adkernelAdn/usersync_test.go
@@ -30,6 +30,5 @@ func TestAdkernelAdnSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://tag.adkernel.com/syncr?gdpr=1&gdpr_consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw&us_privacy=1NYN&r=https%3A%2F%2Flocalhost%3A8888%2Fsetuid%3Fbidder%3DadkernelAdn%26gdpr%3D1%26gdpr_consent%3DBONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw%26uid%3D%7BUID%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, adkernelGDPRVendorID, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/adman/usersync_test.go b/adapters/adman/usersync_test.go
index 25da77db..d0dc90c8 100644
--- a/adapters/adman/usersync_test.go
+++ b/adapters/adman/usersync_test.go
@@ -30,6 +30,5 @@ func TestAdmanSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://sync.admanmedia.com/pbs.gif?gdpr=0&gdpr_consent=ANDFJDS&us_privacy=1-YY&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dadman%26uid%3D%5BUID%5D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 149, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/admixer/usersync_test.go b/adapters/admixer/usersync_test.go
index 79f023a2..6acc4845 100644
--- a/adapters/admixer/usersync_test.go
+++ b/adapters/admixer/usersync_test.go
@@ -30,6 +30,6 @@ func TestAdmixerSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "http://anyHost/anyPath", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 511, syncer.GDPRVendorID())
+
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/adocean/usersync_test.go b/adapters/adocean/usersync_test.go
index 9ca81b98..5257017a 100644
--- a/adapters/adocean/usersync_test.go
+++ b/adapters/adocean/usersync_test.go
@@ -30,5 +30,4 @@ func TestAdOceanSyncer(t *testing.T) {
 		syncInfo.URL,
 	)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 328, syncer.GDPRVendorID())
 }
diff --git a/adapters/adpone/usersync_test.go b/adapters/adpone/usersync_test.go
index 87b4e9ae..e744de5e 100644
--- a/adapters/adpone/usersync_test.go
+++ b/adapters/adpone/usersync_test.go
@@ -20,5 +20,4 @@ func TestAdponeSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://usersync.adpone.com/csync?t=p&ep=0&redir=localhost%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D%26gdpr_consent%3D%26uid%3D%7Buid%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, adponeGDPRVendorID, syncer.GDPRVendorID())
 }
diff --git a/adapters/adtarget/usersync_test.go b/adapters/adtarget/usersync_test.go
index ddba9e7a..ea6146ce 100644
--- a/adapters/adtarget/usersync_test.go
+++ b/adapters/adtarget/usersync_test.go
@@ -33,6 +33,5 @@ func TestAdtargetSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//sync.console.adtarget.com.tr/csync?t=p&ep=0&gdpr=0&gdpr_consent=123&us_privacy=1-YY&redir=localhost%2Fsetuid%3Fbidder%3Dadtarget%26gdpr%3D0%26gdpr_consent%3D123%26uid%3D%7Buid%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/adtelligent/usersync_test.go b/adapters/adtelligent/usersync_test.go
index fa157d22..2430f377 100644
--- a/adapters/adtelligent/usersync_test.go
+++ b/adapters/adtelligent/usersync_test.go
@@ -25,6 +25,5 @@ func TestAdtelligentSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//sync.adtelligent.com/csync?t=p&ep=0&redir=localhost%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%7Buid%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 410, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/advangelists/usersync_test.go b/adapters/advangelists/usersync_test.go
index 2167d49f..04ee7968 100644
--- a/adapters/advangelists/usersync_test.go
+++ b/adapters/advangelists/usersync_test.go
@@ -26,6 +26,5 @@ func TestAdvangelistsSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://nep.advangelists.com/xp/user-sync?acctid={aid}&&redirect=localhost/setuid?bidder=advangelists&gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&uid=$UID", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/adyoulike/usersync_test.go b/adapters/adyoulike/usersync_test.go
index 5090acb1..72def4cf 100644
--- a/adapters/adyoulike/usersync_test.go
+++ b/adapters/adyoulike/usersync_test.go
@@ -31,6 +31,5 @@ func TestAdyoulikeSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//visitor.omnitagjs.com/visitor/bsync?uid=19340f4f097d16f41f34fc0274981ca4&name=PrebidServer&gdpr_consent_string=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw&gdpr=1&us_privacy=1-YY", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 259, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/aja/usersync_test.go b/adapters/aja/usersync_test.go
index 4b6c90ef..dabd5e19 100644
--- a/adapters/aja/usersync_test.go
+++ b/adapters/aja/usersync_test.go
@@ -31,6 +31,5 @@ func TestAJASyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://ad.as.amanad.adtdp.com/v1/sync/ssp?ssp=4&gdpr=1&us_privacy=C&redir=localhost/setuid?bidder=aja&gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&uid=%s", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/amx/usersync_test.go b/adapters/amx/usersync_test.go
index 20a47c33..b6b6e6ba 100644
--- a/adapters/amx/usersync_test.go
+++ b/adapters/amx/usersync_test.go
@@ -18,6 +18,5 @@ func TestAMXSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "http://pbs.amxrtb.com/cchain/0?gdpr=&gdpr_consent=&cb=localhost%2Fsetuid%3Fbidder%3Damx%26uid%3D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 737, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/appnexus/usersync_test.go b/adapters/appnexus/usersync_test.go
index 24b9eede..d01e5704 100644
--- a/adapters/appnexus/usersync_test.go
+++ b/adapters/appnexus/usersync_test.go
@@ -20,6 +20,5 @@ func TestAppNexusSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//ib.adnxs.com/getuid?https%3A%2F%2Fprebid.adnxs.com%2Fpbs%2Fv1%2Fsetuid%3Fbidder%3Dadnxs%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 32, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/audienceNetwork/usersync_test.go b/adapters/audienceNetwork/usersync_test.go
index c3836e4f..591ea74f 100644
--- a/adapters/audienceNetwork/usersync_test.go
+++ b/adapters/audienceNetwork/usersync_test.go
@@ -20,6 +20,5 @@ func TestFacebookSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://www.facebook.com/audiencenetwork/idsync/?partner=partnerId&callback=localhost%2Fsetuid%3Fbidder%3DaudienceNetwork%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/avocet/usersync_test.go b/adapters/avocet/usersync_test.go
index 3df39b77..bd4cd414 100644
--- a/adapters/avocet/usersync_test.go
+++ b/adapters/avocet/usersync_test.go
@@ -30,6 +30,5 @@ func TestAvocetSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://ads.avct.cloud/getuid?&gdpr=1&gdpr_consent=ConsentString&us_privacy=PrivacyString&url=%2Fsetuid%3Fbidder%3Davocet%26gdpr%3D1%26gdpr_consent%3DConsentString%26uid%3D%7B%7BUUID%7D%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 63, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/beachfront/beachfronttest/exemplary/adm-video.json b/adapters/beachfront/beachfronttest/exemplary/adm-video.json
index 26b050f2..edde1301 100644
--- a/adapters/beachfront/beachfronttest/exemplary/adm-video.json
+++ b/adapters/beachfront/beachfronttest/exemplary/adm-video.json
@@ -96,6 +96,7 @@
 
   "expectedBidResponses": [
     {
+      "currency": "USD",
       "bids": [
         {
           "bid": {
diff --git a/adapters/beachfront/beachfronttest/exemplary/banner.json b/adapters/beachfront/beachfronttest/exemplary/banner.json
index e3dcffa3..1a8e8305 100644
--- a/adapters/beachfront/beachfronttest/exemplary/banner.json
+++ b/adapters/beachfront/beachfronttest/exemplary/banner.json
@@ -45,6 +45,7 @@
           ],
           "domain": "some.domain.us",
           "page": "https://some.domain.us/some/page.html",
+          "real204": true,
           "referrer": "",
           "search": "",
           "secure": 1,
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-204-with-body.json b/adapters/beachfront/beachfronttest/supplemental/banner-204-with-body.json
new file mode 100644
index 00000000..edba490e
--- /dev/null
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-204-with-body.json
@@ -0,0 +1,79 @@
+{
+  "mockBidRequest": {
+    "id": "some_test_ad",
+    "site": {
+      "page": "https://some.domain.us/some/page.html"
+    },
+    "imp": [
+      {
+        "id": "dudImp",
+        "bidfloor": 0.02,
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "bidfloor": 0.02,
+            "appId": "dudAppId1"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/prebid_display",
+        "body": {
+          "slots": [
+            {
+              "slot": "dudImp",
+              "id": "dudAppId1",
+              "bidfloor": 0.02,
+              "sizes": [
+                {
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "domain": "some.domain.us",
+          "page": "https://some.domain.us/some/page.html",
+          "real204": true,
+          "referrer": "",
+          "search": "",
+          "secure": 1,
+          "requestId": "some_test_ad",
+          "isMobile": 0,
+          "ip": "",
+          "deviceModel": "",
+          "deviceOs": "",
+          "dnt": 0,
+          "ua": "",
+          "adapterName": "BF_PREBID_S2S",
+          "adapterVersion": "0.9.2",
+          "user": {
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 204,
+        "body": [
+          {
+            "something": "where nothing should be"
+          }
+        ]
+      }
+    }
+  ],
+
+  "expectedBidResponses": []
+}
+
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-empty_array-200.json b/adapters/beachfront/beachfronttest/supplemental/banner-204.json
similarity index 81%
rename from adapters/beachfront/beachfronttest/supplemental/banner-empty_array-200.json
rename to adapters/beachfront/beachfronttest/supplemental/banner-204.json
index 8e607f67..69aa0b88 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-empty_array-200.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-204.json
@@ -46,6 +46,7 @@
           ],
           "domain": "some.domain.us",
           "page": "https://some.domain.us/some/page.html",
+          "real204": true,
           "referrer": "",
           "search": "",
           "secure": 1,
@@ -63,18 +64,13 @@
         }
       },
       "mockResponse": {
-        "status": 200,
-        "body": []
+        "status": 204,
+        "body": ""
       }
     }
   ],
 
-  "expectedBidResponses": [],
+  "expectedBidResponses": [
 
-  "expectedMakeBidsErrors": [
-    {
-      "value": "no content or truncated content received from server. status code 200 from https://qa.beachrtb.com/prebid_display. Run with request.debug = 1 for more info",
-      "comparison": "literal"
-    }
   ]
 }
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-by-explicit.json b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-by-explicit.json
index c326a33a..b060efe6 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-by-explicit.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-by-explicit.json
@@ -60,6 +60,7 @@
           ],
           "domain": "some.domain.us",
           "page": "https://some.domain.us/some/page.html",
+          "real204": true,
           "referrer": "",
           "search": "",
           "secure": 1,
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-expected-204-response-on-banner.json b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-expected-204-response-on-banner.json
new file mode 100644
index 00000000..240994bb
--- /dev/null
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video-expected-204-response-on-banner.json
@@ -0,0 +1,171 @@
+{
+  "mockBidRequest": {
+    "id": "banner-and-video",
+    "imp": [
+      {
+        "id": "mix1",
+        "ext": {
+          "bidder": {
+            "bidfloor": 0.41,
+            "appIds": {
+              "banner": "bannerAppId1",
+              "video": "videoAppId1"
+            }
+          }
+        },
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "context": "instream",
+          "w": 300,
+          "h": 250
+        },
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            }
+          ]
+        }
+      }
+    ],
+    "site": {
+      "page": "https://some.domain.us/some/page.html"
+    },
+    "device":{
+      "ip":"255.255.255.255"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/prebid_display",
+        "body": {
+          "slots": [
+            {
+              "slot": "mix1",
+              "id": "bannerAppId1",
+              "bidfloor": 0.41,
+              "sizes": [
+                {
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ],
+          "domain": "some.domain.us",
+          "page": "https://some.domain.us/some/page.html",
+          "real204": true,
+          "referrer": "",
+          "search": "",
+          "secure": 1,
+          "deviceOs": "",
+          "deviceModel": "",
+          "isMobile": 0,
+          "ua": "",
+          "ip": "255.255.255.255",
+          "dnt": 0,
+          "user": {},
+          "adapterName": "BF_PREBID_S2S",
+          "adapterVersion": "0.9.2",
+          "requestId": "banner-and-video"
+        }
+      },
+      "mockResponse": {
+        "status": 204,
+        "body": ""
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "https://qa.beachrtb.com/bid.json?exchange_id=videoAppId1",
+        "body": {
+          "id": "banner-and-video",
+          "imp": [
+            {
+              "video": {
+                "w": 300,
+                "h": 250,
+                "mimes": [
+                  "video/mp4"
+                ]
+              },
+              "bidfloor": 0.41,
+              "secure": 1,
+              "id": "mix1"
+            }
+          ],
+          "site": {
+            "domain": "some.domain.us",
+            "page": "https://some.domain.us/some/page.html"
+          },
+          "device": {
+            "devicetype": 2,
+            "ip": "255.255.255.255"
+          },
+          "cur": [
+            "USD"
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "61b87329-8790-47b7-90dd-c53ae7ce1723",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "5d839458f73decdc1572b7f6",
+                  "impid": "mix1",
+                  "price": 20,
+                  "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+                  "adid": "1088",
+                  "adomain": [
+                    "beachfront.io"
+                  ],
+                  "cid": "277",
+                  "crid": "532",
+                  "w": 300,
+                  "h": 250
+                }
+              ],
+              "seat": "bfb-io-s1"
+            }
+          ],
+          "bidid": "5d839458f73decdc1572b7f6",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {"bids":[]},
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "mix1AdmVideo",
+            "impid": "mix1",
+            "price": 20,
+            "adm": "<VAST version=\"2.0\"><Ad><Wrapper>http://example.com/vast.xml</Wrapper></Ad></VAST>",
+            "adid": "1088",
+            "adomain": [
+              "beachfront.io"
+            ],
+            "cid": "277",
+            "crid": "532",
+            "w": 300,
+            "h": 250
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video.json b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video.json
index f67903d0..18e8ca7a 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-and-adm-video.json
@@ -59,6 +59,7 @@
           ],
           "domain": "some.domain.us",
           "page": "https://some.domain.us/some/page.html",
+          "real204": true,
           "referrer": "",
           "search": "",
           "secure": 1,
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-and-nurl-video.json b/adapters/beachfront/beachfronttest/supplemental/banner-and-nurl-video.json
index b950d734..2ef8ca58 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-and-nurl-video.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-and-nurl-video.json
@@ -57,6 +57,7 @@
           ],
           "domain": "some.domain.us",
           "page": "https://some.domain.us/some/page.html",
+          "real204": true,
           "referrer": "",
           "search": "",
           "secure": 1,
diff --git a/adapters/beachfront/beachfronttest/supplemental/banner-bad-request-400.json b/adapters/beachfront/beachfronttest/supplemental/banner-bad-request-400.json
index 1771f393..35926b3c 100644
--- a/adapters/beachfront/beachfronttest/supplemental/banner-bad-request-400.json
+++ b/adapters/beachfront/beachfronttest/supplemental/banner-bad-request-400.json
@@ -45,6 +45,7 @@
           ],
           "domain": "some.domain.us",
           "page": "https://some.domain.us/some/page.html",
+          "real204": true,
           "referrer": "",
           "search": "",
           "secure": 1,
diff --git a/adapters/beachfront/beachfronttest/supplemental/bidder_response_unmarshal_error_banner.json b/adapters/beachfront/beachfronttest/supplemental/bidder_response_unmarshal_error_banner.json
index b751f763..e7869d36 100644
--- a/adapters/beachfront/beachfronttest/supplemental/bidder_response_unmarshal_error_banner.json
+++ b/adapters/beachfront/beachfronttest/supplemental/bidder_response_unmarshal_error_banner.json
@@ -44,6 +44,7 @@
           ],
           "domain": "some.domain.us",
           "page": "https://some.domain.us/some/page.html",
+          "real204": true,
           "referrer": "",
           "search": "",
           "secure": 1,
diff --git a/adapters/beachfront/beachfronttest/supplemental/bidfloor-below-min.json b/adapters/beachfront/beachfronttest/supplemental/bidfloor-below-min.json
index f5d4e822..7ebe6cf4 100644
--- a/adapters/beachfront/beachfronttest/supplemental/bidfloor-below-min.json
+++ b/adapters/beachfront/beachfronttest/supplemental/bidfloor-below-min.json
@@ -44,6 +44,7 @@
           ],
           "domain": "some.domain.us",
           "page": "https://some.domain.us/some/page.html",
+          "real204": true,
           "referrer": "",
           "search": "",
           "secure": 1,
diff --git a/adapters/beachfront/beachfronttest/supplemental/six-nine-combo.json b/adapters/beachfront/beachfronttest/supplemental/six-nine-combo.json
index 4c009ff0..9a8ca734 100644
--- a/adapters/beachfront/beachfronttest/supplemental/six-nine-combo.json
+++ b/adapters/beachfront/beachfronttest/supplemental/six-nine-combo.json
@@ -189,6 +189,7 @@
           ],
           "domain": "example.com",
           "page": "http://example.com/whatever/something.html",
+          "real204": true,
           "referrer": "",
           "search": "",
           "secure": 0,
diff --git a/adapters/beachfront/beachfronttest/supplemental/two-four-combo.json b/adapters/beachfront/beachfronttest/supplemental/two-four-combo.json
index d48962e6..ac1ca19f 100644
--- a/adapters/beachfront/beachfronttest/supplemental/two-four-combo.json
+++ b/adapters/beachfront/beachfronttest/supplemental/two-four-combo.json
@@ -97,6 +97,7 @@
           ],
           "domain":"example.com",
           "page":"http://example.com/whatever/something.html",
+          "real204": true,
           "referrer":"",
           "search":"",
           "secure":0,
diff --git a/adapters/beachfront/usersync_test.go b/adapters/beachfront/usersync_test.go
index db4d825e..edb52bd4 100644
--- a/adapters/beachfront/usersync_test.go
+++ b/adapters/beachfront/usersync_test.go
@@ -30,6 +30,5 @@ func TestBeachfrontSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://sync.bfmio.com/sync_s2s?gdpr=A&us_privacy=C&url=https%3A%2F%2Flocalhost%3A8888%2Fsetuid%3Fbidder%3Dbeachfront%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D%5Bio_cid%5D", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, uint16(335), syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/beintoo/usersync_test.go b/adapters/beintoo/usersync_test.go
index 880d6a84..65d92e6d 100644
--- a/adapters/beintoo/usersync_test.go
+++ b/adapters/beintoo/usersync_test.go
@@ -30,6 +30,5 @@ func TestBeintooSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://ib.beintoo.com/um?ssp=pbs&gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&us_privacy=1NYN&redirect=localhost%2Fsetuid%3Fbidder%3Dbeintoo%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 618, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/between/usersync_test.go b/adapters/between/usersync_test.go
index 6470a9c4..c54f473b 100644
--- a/adapters/between/usersync_test.go
+++ b/adapters/between/usersync_test.go
@@ -1,10 +1,11 @@
 package between
 
 import (
-	"github.com/prebid/prebid-server/privacy"
-	"github.com/stretchr/testify/assert"
 	"testing"
 	"text/template"
+
+	"github.com/prebid/prebid-server/privacy"
+	"github.com/stretchr/testify/assert"
 )
 
 func TestNewBetweenSyncerSyncer(t *testing.T) {
@@ -19,6 +20,5 @@ func TestNewBetweenSyncerSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://ads.betweendigital.com/match?bidder_id=pbs&gdpr=&gdpr_consent=&us_privacy=&callback_url=localhost:8080%2Fsetuid%3Fbidder%3Dbetween%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%24%7BUSER_ID%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 724, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/brightroll/usersync_test.go b/adapters/brightroll/usersync_test.go
index a5d47e35..fac17b0a 100644
--- a/adapters/brightroll/usersync_test.go
+++ b/adapters/brightroll/usersync_test.go
@@ -20,6 +20,5 @@ func TestBrightrollSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "http://test-bh.ybp.yahoo.com/sync/appnexuspbs?gdpr=&euconsent=&us_privacy=&url=localhost%2Fsetuid%3Fbidder%3Dbrightroll%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24%7BUID%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 25, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/colossus/usersync_test.go b/adapters/colossus/usersync_test.go
index 52eb6389..4cb3a4bb 100644
--- a/adapters/colossus/usersync_test.go
+++ b/adapters/colossus/usersync_test.go
@@ -30,6 +30,5 @@ func TestColossusSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://sync.colossusssp.com/pbs.gif?gdpr=0&gdpr_consent=A&us_privacy=1-YY&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dcolossus%26uid%3D%5BUID%5D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/connectad/usersync_test.go b/adapters/connectad/usersync_test.go
index c4b4962b..81b0dc47 100644
--- a/adapters/connectad/usersync_test.go
+++ b/adapters/connectad/usersync_test.go
@@ -30,6 +30,5 @@ func TestConnectAdSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://cdn.connectad.io/connectmyusers.php?gdpr=1&consent=fakeconsent&us_privacy=fake&cb=localhost%2Fsetuid%3Fbidder%3Dconnectad%26gdpr%3D1%26gdpr_consent%3Dfakeconsent%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 138, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/consumable/usersync_test.go b/adapters/consumable/usersync_test.go
index ef71c0b1..0af868a0 100644
--- a/adapters/consumable/usersync_test.go
+++ b/adapters/consumable/usersync_test.go
@@ -30,6 +30,5 @@ func TestConsumableSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//e.serverbid.com/udb/9969/match?gdpr=A&euconsent=B&us_privacy=C&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dconsumable%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D", u.URL)
 	assert.Equal(t, "redirect", u.Type)
-	assert.Equal(t, uint16(591), syncer.GDPRVendorID())
 	assert.Equal(t, false, u.SupportCORS)
 }
diff --git a/adapters/conversant/usersync_test.go b/adapters/conversant/usersync_test.go
index 16affbd1..62ab732c 100644
--- a/adapters/conversant/usersync_test.go
+++ b/adapters/conversant/usersync_test.go
@@ -25,6 +25,5 @@ func TestConversantSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "usersync?rurl=localhost%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D0%26gdpr_consent%3D%26uid%3D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 24, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/cpmstar/usersync_test.go b/adapters/cpmstar/usersync_test.go
index dae55e63..9bae7062 100644
--- a/adapters/cpmstar/usersync_test.go
+++ b/adapters/cpmstar/usersync_test.go
@@ -20,6 +20,5 @@ func TestCpmstarSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://server.cpmstar.com/usersync.aspx?gdpr=&gdpr_consent=&us_privacy=&redirectUri=http%3A%2F%2Flocalhost:8000%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D%26gdpr_consent%3D%26us_privacy%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.False(t, syncInfo.SupportCORS)
 }
diff --git a/adapters/datablocks/usersync_test.go b/adapters/datablocks/usersync_test.go
index a7518e9b..d6ee5804 100644
--- a/adapters/datablocks/usersync_test.go
+++ b/adapters/datablocks/usersync_test.go
@@ -30,6 +30,5 @@ func TestDatablocksSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://sync.v5prebid.datablocks.net/s2ssync?gdpr=1&gdpr_consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw&us_privacy=1NYN&r=https%3A%2F%2Flocalhost%3A8888%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D1%26gdpr_consent%3DBONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw%26uid%3D%24%7Buid%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, datablocksGDPRVendorID, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/deepintent/usersync_test.go b/adapters/deepintent/usersync_test.go
index 06a221ca..6709a838 100644
--- a/adapters/deepintent/usersync_test.go
+++ b/adapters/deepintent/usersync_test.go
@@ -30,6 +30,5 @@ func TestDeepintentSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://match.deepintent.com/usersync/136?id=unk&gdpr=A&us_privacy=C&url=https%3A%2F%2Flocalhost%3A8888%2Fsetuid%3Fbidder%3Ddeepintent%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D%5Bio_cid%5D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, uint16(541), syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/dmx/usersync_test.go b/adapters/dmx/usersync_test.go
index e4e3c7d8..a316af04 100644
--- a/adapters/dmx/usersync_test.go
+++ b/adapters/dmx/usersync_test.go
@@ -1,10 +1,11 @@
 package dmx
 
 import (
-	"github.com/prebid/prebid-server/privacy"
 	"testing"
 	"text/template"
 
+	"github.com/prebid/prebid-server/privacy"
+
 	"github.com/stretchr/testify/assert"
 )
 
@@ -15,6 +16,5 @@ func TestDmxSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://dmx.districtm.io/s/v1/img/s/10007", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 144, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/emx_digital/usersync_test.go b/adapters/emx_digital/usersync_test.go
index 59d66d87..7aaf1335 100644
--- a/adapters/emx_digital/usersync_test.go
+++ b/adapters/emx_digital/usersync_test.go
@@ -30,6 +30,5 @@ func TestEMXDigitalSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://cs.emxdgt.com/um?ssp=pbs&gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&us_privacy=1NYN&redirect=localhost%2Fsetuid%3Fbidder%3Demx_digital%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 183, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/engagebdr/usersync_test.go b/adapters/engagebdr/usersync_test.go
index 3a6c179a..5cee3abe 100644
--- a/adapters/engagebdr/usersync_test.go
+++ b/adapters/engagebdr/usersync_test.go
@@ -30,6 +30,5 @@ func TestEngageBDRSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://match.bnmla.com/usersync/s2s?gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&us_privacy=1NYN&r=localhost%2Fsetuid%3Fbidder%3Dengagebdr%26gdpr%3D1%26gdpr_consent%3DBOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA%26uid%3D", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 62, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/eplanning/usersync_test.go b/adapters/eplanning/usersync_test.go
index 85770689..922ab4e2 100644
--- a/adapters/eplanning/usersync_test.go
+++ b/adapters/eplanning/usersync_test.go
@@ -20,6 +20,5 @@ func TestEPlanningSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://ads.us.e-planning.net/uspd/1/?du=https%3A%2F%2Fads.us.e-planning.net%2Fgetuid%2F1%2F5a1ad71d2d53a0f5%3Flocalhost%2Fsetuid%3Fbidder%3Deplanning%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 90, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/gamma/usersync_test.go b/adapters/gamma/usersync_test.go
index 4278f9ab..7636e05a 100644
--- a/adapters/gamma/usersync_test.go
+++ b/adapters/gamma/usersync_test.go
@@ -25,6 +25,5 @@ func TestGammaSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//hb.gammaplatform.com/sync?gdpr=0&gdpr_consent=&redirectUri=http%3A%2F%2Flocalhost%2F%2Fsetuid%3Fbidder%3Dgamma%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.False(t, syncInfo.SupportCORS)
 }
diff --git a/adapters/gamoshi/usersync_test.go b/adapters/gamoshi/usersync_test.go
index 43dc88a4..0465365f 100644
--- a/adapters/gamoshi/usersync_test.go
+++ b/adapters/gamoshi/usersync_test.go
@@ -25,6 +25,5 @@ func TestGamoshiSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://rtb.gamoshi.io/user_sync_prebid?gdpr=&consent=&us_privacy=anyValue&rurl=https%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Dgamoshi%26gdpr%3D%7B%7B.GDPR%7D%7D%26gdpr_consent%3D%7B%7B.GDPRConsent%7D%7D%26uid%3D%5Bgusr%5D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 644, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/grid/usersync_test.go b/adapters/grid/usersync_test.go
index 99730b5d..2d7f3fc6 100644
--- a/adapters/grid/usersync_test.go
+++ b/adapters/grid/usersync_test.go
@@ -25,6 +25,5 @@ func TestGridSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//not_localhost/synclocalhost%2Fsetuid%3Fbidder%3Dgrid%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 686, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/gumgum/usersync_test.go b/adapters/gumgum/usersync_test.go
index 9c6dc420..9b7c7aa4 100644
--- a/adapters/gumgum/usersync_test.go
+++ b/adapters/gumgum/usersync_test.go
@@ -30,6 +30,5 @@ func TestGumGumSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://rtb.gumgum.com/usync/prbds2s?gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&us_privacy=1NYN&r=localhost%2Fsetuid%3Fbidder%3Dgumgum%26gdpr%3D1%26gdpr_consent%3DBOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA%26uid%3D", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 61, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/improvedigital/improvedigitaltest/exemplary/native.json b/adapters/improvedigital/improvedigitaltest/exemplary/native.json
new file mode 100644
index 00000000..3309b35a
--- /dev/null
+++ b/adapters/improvedigital/improvedigitaltest/exemplary/native.json
@@ -0,0 +1,161 @@
+{
+  "mockBidRequest": {
+    "id": "26f14780-8ef8-4f41-b70c-c4d062237df6",
+    "source": {
+      "tid": "26f14780-8ef8-4f41-b70c-c4d062237df6",
+      "ext": {
+        "schain": {
+          "ver": "1.0",
+          "complete": 1,
+          "nodes": [
+            { "asi": "example.com", "hp": 1, "sid": "1234abc" }
+          ]
+        }
+      }
+    },
+    "tmax": 1000,
+    "imp": [
+      {
+        "id": "native",
+        "ext": { "improvedigital": { "placementId": 1234 } },
+        "native": {
+          "request": "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1]}],\"assets\":[{\"required\":1,\"title\":{\"len\":80}},{\"required\":1,\"data\":{\"type\":2}},{\"required\":0,\"data\":{\"type\":12}},{\"required\":0,\"img\":{\"type\":3,\"wmin\":300,\"hmin\":225,\"ext\":{\"aspectratios\":[\"4:3\"]}}},{\"required\":0,\"img\":{\"type\":1,\"w\":128,\"h\":128}},{\"required\":0,\"data\":{\"type\":3}},{\"required\":0,\"data\":{\"type\":6}}]}",
+          "ver": "1.2"
+        }
+      }
+    ],
+    "ext": {
+      "prebid": {
+        "targeting": {
+          "includewinners": true,
+          "includebidderkeys": false
+        }
+      }
+    },
+    "site": {
+      "publisher": { "id": "pubid" },
+      "page": "example.com"
+    },
+    "device": { "w": 1581, "h": 922, "ip": "1.1.1.1" },
+    "regs": { "ext": { "gdpr": 0 } },
+    "user": {
+      "ext": {
+        "consent": "XYZ"
+      }
+    }
+  },
+  "httpcalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://localhost/pbs",
+        "body": {
+          "id": "26f14780-8ef8-4f41-b70c-c4d062237df6",
+          "imp": [
+            {
+              "id": "native",
+              "native": {
+                "request": "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1]}],\"assets\":[{\"required\":1,\"title\":{\"len\":80}},{\"required\":1,\"data\":{\"type\":2}},{\"required\":0,\"data\":{\"type\":12}},{\"required\":0,\"img\":{\"type\":3,\"wmin\":300,\"hmin\":225,\"ext\":{\"aspectratios\":[\"4:3\"]}}},{\"required\":0,\"img\":{\"type\":1,\"w\":128,\"h\":128}},{\"required\":0,\"data\":{\"type\":3}},{\"required\":0,\"data\":{\"type\":6}}]}",
+                "ver": "1.2"
+              },
+              "ext": { "improvedigital": { "placementId": 1234 } }
+            }
+          ],
+          "site": {
+            "page": "example.com",
+            "publisher": { "id": "pubid" }
+          },
+          "device": {
+            "ip": "1.1.1.1",
+            "h": 922,
+            "w": 1581
+          },
+          "user": {
+            "ext": {
+              "consent": "XYZ"
+            }
+          },
+          "tmax": 1000,
+          "source": {
+            "tid": "26f14780-8ef8-4f41-b70c-c4d062237df6",
+            "ext": {
+              "schain": {
+                "ver": "1.0",
+                "complete": 1,
+                "nodes": [
+                  {
+                    "asi": "example.com",
+                    "hp": 1,
+                    "sid": "1234abc"
+                  }
+                ]
+              }
+            }
+          },
+          "regs": { "ext": { "gdpr": 0 } },
+          "ext": {
+            "prebid": {
+              "targeting": {
+                "includewinners": true,
+                "includebidderkeys": false
+              }
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "26f14780-8ef8-4f41-b70c-c4d062237df6",
+          "cur": "USD",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "ext": {
+                    "improvedigital": {
+                      "brand_name": "AdvertiserABC",
+                      "bidder_id": 301
+                    }
+                  },
+                  "crid": "14065",
+                  "id": "d4f04449-ba04-4d7c-bb34-dc0fc5240f59",
+                  "price": 0.01,
+                  "adm": "{\"assets\":[{\"title\":{\"text\":\"Luxury Mars Cruises\"},\"id\":1},{\"id\":2,\"data\":{\"type\":2,\"value\":\"Visit the planet in a luxury spaceship.\"}},{\"id\":3,\"data\":{\"type\":12,\"value\":\"Book today\"}},{\"id\":4,\"img\":{\"h\":250,\"type\":3,\"url\":\"http://hb.improvedigital.com/creatives/display/300x250.jpg\",\"w\":300}},{\"id\":6,\"data\":{\"type\":3,\"value\":\"3.8\"}},{\"id\":7,\"data\":{\"type\":6,\"value\":\"FREE\"}}],\"link\":{\"clicktrackers\":[\"http://localhost.localdomain/click/\"],\"url\":\"http://www.iponweb.com/careers/\"}}",
+                  "adomain": ["example.com"],
+                  "impid": "native",
+                  "cid": "25076"
+                }
+              ],
+              "seat": "improvedigital"
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "d4f04449-ba04-4d7c-bb34-dc0fc5240f59",
+            "impid": "native",
+            "price": 0.01,
+            "adm": "{\"assets\":[{\"title\":{\"text\":\"Luxury Mars Cruises\"},\"id\":1},{\"id\":2,\"data\":{\"type\":2,\"value\":\"Visit the planet in a luxury spaceship.\"}},{\"id\":3,\"data\":{\"type\":12,\"value\":\"Book today\"}},{\"id\":4,\"img\":{\"h\":250,\"type\":3,\"url\":\"http://hb.improvedigital.com/creatives/display/300x250.jpg\",\"w\":300}},{\"id\":6,\"data\":{\"type\":3,\"value\":\"3.8\"}},{\"id\":7,\"data\":{\"type\":6,\"value\":\"FREE\"}}],\"link\":{\"clicktrackers\":[\"http://localhost.localdomain/click/\"],\"url\":\"http://www.iponweb.com/careers/\"}}",
+            "adomain": ["example.com"],
+            "cid": "25076",
+            "crid": "14065",
+            "ext": {
+              "improvedigital": {
+                "brand_name": "AdvertiserABC",
+                "bidder_id": 301
+              }
+            }
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/improvedigital/improvedigitaltest/params/race/native.json b/adapters/improvedigital/improvedigitaltest/params/race/native.json
new file mode 100644
index 00000000..0de1d580
--- /dev/null
+++ b/adapters/improvedigital/improvedigitaltest/params/race/native.json
@@ -0,0 +1,12 @@
+{
+    "placementId": 123,
+    "publisherId": 321,
+    "placementKey": "uniq_name",
+    "size": {
+        "w": 100,
+        "h": 100
+    },
+    "keyValues": {
+        "testKey1": ["testValueA", "testValueB"]
+    }
+}
diff --git a/adapters/improvedigital/improvedigitaltest/supplemental/native.json b/adapters/improvedigital/improvedigitaltest/supplemental/native.json
deleted file mode 100644
index 3090700a..00000000
--- a/adapters/improvedigital/improvedigitaltest/supplemental/native.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
-    "mockBidRequest": {
-        "id": "test-request-id",
-        "imp": [{
-            "id": "test-imp-id",
-            "native": {
-                "ver": "1.1"
-            },
-            "ext": {
-                "bidder": {
-                    "placementId": 13245
-                }
-            }
-        }]
-    },
-    "httpCalls": [{
-        "expectedRequest": {
-            "uri": "http://localhost/pbs",
-            "body": {
-                "id": "test-request-id",
-                "imp": [{
-                    "id": "test-imp-id",
-					"native": {
-						"request": "",
-		                "ver": "1.1"
-		            },
-                    "ext": {
-                        "bidder": {
-                            "placementId": 13245
-                        }
-                    }
-                }]
-            }
-        },
-		"mockResponse": {
-            "status": 200,
-            "body": {
-                "id": "test-request-id",
-                "seatbid": [{
-                    "seat": "improvedigital",
-                    "bid": [{
-                        "id": "randomid",
-                        "impid": "test-imp-id",
-                        "price": 0.500000,
-                        "adid": "12345678",
-                        "adm": "some-test-native",
-                        "cid": "987",
-                        "crid": "12345678",
-                        "h": 250,
-                        "w": 300
-                    }]
-                }],
-                "cur": "USD"
-            }
-        }
-    }],
-
-    "expectedMakeBidsErrors": [
-        {
-            "value":  "Unknown impression type for ID: \"test-imp-id\"",
-            "comparison": "literal"
-        }
-    ]
-}
diff --git a/adapters/improvedigital/usersync_test.go b/adapters/improvedigital/usersync_test.go
index 35ea89cf..9892e0cc 100644
--- a/adapters/improvedigital/usersync_test.go
+++ b/adapters/improvedigital/usersync_test.go
@@ -30,6 +30,5 @@ func TestImprovedigitalSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://ad.360yield.com/server_match?gdpr=A&gdpr_consent=B&us_privacy=C&r=%2Fsetuid%3Fbidder%3Dimprovedigital%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D%7BPUB_USER_ID%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 253, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/invibes/usersync_test.go b/adapters/invibes/usersync_test.go
index 3f715f6b..49288622 100644
--- a/adapters/invibes/usersync_test.go
+++ b/adapters/invibes/usersync_test.go
@@ -26,7 +26,6 @@ func TestInvibesSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "http://localhost:56479/home/getLid?gdpr=1&gdpr_consent=abc&us_privacy=&redirectUri=test.com%2Fsetuid%3Fbidder%3Dinvibes%26gdpr%3D1%26gdpr_consent%3Dabc%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 436, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 
 }
diff --git a/adapters/ix/usersync_test.go b/adapters/ix/usersync_test.go
index 10f91da1..3288b1ae 100644
--- a/adapters/ix/usersync_test.go
+++ b/adapters/ix/usersync_test.go
@@ -20,6 +20,5 @@ func TestIxSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//ssum-sec.casalemedia.com/usermatchredir?s=184932&cb=localhost%2Fsetuid%3Fbidder%3Dix%26gdpr%3D%26gdpr_consent%3D%26uid%3D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 10, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/jixie/jixie_test.go b/adapters/jixie/jixie_test.go
index 968fb4fd..289dd241 100644
--- a/adapters/jixie/jixie_test.go
+++ b/adapters/jixie/jixie_test.go
@@ -1,10 +1,11 @@
 package jixie
 
 import (
+	"testing"
+
 	"github.com/prebid/prebid-server/adapters/adapterstest"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
-	"testing"
 )
 
 func TestJsonSamples(t *testing.T) {
diff --git a/adapters/jixie/usersync_test.go b/adapters/jixie/usersync_test.go
index 5be5e514..57548243 100644
--- a/adapters/jixie/usersync_test.go
+++ b/adapters/jixie/usersync_test.go
@@ -1,10 +1,10 @@
 package jixie
 
 import (
-	"github.com/prebid/prebid-server/privacy"
 	"testing"
 	"text/template"
 
+	"github.com/prebid/prebid-server/privacy"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -20,6 +20,5 @@ func TestJixieSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://id.jixie.io/api/sync?pid=&gdpr=&gdpr_consent=&us_privacy=&redirect=https%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Djixie%26gdpr%3D%26gdpr_consent%3D%26uid%3D%25%25JXUID%25%25", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/krushmedia/usersync_test.go b/adapters/krushmedia/usersync_test.go
index b58f2f1b..765b0faa 100644
--- a/adapters/krushmedia/usersync_test.go
+++ b/adapters/krushmedia/usersync_test.go
@@ -29,6 +29,5 @@ func TestKrushmediaSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://cs.krushmedia.com/4e4abdd5ecc661643458a730b1aa927d.gif?gdpr=0&gdpr_consent=allGdpr&us_privacy=1-YY&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dkrushmedia%26uid%3D%5BUID%5D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/lifestreet/usersync_test.go b/adapters/lifestreet/usersync_test.go
index 8fee0935..e41217fe 100644
--- a/adapters/lifestreet/usersync_test.go
+++ b/adapters/lifestreet/usersync_test.go
@@ -25,6 +25,5 @@ func TestLifestreetSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//ads.lfstmedia.com/idsync/137062?synced=1&ttl=1s&rurl=localhost%2Fsetuid%3Fbidder%3Dlifestreet%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%24%24visitor_cookie%24%24", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 67, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/lockerdome/usersync_test.go b/adapters/lockerdome/usersync_test.go
index b5dea226..3a2bd7f3 100644
--- a/adapters/lockerdome/usersync_test.go
+++ b/adapters/lockerdome/usersync_test.go
@@ -20,6 +20,5 @@ func TestLockerDomeSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://lockerdome.com/usync/prebidserver?pid=&gdpr=&gdpr_consent=&us_privacy=&redirect=https%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Dlockerdome%26gdpr%3D%26gdpr_consent%3D%26uid%3D%7B%7Buid%7D%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/logicad/usersync_test.go b/adapters/logicad/usersync_test.go
index 89d6207d..e8b10c66 100644
--- a/adapters/logicad/usersync_test.go
+++ b/adapters/logicad/usersync_test.go
@@ -26,6 +26,5 @@ func TestLogicadSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://localhost/cookiesender?r=true&gdpr=1&gdpr_consent=A&ru=localhost%2Fsetuid%3Fbidder%3Dlogicad%26gdpr%3D1%26gdpr_consent%3DA%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/lunamedia/usersync_test.go b/adapters/lunamedia/usersync_test.go
index c9fe2032..24cd740d 100644
--- a/adapters/lunamedia/usersync_test.go
+++ b/adapters/lunamedia/usersync_test.go
@@ -26,6 +26,5 @@ func TestLunaMediaSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://api.lunamedia.io/xp/user-sync?acctid={aid}&&redirect=localhost/setuid?bidder=lunamedia&gdpr=1&gdpr_consent=A&uid=$UID", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/marsmedia/usersync_test.go b/adapters/marsmedia/usersync_test.go
index f019c014..975af65f 100644
--- a/adapters/marsmedia/usersync_test.go
+++ b/adapters/marsmedia/usersync_test.go
@@ -30,7 +30,6 @@ func TestMarsmediaSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://dmp.rtbsrv.com/dmp/profiles/cm?p_id=179&gdpr=A&gdpr_consent=B&us_privacy=C&redirect=localhost:8000%2Fsetuid%3Fbidder%3Dmarsmedia%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D%24%7BUUID%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 
 }
diff --git a/adapters/mediafuse/usersync_test.go b/adapters/mediafuse/usersync_test.go
index 30b5b535..e3dfa068 100644
--- a/adapters/mediafuse/usersync_test.go
+++ b/adapters/mediafuse/usersync_test.go
@@ -25,6 +25,5 @@ func TestMediafuseSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//sync.hbmp.mediafuse.com/csync?t=p&ep=0&redir=localhost%2Fsetuid%3Fbidder%3Dmediafuse%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%7Buid%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 411, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/mgid/usersync_test.go b/adapters/mgid/usersync_test.go
index 2ce634ee..3fa5a151 100644
--- a/adapters/mgid/usersync_test.go
+++ b/adapters/mgid/usersync_test.go
@@ -25,6 +25,5 @@ func TestMgidSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://cm.mgid.com/m?cdsp=363893&adu=https%3A//external.com%2Fsetuid%3Fbidder%3Dmgid%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%7Bmuidn%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 358, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/nanointeractive/usersync_test.go b/adapters/nanointeractive/usersync_test.go
index fa786649..4d816ab7 100644
--- a/adapters/nanointeractive/usersync_test.go
+++ b/adapters/nanointeractive/usersync_test.go
@@ -31,6 +31,5 @@ func TestNewNanoInteractiveSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://ad.audiencemanager.de/hbs/cookie_sync?gdpr=1&consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw&us_privacy=1NYN&redirectUri=http%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Dnanointeractive%26gdpr%3D1%26gdpr_consent%3DBONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 72, userSync.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/ninthdecimal/usersync_test.go b/adapters/ninthdecimal/usersync_test.go
index ee121434..e722a2b6 100755
--- a/adapters/ninthdecimal/usersync_test.go
+++ b/adapters/ninthdecimal/usersync_test.go
@@ -26,6 +26,5 @@ func TestNinthDecimalSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://rtb.ninthdecimal.com/xp/user-sync?acctid={aid}&&redirect=localhost/setuid?bidder=ninthdecimal&gdpr=1&gdpr_consent=A&uid=$UID", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/onetag/usersync_test.go b/adapters/onetag/usersync_test.go
index 31892e6d..21f4837d 100644
--- a/adapters/onetag/usersync_test.go
+++ b/adapters/onetag/usersync_test.go
@@ -20,5 +20,4 @@ func TestOneTagSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://onetag-sys.com/usync/?redir=", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 241, syncer.GDPRVendorID())
 }
diff --git a/adapters/openx/usersync_test.go b/adapters/openx/usersync_test.go
index cdbb7da8..14ec38be 100644
--- a/adapters/openx/usersync_test.go
+++ b/adapters/openx/usersync_test.go
@@ -20,6 +20,5 @@ func TestOpenxSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://rtb.openx.net/sync/prebid?gdpr=&gdpr_consent=&r=localhost%2Fsetuid%3Fbidder%3Dopenx%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24%7BUID%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 69, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/pubmatic/usersync_test.go b/adapters/pubmatic/usersync_test.go
index d6cd9f78..0f4d2e85 100644
--- a/adapters/pubmatic/usersync_test.go
+++ b/adapters/pubmatic/usersync_test.go
@@ -30,6 +30,5 @@ func TestPubmaticSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//ads.pubmatic.com/AdServer/js/user_sync.html?gdpr=A&gdpr_consent=B&us_privacy=C&predirect=localhost%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 76, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/pulsepoint/usersync_test.go b/adapters/pulsepoint/usersync_test.go
index e1680fd2..7cfea57c 100644
--- a/adapters/pulsepoint/usersync_test.go
+++ b/adapters/pulsepoint/usersync_test.go
@@ -20,6 +20,5 @@ func TestPulsepointSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//bh.contextweb.com/rtset?pid=561205&ev=1&rurl=http%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Dpulsepoint%26gdpr%3D%26gdpr_consent%3D%26uid%3D%25%25VGUID%25%25", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 81, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/rhythmone/usersync_test.go b/adapters/rhythmone/usersync_test.go
index 85ecba2a..97920fb4 100644
--- a/adapters/rhythmone/usersync_test.go
+++ b/adapters/rhythmone/usersync_test.go
@@ -30,6 +30,5 @@ func TestRhythmoneSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://sync.1rx.io/usersync2/rmphb?gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&us_privacy=1NYN&redir=localhost%2Fsetuid%3Fbidder%3Drhythmone%26gdpr%3D1%26gdpr_consent%3DBOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA%26uid%3D%5BRX_UUID%5D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 36, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/rtbhouse/usersync_test.go b/adapters/rtbhouse/usersync_test.go
index bb139f7a..a7cb2590 100644
--- a/adapters/rtbhouse/usersync_test.go
+++ b/adapters/rtbhouse/usersync_test.go
@@ -25,6 +25,5 @@ func TestRTBHouseSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://creativecdn.com/cm-notify?pi=prebidsrvtst&gdpr=0&gdpr_consent=", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, rtbHouseGDPRVendorID, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/rubicon/rubicontest/exemplary/simple-video.json b/adapters/rubicon/rubicontest/exemplary/simple-video.json
index 76c64ff9..b85c28de 100644
--- a/adapters/rubicon/rubicontest/exemplary/simple-video.json
+++ b/adapters/rubicon/rubicontest/exemplary/simple-video.json
@@ -9,6 +9,52 @@
       "id": "1",
       "bundle": "com.wls.testwlsapplication"
     },
+    "user": {
+      "data": [
+        {
+          "ext": {
+            "taxonomyname": "iab"
+          },
+          "segment": [
+            {
+              "id": "idToCopy"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "taxonomyname": "someValue"
+          },
+          "segment": [
+            {
+              "id": "shouldNotBeCopied"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "taxonomyname": "IaB"
+          },
+          "segment": [
+            {
+              "id": "idToCopy2"
+            }
+          ]
+        },
+        {
+          "ext": {
+            "taxonomyname": [
+              "wrong iab type"
+            ]
+          },
+          "segment": [
+            {
+              "id": "shouldNotBeCopied2"
+            }
+          ]
+        }
+      ]
+    },
     "imp": [
       {
         "id": "test-imp-id",
@@ -30,8 +76,8 @@
             "video": {
             },
             "accountId": 1001,
-            "siteId":113932,
-            "zoneId":535510
+            "siteId": 113932,
+            "zoneId": 535510
           }
         }
       }
@@ -52,6 +98,63 @@
             "ip": "123.123.123.123",
             "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx"
           },
+          "user": {
+            "data": [
+              {
+                "ext": {
+                  "taxonomyname": "iab"
+                },
+                "segment": [
+                  {
+                    "id": "idToCopy"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "taxonomyname": "someValue"
+                },
+                "segment": [
+                  {
+                    "id": "shouldNotBeCopied"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "taxonomyname": "IaB"
+                },
+                "segment": [
+                  {
+                    "id": "idToCopy2"
+                  }
+                ]
+              },
+              {
+                "ext": {
+                  "taxonomyname": [
+                    "wrong iab type"
+                  ]
+                },
+                "segment": [
+                  {
+                    "id": "shouldNotBeCopied2"
+                  }
+                ]
+              }
+            ],
+            "ext": {
+              "digitrust": null,
+              "rp": {
+                "target": {
+                  "iab": [
+                    "idToCopy",
+                    "idToCopy2"
+                  ]
+                }
+              }
+            }
+          },
           "app": {
             "id": "1",
             "ext": {
@@ -91,7 +194,7 @@
               },
               "ext": {
                 "rp": {
-                  "track":{
+                  "track": {
                     "mint": "",
                     "mint_version": ""
                   },
diff --git a/adapters/rubicon/usersync_test.go b/adapters/rubicon/usersync_test.go
index 646ebff2..eca40562 100644
--- a/adapters/rubicon/usersync_test.go
+++ b/adapters/rubicon/usersync_test.go
@@ -25,7 +25,6 @@ func TestRubiconSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://pixel.rubiconproject.com/exchange/sync.php?p=prebid&gdpr=0&gdpr_consent=", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 52, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 	assert.Equal(t, "rubicon", syncer.FamilyName())
 }
diff --git a/adapters/sharethrough/usersync_test.go b/adapters/sharethrough/usersync_test.go
index 48023295..00b3c427 100644
--- a/adapters/sharethrough/usersync_test.go
+++ b/adapters/sharethrough/usersync_test.go
@@ -25,7 +25,6 @@ func TestSharethroughSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://match.sharethrough.com?gdpr=0&gdpr_consent=", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 80, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 	assert.Equal(t, "sharethrough", syncer.FamilyName())
 }
diff --git a/adapters/smartadserver/usersync_test.go b/adapters/smartadserver/usersync_test.go
index c4e66606..319ce5b5 100644
--- a/adapters/smartadserver/usersync_test.go
+++ b/adapters/smartadserver/usersync_test.go
@@ -30,6 +30,5 @@ func TestSmartadserverSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//ssbsync.smartadserver.com/getuid?gdpr=1&gdpr_consent=COyASAoOyASAoAfAAAENAfCAAAAAAAAAAAAAAAAAAAAA&us_privacy=1YNN&url=localhost%2Fsetuid%3Fbidder%3Dsmartadserver%26gdpr%3D1%26gdpr_consent%3DCOyASAoOyASAoAfAAAENAfCAAAAAAAAAAAAAAAAAAAAA%26uid%3D%5Bsas_uid%5D%22", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 45, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/smartrtb/usersync_test.go b/adapters/smartrtb/usersync_test.go
index ae3ae5dc..68a8452a 100644
--- a/adapters/smartrtb/usersync_test.go
+++ b/adapters/smartrtb/usersync_test.go
@@ -15,6 +15,5 @@ func TestSmartRTBSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "http://market-east.smrtb.com/sync/all?nid=smartrtb&gdpr=&gdpr_consent=&url=localhost%2Fsetuid%3Fbidder%smartrtb%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24%7BUID%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/smartyads/usersync_test.go b/adapters/smartyads/usersync_test.go
index ed7fc07b..4f94591c 100644
--- a/adapters/smartyads/usersync_test.go
+++ b/adapters/smartyads/usersync_test.go
@@ -29,6 +29,5 @@ func TestSmartyAdsSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://as.ck-ie.com/prebid.gif?gdpr=0&gdpr_consent=ANDFJDS&us_privacy=1-YY&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dsmartyads%26uid%3D%5BUID%5D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/somoaudience/usersync_test.go b/adapters/somoaudience/usersync_test.go
index 86460506..2367a567 100644
--- a/adapters/somoaudience/usersync_test.go
+++ b/adapters/somoaudience/usersync_test.go
@@ -20,6 +20,5 @@ func TestSomoaudienceSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//publisher-east.mobileadtrading.com/usersync?ru=localhost%2Fsetuid%3Fbidder%3Dsomoaudience%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24%7BUID%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 341, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/sonobi/usersync_test.go b/adapters/sonobi/usersync_test.go
index bfe5859e..995c3757 100644
--- a/adapters/sonobi/usersync_test.go
+++ b/adapters/sonobi/usersync_test.go
@@ -25,6 +25,5 @@ func TestSonobiSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//sync.go.sonobi.com/us.gif?loc=external.com%2Fsetuid%3Fbidder%3Dsonobi%26consent_string%3D0%26gdpr%3D%26uid%3D%5BUID%5D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 104, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/sovrn/usersync_test.go b/adapters/sovrn/usersync_test.go
index 4a4dfd9d..6c35ecdb 100644
--- a/adapters/sovrn/usersync_test.go
+++ b/adapters/sovrn/usersync_test.go
@@ -25,6 +25,5 @@ func TestSovrnSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//ap.lijit.com/pixel?redir=external.com%2Fsetuid%3Fbidder%3Dsovrn%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 13, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/synacormedia/usersync_test.go b/adapters/synacormedia/usersync_test.go
index 538efb30..ce45353c 100644
--- a/adapters/synacormedia/usersync_test.go
+++ b/adapters/synacormedia/usersync_test.go
@@ -20,6 +20,5 @@ func TestSynacormediaSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//sync.technoratimedia.com/services?srv=cs&pid=70&cb=localhost%2Fsetuid%3Fbidder%3Dsynacormedia%26uid%3D%5BUSER_ID%5D", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.False(t, syncInfo.SupportCORS)
 }
diff --git a/adapters/tappx/usersync_test.go b/adapters/tappx/usersync_test.go
index 65919b9f..992a35de 100644
--- a/adapters/tappx/usersync_test.go
+++ b/adapters/tappx/usersync_test.go
@@ -30,6 +30,5 @@ func TestTappxSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//testing.ssp.tappx.com/cs/usersync.php?gdpr_optin=1&gdpr_consent=A&us_privacy=1YNN&type=iframe&ruid=localhost%2Fsetuid%3Fbidder%3Dtappx%26gdpr%3D1%26gdpr_consent%3DA%26uid%3D%7B%7BTPPXUID%7D%7D", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 628, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/telaria/usersync_test.go b/adapters/telaria/usersync_test.go
index 4896b253..f019ed5c 100644
--- a/adapters/telaria/usersync_test.go
+++ b/adapters/telaria/usersync_test.go
@@ -26,7 +26,6 @@ func TestTelariaSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://pbs.publishers.tremorhub.com/pubsync?gdpr=0&gdpr_consent=", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 202, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 	assert.Equal(t, "telaria", syncer.FamilyName())
 
diff --git a/adapters/triplelift/usersync_test.go b/adapters/triplelift/usersync_test.go
index 1731b22d..012e33a4 100644
--- a/adapters/triplelift/usersync_test.go
+++ b/adapters/triplelift/usersync_test.go
@@ -20,6 +20,5 @@ func TestTripleliftSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//eb2.3lift.com/getuid?gdpr=&cmp_cs=&us_privacy=&redir=https%3A%2F%2Feb2.3lift.com%2Fsetuid%3Fbidder%3Dtriplelift%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 28, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/triplelift_native/usersync_test.go b/adapters/triplelift_native/usersync_test.go
index df0757ed..7fd878b7 100644
--- a/adapters/triplelift_native/usersync_test.go
+++ b/adapters/triplelift_native/usersync_test.go
@@ -20,6 +20,5 @@ func TestTripleliftSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//eb2.3lift.com/getuid?gdpr=&cmp_cs=&us_privacy=&redir=https%3A%2F%2Feb2.3lift.com%2Fsetuid%3Fbidder%3Dtriplelift%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 28, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/trustx/usersync_test.go b/adapters/trustx/usersync_test.go
index ced0d215..ba371bc2 100644
--- a/adapters/trustx/usersync_test.go
+++ b/adapters/trustx/usersync_test.go
@@ -25,6 +25,5 @@ func TestTrustXSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//not_localhost/synclocalhost%2Fsetuid%3Fbidder%3Dgrid%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 686, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/ucfunnel/usersync_test.go b/adapters/ucfunnel/usersync_test.go
index 45320b8c..204de978 100644
--- a/adapters/ucfunnel/usersync_test.go
+++ b/adapters/ucfunnel/usersync_test.go
@@ -25,6 +25,5 @@ func TestUcfunnelSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//sync.aralego.com/idsync?gdpr=0&redirect=externalURL.com%2Fsetuid%3Fbidder%3Ducfunnel%26uid%3DSspCookieUserId", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 607, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/unruly/usersync_test.go b/adapters/unruly/usersync_test.go
index 2f0e07d8..f0702ceb 100644
--- a/adapters/unruly/usersync_test.go
+++ b/adapters/unruly/usersync_test.go
@@ -30,6 +30,5 @@ func TestUnrulySyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://usermatch.targeting.unrulymedia.com/pbsync?gdpr=A&consent=B&us_privacy=C&rurl=%2Fsetuid%3Fbidder%3Dunruly%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "iframe", syncInfo.Type)
-	assert.EqualValues(t, 162, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/valueimpression/usersync_test.go b/adapters/valueimpression/usersync_test.go
index ffb3f372..7b3a13c5 100644
--- a/adapters/valueimpression/usersync_test.go
+++ b/adapters/valueimpression/usersync_test.go
@@ -30,6 +30,5 @@ func TestValueImpressionSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://rtb.valueimpression.com/usersync?gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&redirectUri=http%3A%2F%2Flocalhost:8000%2Fsetuid%3Fbidder%3Dvalueimpression%26gdpr%3D1%26gdpr_consent%3DBOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.False(t, syncInfo.SupportCORS)
 }
diff --git a/adapters/verizonmedia/usersync_test.go b/adapters/verizonmedia/usersync_test.go
index ad77ef0e..9dd1ae70 100644
--- a/adapters/verizonmedia/usersync_test.go
+++ b/adapters/verizonmedia/usersync_test.go
@@ -19,5 +19,4 @@ func TestVerizonMediaSyncer(t *testing.T) {
 
 	assert.NoError(t, err)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 25, syncer.GDPRVendorID())
 }
diff --git a/adapters/visx/usersync_test.go b/adapters/visx/usersync_test.go
index b410cda6..ec4cf82b 100644
--- a/adapters/visx/usersync_test.go
+++ b/adapters/visx/usersync_test.go
@@ -30,6 +30,5 @@ func TestVisxSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://t.visx.net/s2s_sync?gdpr=A&gdpr_consent=B&us_privacy=C&redir=%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D%24%7BUUID%7D", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 154, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/vrtcal/usersync_test.go b/adapters/vrtcal/usersync_test.go
index 26e5838d..7f8ca220 100644
--- a/adapters/vrtcal/usersync_test.go
+++ b/adapters/vrtcal/usersync_test.go
@@ -25,7 +25,6 @@ func TestVrtcalSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "http://usync-prebid.vrtcal.com/s?gdpr=0&gdpr_consent=", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 	assert.Equal(t, "vrtcal", syncer.FamilyName())
 }
diff --git a/adapters/yieldlab/usersync_test.go b/adapters/yieldlab/usersync_test.go
index 3892c16b..eabd46f6 100644
--- a/adapters/yieldlab/usersync_test.go
+++ b/adapters/yieldlab/usersync_test.go
@@ -21,6 +21,5 @@ func TestYieldlabSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "https://ad.yieldlab.net/mr?t=2&pid=9140838&gdpr=0&gdpr_consent=&redirectUri=http%3A%2F%2Flocalhost%2F%2Fsetuid%3Fbidder%3Dyieldlab%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%25%25YL_UID%25%25", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 70, syncer.GDPRVendorID())
 	assert.False(t, syncInfo.SupportCORS)
 }
diff --git a/adapters/yieldmo/usersync_test.go b/adapters/yieldmo/usersync_test.go
index 598710ec..5d12c63e 100644
--- a/adapters/yieldmo/usersync_test.go
+++ b/adapters/yieldmo/usersync_test.go
@@ -25,6 +25,5 @@ func TestYieldmoSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//ads.yieldmo.com/pbsync?gdpr=0&gdpr_consent=&us_privacy=&redirectUri=http%3A%2F%2Flocalhost%2F%2Fsetuid%3Fbidder%3Dyieldmo%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 173, syncer.GDPRVendorID())
 	assert.False(t, syncInfo.SupportCORS)
 }
diff --git a/adapters/yieldone/usersync_test.go b/adapters/yieldone/usersync_test.go
index 902f3b66..c4d0fee9 100644
--- a/adapters/yieldone/usersync_test.go
+++ b/adapters/yieldone/usersync_test.go
@@ -25,6 +25,5 @@ func TestYieldoneSyncer(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, "//not_localhost/synclocalhost%2Fsetuid%3Fbidder%3Dyieldone%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
 	assert.Equal(t, "redirect", syncInfo.Type)
-	assert.EqualValues(t, 0, syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/config/bidderinfo_test.go b/config/bidderinfo_test.go
index 2d9d53ec..62a0b853 100644
--- a/config/bidderinfo_test.go
+++ b/config/bidderinfo_test.go
@@ -13,6 +13,7 @@ const testInfoFilesPath = "./test/bidder-info"
 const testYAML = `
 maintainer:
   email: "some-email@domain.com"
+gvlVendorID: 42
 capabilities:
   app:
     mediaTypes:
@@ -36,12 +37,13 @@ func TestLoadBidderInfoFromDisk(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	expected := map[string]BidderInfo{
+	expected := BidderInfos{
 		bidder: {
 			Enabled: true,
 			Maintainer: &MaintainerInfo{
 				Email: "some-email@domain.com",
 			},
+			GVLVendorID: 42,
 			Capabilities: &CapabilitiesInfo{
 				App: &PlatformInfo{
 					MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeNative},
@@ -63,7 +65,7 @@ func TestLoadBidderInfo(t *testing.T) {
 		givenConfigs  map[string]Adapter
 		givenContent  string
 		givenError    error
-		expectedInfo  map[string]BidderInfo
+		expectedInfo  BidderInfos
 		expectedError string
 	}{
 		{
@@ -76,6 +78,7 @@ func TestLoadBidderInfo(t *testing.T) {
 					Maintainer: &MaintainerInfo{
 						Email: "some-email@domain.com",
 					},
+					GVLVendorID: 42,
 					Capabilities: &CapabilitiesInfo{
 						App: &PlatformInfo{
 							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeNative},
@@ -97,6 +100,7 @@ func TestLoadBidderInfo(t *testing.T) {
 					Maintainer: &MaintainerInfo{
 						Email: "some-email@domain.com",
 					},
+					GVLVendorID: 42,
 					Capabilities: &CapabilitiesInfo{
 						App: &PlatformInfo{
 							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeNative},
@@ -118,6 +122,7 @@ func TestLoadBidderInfo(t *testing.T) {
 					Maintainer: &MaintainerInfo{
 						Email: "some-email@domain.com",
 					},
+					GVLVendorID: 42,
 					Capabilities: &CapabilitiesInfo{
 						App: &PlatformInfo{
 							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeNative},
@@ -139,6 +144,7 @@ func TestLoadBidderInfo(t *testing.T) {
 					Maintainer: &MaintainerInfo{
 						Email: "some-email@domain.com",
 					},
+					GVLVendorID: 42,
 					Capabilities: &CapabilitiesInfo{
 						App: &PlatformInfo{
 							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeNative},
@@ -186,3 +192,19 @@ type fakeInfoReader struct {
 func (r fakeInfoReader) Read(bidder string) ([]byte, error) {
 	return []byte(r.content), r.err
 }
+
+func TestToGVLVendorIDMap(t *testing.T) {
+	givenBidderInfos := BidderInfos{
+		"bidderA": BidderInfo{Enabled: true, GVLVendorID: 0},
+		"bidderB": BidderInfo{Enabled: true, GVLVendorID: 100},
+		"bidderC": BidderInfo{Enabled: false, GVLVendorID: 0},
+		"bidderD": BidderInfo{Enabled: false, GVLVendorID: 200},
+	}
+
+	expectedGVLVendorIDMap := map[openrtb_ext.BidderName]uint16{
+		"bidderB": 100,
+	}
+
+	result := givenBidderInfos.ToGVLVendorIDMap()
+	assert.Equal(t, expectedGVLVendorIDMap, result)
+}
diff --git a/config/test/bidder-info/someBidder.yaml b/config/test/bidder-info/someBidder.yaml
index b5216f0f..232b73d0 100644
--- a/config/test/bidder-info/someBidder.yaml
+++ b/config/test/bidder-info/someBidder.yaml
@@ -1,5 +1,6 @@
 maintainer:
   email: "some-email@domain.com"
+gvlVendorID: 42
 capabilities:
   app:
     mediaTypes:
diff --git a/docs/developers/automated-tests.md b/docs/developers/automated-tests.md
index 0dff9b04..6814fba3 100644
--- a/docs/developers/automated-tests.md
+++ b/docs/developers/automated-tests.md
@@ -1,6 +1,6 @@
 # Automated Tests
 
-This project uses [TravisCI](https://travis-ci.org/) to make sure that every PR passes automated tests.
+This project uses GitHub Actions to make sure that every PR passes automated tests.
 To reproduce these tests locally, use:
 
 ```
diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go
index 08461d40..77e76613 100644
--- a/endpoints/openrtb2/amp_auction_test.go
+++ b/endpoints/openrtb2/amp_auction_test.go
@@ -395,103 +395,119 @@ func TestCCPAConsent(t *testing.T) {
 	}
 }
 
-func TestNoConsent(t *testing.T) {
-	// Build Request
-	bid, err := getTestBidRequest(true, nil, true, nil)
-	if err != nil {
-		t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err)
+func TestConsentWarnings(t *testing.T) {
+	type inputTest struct {
+		regs              *openrtb_ext.ExtRegs
+		invalidConsentURL bool
+		expectedWarnings  map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage
 	}
+	invalidConsent := "invalid"
 
-	// Simulated Stored Request Backend
-	stored := map[string]json.RawMessage{"1": json.RawMessage(bid)}
+	bidderWarning := openrtb_ext.ExtBidderMessage{
+		Code:    10003,
+		Message: "debug turned off for bidder",
+	}
+	invalidCCPAWarning := openrtb_ext.ExtBidderMessage{
+		Code:    10001,
+		Message: "Consent '" + invalidConsent + "' is not recognized as either CCPA or GDPR TCF.",
+	}
+	invalidConsentWarning := openrtb_ext.ExtBidderMessage{
+		Code:    10001,
+		Message: "CCPA consent is invalid and will be ignored. (request.regs.ext.us_privacy must contain 4 characters)",
+	}
 
-	// Build Exchange Endpoint
-	mockExchange := &mockAmpExchange{}
-	endpoint, _ := NewAmpEndpoint(
-		mockExchange,
-		newParamsValidator(t),
-		&mockAmpStoredReqFetcher{stored},
-		empty_fetcher.EmptyFetcher{},
-		&config.Configuration{MaxRequestSize: maxSize},
-		newTestMetrics(),
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
-		map[string]string{},
-		[]byte{},
-		openrtb_ext.BuildBidderMap(),
-	)
+	testData := []inputTest{
+		{
+			regs:              nil,
+			invalidConsentURL: false,
+			expectedWarnings:  nil,
+		},
+		{
+			regs:              nil,
+			invalidConsentURL: true,
+			expectedWarnings:  map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage{openrtb_ext.BidderReservedGeneral: {invalidCCPAWarning}},
+		},
+		{
+			regs:              &openrtb_ext.ExtRegs{USPrivacy: "invalid"},
+			invalidConsentURL: true,
+			expectedWarnings: map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage{
+				openrtb_ext.BidderReservedGeneral:  {invalidCCPAWarning, invalidConsentWarning},
+				openrtb_ext.BidderName("appnexus"): {bidderWarning},
+			},
+		},
+		{
+			regs:              &openrtb_ext.ExtRegs{USPrivacy: "1NYN"},
+			invalidConsentURL: false,
+			expectedWarnings:  map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage{openrtb_ext.BidderName("appnexus"): {bidderWarning}},
+		},
+	}
 
-	// Invoke Endpoint
-	request := httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1", nil)
-	responseRecorder := httptest.NewRecorder()
-	endpoint(responseRecorder, request, nil)
+	for _, testCase := range testData {
 
-	// Parse Response
-	var response AmpResponse
-	if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil {
-		t.Fatalf("Error unmarshalling response: %s", err.Error())
-	}
+		bid, err := getTestBidRequest(true, nil, testCase.regs == nil, testCase.regs)
+		if err != nil {
+			t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err)
+		}
 
-	// Assert Result
-	result := mockExchange.lastRequest
-	assert.NotNil(t, result, "lastRequest")
-	assert.Nil(t, result.User, "lastRequest.User")
-	assert.Nil(t, result.Regs, "lastRequest.Regs")
-	assert.Equal(t, expectedErrorsFromHoldAuction, response.Errors)
-	assert.Empty(t, response.Warnings)
-}
+		// Simulated Stored Request Backend
+		stored := map[string]json.RawMessage{"1": json.RawMessage(bid)}
 
-func TestInvalidConsent(t *testing.T) {
-	// Build Request
-	bid, err := getTestBidRequest(true, nil, true, nil)
-	if err != nil {
-		t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err)
-	}
+		// Build Exchange Endpoint
+		var mockExchange exchange.Exchange
+		if testCase.regs != nil {
+			mockExchange = &mockAmpExchangeWarnings{}
+		} else {
+			mockExchange = &mockAmpExchange{}
+		}
+		endpoint, _ := NewAmpEndpoint(
+			mockExchange,
+			newParamsValidator(t),
+			&mockAmpStoredReqFetcher{stored},
+			empty_fetcher.EmptyFetcher{},
+			&config.Configuration{MaxRequestSize: maxSize},
+			newTestMetrics(),
+			analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+			map[string]string{},
+			[]byte{},
+			openrtb_ext.BuildBidderMap(),
+		)
 
-	// Simulated Stored Request Backend
-	stored := map[string]json.RawMessage{"1": json.RawMessage(bid)}
+		// Invoke Endpoint
+		var request *http.Request
 
-	// Build Exchange Endpoint
-	mockExchange := &mockAmpExchange{}
-	endpoint, _ := NewAmpEndpoint(
-		mockExchange,
-		newParamsValidator(t),
-		&mockAmpStoredReqFetcher{stored},
-		empty_fetcher.EmptyFetcher{},
-		&config.Configuration{MaxRequestSize: maxSize},
-		newTestMetrics(),
-		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
-		map[string]string{},
-		[]byte{},
-		openrtb_ext.BuildBidderMap(),
-	)
+		if testCase.invalidConsentURL {
+			request = httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1&consent_string="+invalidConsent, nil)
 
-	// Invoke Endpoint
-	invalidConsent := "invalid"
-	request := httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1&consent_string="+invalidConsent, nil)
-	responseRecorder := httptest.NewRecorder()
-	endpoint(responseRecorder, request, nil)
+		} else {
+			request = httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1", nil)
+		}
 
-	// Parse Response
-	var response AmpResponse
-	if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil {
-		t.Fatalf("Error unmarshalling response: %s", err.Error())
-	}
+		responseRecorder := httptest.NewRecorder()
+		endpoint(responseRecorder, request, nil)
 
-	// Assert Result
-	expectedWarnings := map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError{
-		openrtb_ext.BidderReservedGeneral: {
-			{
-				Code:    10001,
-				Message: "Consent '" + invalidConsent + "' is not recognized as either CCPA or GDPR TCF.",
-			},
-		},
+		// Parse Response
+		var response AmpResponse
+		if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil {
+			t.Fatalf("Error unmarshalling response: %s", err.Error())
+		}
+
+		// Assert Result
+		if testCase.regs == nil {
+			result := mockExchange.(*mockAmpExchange).lastRequest
+			assert.NotNil(t, result, "lastRequest")
+			assert.Nil(t, result.User, "lastRequest.User")
+			assert.Nil(t, result.Regs, "lastRequest.Regs")
+			assert.Equal(t, expectedErrorsFromHoldAuction, response.Errors)
+			if testCase.invalidConsentURL {
+				assert.Equal(t, testCase.expectedWarnings, response.Warnings)
+			} else {
+				assert.Empty(t, response.Warnings)
+			}
+
+		} else {
+			assert.Equal(t, testCase.expectedWarnings, response.Warnings)
+		}
 	}
-	result := mockExchange.lastRequest
-	assert.NotNil(t, result, "lastRequest")
-	assert.Nil(t, result.User, "lastRequest.User")
-	assert.Nil(t, result.Regs, "lastRequest.Regs")
-	assert.Equal(t, expectedErrorsFromHoldAuction, response.Errors)
-	assert.Equal(t, expectedWarnings, response.Warnings)
 }
 
 func TestNewAndLegacyConsentBothProvided(t *testing.T) {
@@ -929,7 +945,7 @@ type mockAmpExchange struct {
 	lastRequest *openrtb.BidRequest
 }
 
-var expectedErrorsFromHoldAuction map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError = map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError{
+var expectedErrorsFromHoldAuction map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage = map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage{
 	openrtb_ext.BidderName("openx"): {
 		{
 			Code:    1,
@@ -962,6 +978,21 @@ func (m *mockAmpExchange) HoldAuction(ctx context.Context, r exchange.AuctionReq
 	return response, nil
 }
 
+type mockAmpExchangeWarnings struct{}
+
+func (m *mockAmpExchangeWarnings) HoldAuction(ctx context.Context, r exchange.AuctionRequest, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) {
+	response := &openrtb.BidResponse{
+		SeatBid: []openrtb.SeatBid{{
+			Bid: []openrtb.Bid{{
+				AdM: "<script></script>",
+				Ext: json.RawMessage(`{ "prebid": {"targeting": { "hb_pb": "1.20", "hb_appnexus_pb": "1.20", "hb_cache_id": "some_id"}}}`),
+			}},
+		}},
+		Ext: json.RawMessage(`{ "warnings": {"appnexus": [{"code": 10003, "message": "debug turned off for bidder"}] }}`),
+	}
+	return response, nil
+}
+
 func getTestBidRequest(nilUser bool, userExt *openrtb_ext.ExtUser, nilRegs bool, regsExt *openrtb_ext.ExtRegs) ([]byte, error) {
 	var width uint64 = 300
 	var height uint64 = 300
@@ -1025,7 +1056,6 @@ func getTestBidRequest(nilUser bool, userExt *openrtb_ext.ExtUser, nilRegs bool,
 			Ext:   regsExtData,
 		}
 	}
-
 	return json.Marshal(bidRequest)
 }
 
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index 768e6059..19c58f05 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -48,7 +48,7 @@ type testConfigValues struct {
 	AliasJSON           string   `json:"aliases"`
 	BlacklistedAccounts []string `json:"blacklistedAccts"`
 	BlacklistedApps     []string `json:"blacklistedApps"`
-	AdapterList         []string `json:"disabledAdapters"`
+	DisabledAdapters    []string `json:"disabledAdapters"`
 }
 
 func TestJsonSampleRequests(t *testing.T) {
@@ -229,9 +229,9 @@ func (tc *testConfigValues) getBlackListedAccountMap() map[string]bool {
 func (tc *testConfigValues) getAdaptersConfigMap() map[string]config.Adapter {
 	var adaptersConfig map[string]config.Adapter
 
-	if len(tc.AdapterList) > 0 {
-		adaptersConfig = make(map[string]config.Adapter, len(tc.AdapterList))
-		for _, adapterName := range tc.AdapterList {
+	if len(tc.DisabledAdapters) > 0 {
+		adaptersConfig = make(map[string]config.Adapter, len(tc.DisabledAdapters))
+		for _, adapterName := range tc.DisabledAdapters {
 			adaptersConfig[adapterName] = config.Adapter{Disabled: true}
 		}
 	}
@@ -1505,7 +1505,9 @@ func TestCCPAInvalid(t *testing.T) {
 
 	errL := deps.validateRequest(&req)
 
-	expectedWarning := errortypes.InvalidPrivacyConsent{Message: "CCPA consent is invalid and will be ignored. (request.regs.ext.us_privacy must contain 4 characters)"}
+	expectedWarning := errortypes.Warning{
+		Message:     "CCPA consent is invalid and will be ignored. (request.regs.ext.us_privacy must contain 4 characters)",
+		WarningCode: errortypes.InvalidPrivacyConsentWarningCode}
 	assert.ElementsMatch(t, errL, []error{&expectedWarning})
 
 	assert.Empty(t, req.Regs.Ext, "Invalid Consent Removed From Request")
@@ -2104,6 +2106,55 @@ func TestIOS14EndToEnd(t *testing.T) {
 	assert.Equal(t, &lmtOne, result.Device.Lmt)
 }
 
+func TestAuctionWarnings(t *testing.T) {
+	reqBody := validRequest(t, "us-privacy-invalid.json")
+	deps := &endpointDeps{
+		&warningsCheckExchange{},
+		newParamsValidator(t),
+		&mockStoredReqFetcher{},
+		empty_fetcher.EmptyFetcher{},
+		empty_fetcher.EmptyFetcher{},
+		&config.Configuration{MaxRequestSize: int64(len(reqBody))},
+		newTestMetrics(),
+		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
+		map[string]string{},
+		false,
+		[]byte{},
+		openrtb_ext.BuildBidderMap(),
+		nil,
+		nil,
+		hardcodedResponseIPValidator{response: true},
+	}
+
+	req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody))
+	recorder := httptest.NewRecorder()
+
+	deps.Auction(recorder, req, nil)
+
+	if recorder.Code != http.StatusOK {
+		t.Errorf("Endpoint should return a 200")
+	}
+	warnings := deps.ex.(*warningsCheckExchange).auctionRequest.Warnings
+	if !assert.Len(t, warnings, 1, "One warning should be returned from exchange") {
+		t.FailNow()
+	}
+	actualWarning := warnings[0].(*errortypes.Warning)
+	expectedMessage := "CCPA consent is invalid and will be ignored. (request.regs.ext.us_privacy must contain 4 characters)"
+	assert.Equal(t, expectedMessage, actualWarning.Message, "Warning message is incorrect")
+
+	assert.Equal(t, errortypes.InvalidPrivacyConsentWarningCode, actualWarning.WarningCode, "Warning code is incorrect")
+}
+
+// warningsCheckExchange is a well-behaved exchange which stores all incoming warnings.
+type warningsCheckExchange struct {
+	auctionRequest exchange.AuctionRequest
+}
+
+func (e *warningsCheckExchange) HoldAuction(ctx context.Context, r exchange.AuctionRequest, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) {
+	e.auctionRequest = r
+	return nil, nil
+}
+
 // nobidExchange is a well-behaved exchange which always bids "no bid".
 type nobidExchange struct {
 	gotRequest *openrtb.BidRequest
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index fc98608e..87c98f23 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -462,8 +462,3 @@ func (s fakeSyncer) FamilyName() string {
 func (s fakeSyncer) GetUsersyncInfo(privacyPolicies privacy.Policies) (*usersync.UsersyncInfo, error) {
 	return nil, nil
 }
-
-// GDPRVendorID implements the Usersyncer interface with a no-op.
-func (s fakeSyncer) GDPRVendorID() uint16 {
-	return 0
-}
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index b829ddd7..e4eb7b95 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -20,6 +20,7 @@ import (
 	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/currency"
+	"github.com/prebid/prebid-server/errortypes"
 	"github.com/prebid/prebid-server/metrics"
 	metricsConfig "github.com/prebid/prebid-server/metrics/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
@@ -109,9 +110,13 @@ func TestSingleBidder(t *testing.T) {
 		}
 
 		// Make sure the returned values are what we expect
-		if len(errs) != 0 {
+		if len(errortypes.FatalOnly(errs)) != 0 {
 			t.Errorf("bidder.Bid returned %d errors. Expected 0", len(errs))
 		}
+
+		if !test.debugInfo.Allow && len(errortypes.WarningOnly(errs)) != 1 {
+			t.Errorf("bidder.Bid returned %d warnings. Expected 1", len(errs))
+		}
 		if len(seatBid.bids) != len(mockBidderResponse.Bids) {
 			t.Fatalf("Expected %d bids. Got %d", len(mockBidderResponse.Bids), len(seatBid.bids))
 		}
@@ -135,10 +140,6 @@ func TestSingleBidder(t *testing.T) {
 		if len(seatBid.httpCalls) != test.httpCallsLen {
 			t.Errorf("The bidder shouldn't log HttpCalls when request.test == 0. Found %d", len(seatBid.httpCalls))
 		}
-
-		if len(seatBid.ext) != 0 {
-			t.Errorf("The bidder shouldn't define any seatBid.ext. Got %s", string(seatBid.ext))
-		}
 	}
 }
 
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 7507ecef..3eaa625f 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -6,6 +6,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"github.com/prebid/prebid-server/errortypes"
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
@@ -141,7 +142,7 @@ func TestCharacterEscape(t *testing.T) {
 	adapterExtra := make(map[openrtb_ext.BidderName]*seatResponseExtra, 1)
 	adapterExtra["appnexus"] = &seatResponseExtra{
 		ResponseTimeMillis: 5,
-		Errors:             []openrtb_ext.ExtBidderError{{Code: 999, Message: "Post ib.adnxs.com/openrtb2?query1&query2: unsupported protocol scheme \"\""}},
+		Errors:             []openrtb_ext.ExtBidderMessage{{Code: 999, Message: "Post ib.adnxs.com/openrtb2?query1&query2: unsupported protocol scheme \"\""}},
 	}
 
 	var errList []error
@@ -180,59 +181,68 @@ func TestDebugBehaviour(t *testing.T) {
 	}
 
 	type aTest struct {
-		desc      string
-		in        inTest
-		out       outTest
-		debugData debugData
+		desc             string
+		in               inTest
+		out              outTest
+		debugData        debugData
+		generateWarnings bool
 	}
 	testCases := []aTest{
 		{
-			desc:      "test flag equals zero, ext debug flag false, no debug info expected",
-			in:        inTest{test: 0, debug: false},
-			out:       outTest{debugInfoIncluded: false},
-			debugData: debugData{true, true},
+			desc:             "test flag equals zero, ext debug flag false, no debug info expected",
+			in:               inTest{test: 0, debug: false},
+			out:              outTest{debugInfoIncluded: false},
+			debugData:        debugData{true, true},
+			generateWarnings: false,
 		},
 		{
-			desc:      "test flag equals zero, ext debug flag true, debug info expected",
-			in:        inTest{test: 0, debug: true},
-			out:       outTest{debugInfoIncluded: true},
-			debugData: debugData{true, true},
+			desc:             "test flag equals zero, ext debug flag true, debug info expected",
+			in:               inTest{test: 0, debug: true},
+			out:              outTest{debugInfoIncluded: true},
+			debugData:        debugData{true, true},
+			generateWarnings: false,
 		},
 		{
-			desc:      "test flag equals 1, ext debug flag false, debug info expected",
-			in:        inTest{test: 1, debug: false},
-			out:       outTest{debugInfoIncluded: true},
-			debugData: debugData{true, true},
+			desc:             "test flag equals 1, ext debug flag false, debug info expected",
+			in:               inTest{test: 1, debug: false},
+			out:              outTest{debugInfoIncluded: true},
+			debugData:        debugData{true, true},
+			generateWarnings: false,
 		},
 		{
-			desc:      "test flag equals 1, ext debug flag true, debug info expected",
-			in:        inTest{test: 1, debug: true},
-			out:       outTest{debugInfoIncluded: true},
-			debugData: debugData{true, true},
+			desc:             "test flag equals 1, ext debug flag true, debug info expected",
+			in:               inTest{test: 1, debug: true},
+			out:              outTest{debugInfoIncluded: true},
+			debugData:        debugData{true, true},
+			generateWarnings: false,
 		},
 		{
-			desc:      "test flag not equal to 0 nor 1, ext debug flag false, no debug info expected",
-			in:        inTest{test: 2, debug: false},
-			out:       outTest{debugInfoIncluded: false},
-			debugData: debugData{true, true},
+			desc:             "test flag not equal to 0 nor 1, ext debug flag false, no debug info expected",
+			in:               inTest{test: 2, debug: false},
+			out:              outTest{debugInfoIncluded: false},
+			debugData:        debugData{true, true},
+			generateWarnings: false,
 		},
 		{
-			desc:      "test flag not equal to 0 nor 1, ext debug flag true, debug info expected",
-			in:        inTest{test: -1, debug: true},
-			out:       outTest{debugInfoIncluded: true},
-			debugData: debugData{true, true},
+			desc:             "test flag not equal to 0 nor 1, ext debug flag true, debug info expected",
+			in:               inTest{test: -1, debug: true},
+			out:              outTest{debugInfoIncluded: true},
+			debugData:        debugData{true, true},
+			generateWarnings: true,
 		},
 		{
-			desc:      "test account level debug disabled",
-			in:        inTest{test: -1, debug: true},
-			out:       outTest{debugInfoIncluded: false},
-			debugData: debugData{true, false},
+			desc:             "test account level debug disabled",
+			in:               inTest{test: -1, debug: true},
+			out:              outTest{debugInfoIncluded: false},
+			debugData:        debugData{true, false},
+			generateWarnings: true,
 		},
 		{
-			desc:      "test bidder level debug disabled",
-			in:        inTest{test: -1, debug: true},
-			out:       outTest{debugInfoIncluded: false},
-			debugData: debugData{false, true},
+			desc:             "test bidder level debug disabled",
+			in:               inTest{test: -1, debug: true},
+			out:              outTest{debugInfoIncluded: false},
+			debugData:        debugData{false, true},
+			generateWarnings: true,
 		},
 	}
 
@@ -243,9 +253,9 @@ func TestDebugBehaviour(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(noBidServer))
 	defer server.Close()
 
-	categoriesFetcher, error := newCategoryFetcher("./test/category-mapping")
-	if error != nil {
-		t.Errorf("Failed to create a category Fetcher: %v", error)
+	categoriesFetcher, err := newCategoryFetcher("./test/category-mapping")
+	if err != nil {
+		t.Errorf("Failed to create a category Fetcher: %v", err)
 	}
 
 	bidRequest := &openrtb.BidRequest{
@@ -305,6 +315,13 @@ func TestDebugBehaviour(t *testing.T) {
 			UserSyncs:  &emptyUsersync{},
 			StartTime:  time.Now(),
 		}
+		if test.generateWarnings {
+			var errL []error
+			errL = append(errL, &errortypes.Warning{
+				Message:     fmt.Sprintf("CCPA consent test warning."),
+				WarningCode: errortypes.InvalidPrivacyConsentWarningCode})
+			auctionRequest.Warnings = errL
+		}
 
 		// Run test
 		outBidResponse, err := e.HoldAuction(ctx, auctionRequest, nil)
@@ -339,6 +356,35 @@ func TestDebugBehaviour(t *testing.T) {
 		} else {
 			assert.Nil(t, actualExt.Debug, "%s. ext.debug.httpcalls array should not be empty", "With bidder level debug disable option http calls should be empty")
 		}
+
+		if test.out.debugInfoIncluded && !test.debugData.accountLevelDebugAllowed {
+			assert.Len(t, actualExt.Warnings, 1, "warnings should have one warning")
+			assert.NotNil(t, actualExt.Warnings["general"], "general warning should be present")
+			assert.Equal(t, "debug turned off for account", actualExt.Warnings["general"][0].Message, "account debug disabled message should be present")
+		}
+
+		if !test.out.debugInfoIncluded && test.in.debug && test.debugData.accountLevelDebugAllowed {
+			if test.generateWarnings {
+				assert.Len(t, actualExt.Warnings, 2, "warnings should have one warning")
+			} else {
+				assert.Len(t, actualExt.Warnings, 1, "warnings should have one warning")
+			}
+			assert.NotNil(t, actualExt.Warnings["appnexus"], "bidder warning should be present")
+			assert.Equal(t, "debug turned off for bidder", actualExt.Warnings["appnexus"][0].Message, "account debug disabled message should be present")
+		}
+
+		if test.generateWarnings {
+			assert.NotNil(t, actualExt.Warnings["general"], "general warning should be present")
+			CCPAWarningPresent := false
+			for _, warn := range actualExt.Warnings["general"] {
+				if warn.Code == errortypes.InvalidPrivacyConsentWarningCode {
+					CCPAWarningPresent = true
+					break
+				}
+			}
+			assert.True(t, CCPAWarningPresent, "CCPA Warning should be present")
+		}
+
 	}
 }
 
@@ -393,7 +439,7 @@ func TestTwoBiddersDebugDisabledAndEnabled(t *testing.T) {
 	e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
 	e.categoriesFetcher = categoriesFetcher
 
-	debugLog := DebugLog{}
+	debugLog := DebugLog{Enabled: true}
 
 	for _, testCase := range testCases {
 		bidRequest := &openrtb.BidRequest{
@@ -762,7 +808,7 @@ func TestGetBidCacheInfoEndToEnd(t *testing.T) {
 	adapterExtra := map[openrtb_ext.BidderName]*seatResponseExtra{
 		bidderName: {
 			ResponseTimeMillis: 5,
-			Errors: []openrtb_ext.ExtBidderError{
+			Errors: []openrtb_ext.ExtBidderMessage{
 				{
 					Code:    999,
 					Message: "Post ib.adnxs.com/openrtb2?query1&query2: unsupported protocol scheme \"\"",
@@ -1823,7 +1869,7 @@ func TestCategoryMapping(t *testing.T) {
 		&bid1_4,
 	}
 
-	seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil}
+	seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"}
 	bidderName1 := openrtb_ext.BidderName("appnexus")
 
 	adapterBids[bidderName1] = &seatBid
@@ -1878,7 +1924,7 @@ func TestCategoryMappingNoIncludeBrandCategory(t *testing.T) {
 		&bid1_4,
 	}
 
-	seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil}
+	seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"}
 	bidderName1 := openrtb_ext.BidderName("appnexus")
 
 	adapterBids[bidderName1] = &seatBid
@@ -1930,7 +1976,7 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) {
 		&bid1_3,
 	}
 
-	seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil}
+	seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"}
 	bidderName1 := openrtb_ext.BidderName("appnexus")
 
 	adapterBids[bidderName1] = &seatBid
@@ -2012,7 +2058,7 @@ func TestCategoryMappingTranslateCategoriesFalse(t *testing.T) {
 		&bid1_3,
 	}
 
-	seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil}
+	seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"}
 	bidderName1 := openrtb_ext.BidderName("appnexus")
 
 	adapterBids[bidderName1] = &seatBid
@@ -2082,7 +2128,7 @@ func TestCategoryDedupe(t *testing.T) {
 			&bid1_5,
 		}
 
-		seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil}
+		seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"}
 		bidderName1 := openrtb_ext.BidderName("appnexus")
 
 		adapterBids[bidderName1] = &seatBid
@@ -2162,7 +2208,7 @@ func TestNoCategoryDedupe(t *testing.T) {
 			&bid1_5,
 		}
 
-		seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil}
+		seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"}
 		bidderName1 := openrtb_ext.BidderName("appnexus")
 
 		adapterBids[bidderName1] = &seatBid
@@ -2223,10 +2269,10 @@ func TestCategoryMappingBidderName(t *testing.T) {
 		&bid1_2,
 	}
 
-	seatBid1 := pbsOrtbSeatBid{innerBids1, "USD", nil, nil}
+	seatBid1 := pbsOrtbSeatBid{bids: innerBids1, currency: "USD"}
 	bidderName1 := openrtb_ext.BidderName("bidder1")
 
-	seatBid2 := pbsOrtbSeatBid{innerBids2, "USD", nil, nil}
+	seatBid2 := pbsOrtbSeatBid{bids: innerBids2, currency: "USD"}
 	bidderName2 := openrtb_ext.BidderName("bidder2")
 
 	adapterBids[bidderName1] = &seatBid1
@@ -2277,10 +2323,10 @@ func TestCategoryMappingBidderNameNoCategories(t *testing.T) {
 		&bid1_2,
 	}
 
-	seatBid1 := pbsOrtbSeatBid{innerBids1, "USD", nil, nil}
+	seatBid1 := pbsOrtbSeatBid{bids: innerBids1, currency: "USD"}
 	bidderName1 := openrtb_ext.BidderName("bidder1")
 
-	seatBid2 := pbsOrtbSeatBid{innerBids2, "USD", nil, nil}
+	seatBid2 := pbsOrtbSeatBid{bids: innerBids2, currency: "USD"}
 	bidderName2 := openrtb_ext.BidderName("bidder2")
 
 	adapterBids[bidderName1] = &seatBid1
@@ -2384,7 +2430,7 @@ func TestBidRejectionErrors(t *testing.T) {
 			innerBids = append(innerBids, &currentBid)
 		}
 
-		seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil}
+		seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"}
 
 		adapterBids[bidderName] = &seatBid
 
@@ -2442,10 +2488,10 @@ func TestCategoryMappingTwoBiddersOneBidEachNoCategorySamePrice(t *testing.T) {
 	for i := 1; i < 10; i++ {
 		adapterBids := make(map[openrtb_ext.BidderName]*pbsOrtbSeatBid)
 
-		seatBidApn1 := pbsOrtbSeatBid{innerBidsApn1, "USD", nil, nil}
+		seatBidApn1 := pbsOrtbSeatBid{bids: innerBidsApn1, currency: "USD"}
 		bidderNameApn1 := openrtb_ext.BidderName("appnexus1")
 
-		seatBidApn2 := pbsOrtbSeatBid{innerBidsApn2, "USD", nil, nil}
+		seatBidApn2 := pbsOrtbSeatBid{bids: innerBidsApn2, currency: "USD"}
 		bidderNameApn2 := openrtb_ext.BidderName("appnexus2")
 
 		adapterBids[bidderNameApn1] = &seatBidApn1
diff --git a/exchange/exchangetest/bid-ext-prebid-collision.json b/exchange/exchangetest/bid-ext-prebid-collision.json
new file mode 100644
index 00000000..054671ce
--- /dev/null
+++ b/exchange/exchangetest/bid-ext-prebid-collision.json
@@ -0,0 +1,90 @@
+{
+    "description": "Verifies bid.ext values are left alone from the adapter, except for overwriting bid.ext.prebid if the adapter ext includes a collision.",
+
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [{
+                "id": "my-imp-id",
+                "video": {
+                    "mimes": ["video/mp4"]
+                },
+                "ext": {
+                    "appnexus": {
+                        "placementId": 1
+                    }
+                }
+            }]
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [{
+                        "id": "my-imp-id",
+                        "video": {
+                            "mimes": ["video/mp4"]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": 1
+                            }
+                        }
+                    }]
+                },
+                "bidAdjustment": 1.0
+            },
+            "mockResponse": {
+                "pbsSeatBid": {
+                    "pbsBids": [{
+                        "ortbBid": {
+                            "id": "apn-bid",
+                            "impid": "my-imp-id",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 250,
+                            "crid": "creative-1",
+                            "ext": {
+                                "someField": "someValue",
+                                "prebid": {
+                                    "willBeOverwritten": "by core logic"
+                                }
+                            }
+                        },
+                        "bidType": "video"
+                    }]
+                }
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [{
+                "seat": "appnexus",
+                "bid": [{
+                    "id": "apn-bid",
+                    "impid": "my-imp-id",
+                    "price": 0.3,
+                    "w": 200,
+                    "h": 250,
+                    "crid": "creative-1",
+                    "ext": {
+                        "someField": "someValue",
+                        "prebid": {
+                            "type": "video"
+                        }
+                    }
+                }]
+            }]
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/bid-ext.json b/exchange/exchangetest/bid-ext.json
new file mode 100644
index 00000000..8211ac88
--- /dev/null
+++ b/exchange/exchangetest/bid-ext.json
@@ -0,0 +1,87 @@
+{
+    "description": "Verifies bid.ext values are left alone from the adapter, except for adding in bid.ext.prebid.",
+
+    "incomingRequest": {
+        "ortbRequest": {
+            "id": "some-request-id",
+            "site": {
+                "page": "test.somepage.com"
+            },
+            "imp": [{
+                "id": "my-imp-id",
+                "video": {
+                    "mimes": ["video/mp4"]
+                },
+                "ext": {
+                    "appnexus": {
+                        "placementId": 1
+                    }
+                }
+            }]
+        }
+    },
+    "outgoingRequests": {
+        "appnexus": {
+            "expectRequest": {
+                "ortbRequest": {
+                    "id": "some-request-id",
+                    "site": {
+                        "page": "test.somepage.com"
+                    },
+                    "imp": [{
+                        "id": "my-imp-id",
+                        "video": {
+                            "mimes": ["video/mp4"]
+                        },
+                        "ext": {
+                            "bidder": {
+                                "placementId": 1
+                            }
+                        }
+                    }]
+                },
+                "bidAdjustment": 1.0
+            },
+            "mockResponse": {
+                "pbsSeatBid": {
+                    "pbsBids": [{
+                        "ortbBid": {
+                            "id": "apn-bid",
+                            "impid": "my-imp-id",
+                            "price": 0.3,
+                            "w": 200,
+                            "h": 250,
+                            "crid": "creative-1",
+                            "ext": {
+                                "someField": "someValue"
+                            }
+                        },
+                        "bidType": "video"
+                    }]
+                }
+            }
+        }
+    },
+    "response": {
+        "bids": {
+            "id": "some-request-id",
+            "seatbid": [{
+                "seat": "appnexus",
+                "bid": [{
+                    "id": "apn-bid",
+                    "impid": "my-imp-id",
+                    "price": 0.3,
+                    "w": 200,
+                    "h": 250,
+                    "crid": "creative-1",
+                    "ext": {
+                        "someField": "someValue",
+                        "prebid": {
+                            "type": "video"
+                        }
+                    }
+                }]
+            }]
+        }
+    }
+}
\ No newline at end of file
diff --git a/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json b/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json 
similarity index 98%
rename from exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json
rename to exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json 
index d62afccf..37654c45 100644
--- a/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json
+++ b/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json 
@@ -61,7 +61,6 @@
                             "bidder": {
                                 "placementId": 1
                             },
-                            "prebid": {},
                             "context": {
                                 "data": {
                                     "keywords": "prebid server example"
@@ -112,7 +111,6 @@
                                 "siteId": 2,
                                 "zoneId": 3
                             },
-                            "prebid": {},
                             "context": {
                                 "data": {
                                     "keywords": "prebid server example"
diff --git a/exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json b/exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json
index 1610b9ea..c931b0b9 100644
--- a/exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json
+++ b/exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json
@@ -56,7 +56,6 @@
                             "bidder": {
                                 "placementId": 1
                             },
-                            "prebid": {},
                             "context": {
                                 "data": {
                                     "keywords": "prebid server example"
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 0407b3c5..1945aee1 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -55,6 +55,370 @@ func assertReq(t *testing.T, bidderRequests []BidderRequest,
 	}
 }
 
+func TestSplitImps(t *testing.T) {
+	testCases := []struct {
+		description   string
+		givenImps     []openrtb.Imp
+		expectedImps  map[string][]openrtb.Imp
+		expectedError string
+	}{
+		{
+			description:   "Nil",
+			givenImps:     nil,
+			expectedImps:  map[string][]openrtb.Imp{},
+			expectedError: "",
+		},
+		{
+			description:   "Empty",
+			givenImps:     []openrtb.Imp{},
+			expectedImps:  map[string][]openrtb.Imp{},
+			expectedError: "",
+		},
+		{
+			description: "1 Imp, 1 Bidder",
+			givenImps: []openrtb.Imp{
+				{ID: "imp1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp1ParamA":"imp1ValueA"}}}}`)},
+			},
+			expectedImps: map[string][]openrtb.Imp{
+				"bidderA": {
+					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1ParamA":"imp1ValueA"}}`)},
+				},
+			},
+			expectedError: "",
+		},
+		{
+			description: "1 Imp, 2 Bidders",
+			givenImps: []openrtb.Imp{
+				{ID: "imp1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp1ParamA":"imp1ValueA"},"bidderB":{"imp1ParamB":"imp1ValueB"}}}}`)},
+			},
+			expectedImps: map[string][]openrtb.Imp{
+				"bidderA": {
+					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1ParamA":"imp1ValueA"}}`)},
+				},
+				"bidderB": {
+					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1ParamB":"imp1ValueB"}}`)},
+				},
+			},
+			expectedError: "",
+		},
+		{
+			description: "2 Imps, 1 Bidders Each",
+			givenImps: []openrtb.Imp{
+				{ID: "imp1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp1ParamA":"imp1ValueA"}}}}`)},
+				{ID: "imp2", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp2ParamA":"imp2ValueA"}}}}`)},
+			},
+			expectedImps: map[string][]openrtb.Imp{
+				"bidderA": {
+					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1ParamA":"imp1ValueA"}}`)},
+					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2ParamA":"imp2ValueA"}}`)},
+				},
+			},
+			expectedError: "",
+		},
+		{
+			description: "2 Imps, 2 Bidders Each",
+			givenImps: []openrtb.Imp{
+				{ID: "imp1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp1paramA":"imp1valueA"},"bidderB":{"imp1paramB":"imp1valueB"}}}}`)},
+				{ID: "imp2", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp2paramA":"imp2valueA"},"bidderB":{"imp2paramB":"imp2valueB"}}}}`)},
+			},
+			expectedImps: map[string][]openrtb.Imp{
+				"bidderA": {
+					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1paramA":"imp1valueA"}}`)},
+					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2paramA":"imp2valueA"}}`)},
+				},
+				"bidderB": {
+					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1paramB":"imp1valueB"}}`)},
+					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2paramB":"imp2valueB"}}`)},
+				},
+			},
+			expectedError: "",
+		},
+		{
+			// This is a "happy path" integration test. Functionality is covered in detail by TestCreateSanitizedImpExt.
+			description: "Other Fields - 2 Imps, 2 Bidders Each",
+			givenImps: []openrtb.Imp{
+				{ID: "imp1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp1paramA":"imp1valueA"},"bidderB":{"imp1paramB":"imp1valueB"}}},"skadn":"imp1SkAdN"}`)},
+				{ID: "imp2", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp2paramA":"imp2valueA"},"bidderB":{"imp2paramB":"imp2valueB"}}},"skadn":"imp2SkAdN"}`)},
+			},
+			expectedImps: map[string][]openrtb.Imp{
+				"bidderA": {
+					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1paramA":"imp1valueA"},"skadn":"imp1SkAdN"}`)},
+					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2paramA":"imp2valueA"},"skadn":"imp2SkAdN"}`)},
+				},
+				"bidderB": {
+					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1paramB":"imp1valueB"},"skadn":"imp1SkAdN"}`)},
+					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2paramB":"imp2valueB"},"skadn":"imp2SkAdN"}`)},
+				},
+			},
+			expectedError: "",
+		},
+		{
+			// This is a "happy path" integration test. Functionality is covered in detail by TestExtractBidderExts.
+			description: "Legacy imp.ext.BIDDER - 2 Imps, 2 Bidders Each",
+			givenImps: []openrtb.Imp{
+				{ID: "imp1", Ext: json.RawMessage(`{"bidderA":{"imp1paramA":"imp1valueA"},"bidderB":{"imp1paramB":"imp1valueB"}}`)},
+				{ID: "imp2", Ext: json.RawMessage(`{"bidderA":{"imp2paramA":"imp2valueA"},"bidderB":{"imp2paramB":"imp2valueB"}}`)},
+			},
+			expectedImps: map[string][]openrtb.Imp{
+				"bidderA": {
+					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1paramA":"imp1valueA"}}`)},
+					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2paramA":"imp2valueA"}}`)},
+				},
+				"bidderB": {
+					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1paramB":"imp1valueB"}}`)},
+					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2paramB":"imp2valueB"}}`)},
+				},
+			},
+			expectedError: "",
+		},
+		{
+			description: "Malformed imp.ext",
+			givenImps: []openrtb.Imp{
+				{ID: "imp1", Ext: json.RawMessage(`malformed`)},
+			},
+			expectedError: "invalid json for imp[0]: invalid character 'm' looking for beginning of value",
+		},
+		{
+			description: "Malformed imp.ext.prebid",
+			givenImps: []openrtb.Imp{
+				{ID: "imp1", Ext: json.RawMessage(`{"prebid": malformed}`)},
+			},
+			expectedError: "invalid json for imp[0]: invalid character 'm' looking for beginning of value",
+		},
+		{
+			description: "Malformed imp.ext.prebid.bidder",
+			givenImps: []openrtb.Imp{
+				{ID: "imp1", Ext: json.RawMessage(`{"prebid": {"bidder": malformed}}`)},
+			},
+			expectedError: "invalid json for imp[0]: invalid character 'm' looking for beginning of value",
+		},
+	}
+
+	for _, test := range testCases {
+		imps, err := splitImps(test.givenImps)
+
+		if test.expectedError == "" {
+			assert.NoError(t, err, test.description+":err")
+		} else {
+			assert.EqualError(t, err, test.expectedError, test.description+":err")
+		}
+
+		assert.Equal(t, test.expectedImps, imps, test.description+":imps")
+	}
+}
+
+func TestCreateSanitizedImpExt(t *testing.T) {
+	testCases := []struct {
+		description       string
+		givenImpExt       map[string]json.RawMessage
+		givenImpExtPrebid map[string]json.RawMessage
+		expected          map[string]json.RawMessage
+		expectedError     string
+	}{
+		{
+			description:       "Nil",
+			givenImpExt:       nil,
+			givenImpExtPrebid: nil,
+			expected:          map[string]json.RawMessage{},
+			expectedError:     "",
+		},
+		{
+			description:       "Empty",
+			givenImpExt:       map[string]json.RawMessage{},
+			givenImpExtPrebid: map[string]json.RawMessage{},
+			expected:          map[string]json.RawMessage{},
+			expectedError:     "",
+		},
+		{
+			description: "imp.ext.prebid - Bidders Only",
+			givenImpExt: map[string]json.RawMessage{
+				"prebid":  json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`),
+				"context": json.RawMessage(`"anyContext"`),
+				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+			},
+			givenImpExtPrebid: map[string]json.RawMessage{
+				"bidder": json.RawMessage(`"anyBidder"`),
+			},
+			expected: map[string]json.RawMessage{
+				"context": json.RawMessage(`"anyContext"`),
+				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+			},
+			expectedError: "",
+		},
+		{
+			description: "imp.ext.prebid - Bidders + Other Values",
+			givenImpExt: map[string]json.RawMessage{
+				"prebid":  json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`),
+				"context": json.RawMessage(`"anyContext"`),
+				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+			},
+			givenImpExtPrebid: map[string]json.RawMessage{
+				"bidder":    json.RawMessage(`"anyBidder"`),
+				"someOther": json.RawMessage(`"value"`),
+			},
+			expected: map[string]json.RawMessage{
+				"prebid":  json.RawMessage(`{"someOther":"value"}`),
+				"context": json.RawMessage(`"anyContext"`),
+				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+			},
+			expectedError: "",
+		},
+		{
+			description: "imp.ext",
+			givenImpExt: map[string]json.RawMessage{
+				"anyBidder": json.RawMessage(`"anyBidderValues"`),
+				"context":   json.RawMessage(`"anyContext"`),
+				"skadn":     json.RawMessage(`"anySKAdNetwork"`),
+			},
+			givenImpExtPrebid: map[string]json.RawMessage{},
+			expected: map[string]json.RawMessage{
+				"context": json.RawMessage(`"anyContext"`),
+				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+			},
+			expectedError: "",
+		},
+		{
+			description: "imp.ext + imp.ext.prebid - Prebid Bidders Only",
+			givenImpExt: map[string]json.RawMessage{
+				"anyBidder": json.RawMessage(`"anyBidderValues"`),
+				"prebid":    json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`),
+				"context":   json.RawMessage(`"anyContext"`),
+				"skadn":     json.RawMessage(`"anySKAdNetwork"`),
+			},
+			givenImpExtPrebid: map[string]json.RawMessage{
+				"bidder": json.RawMessage(`"anyBidder"`),
+			},
+			expected: map[string]json.RawMessage{
+				"context": json.RawMessage(`"anyContext"`),
+				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+			},
+			expectedError: "",
+		},
+		{
+			description: "imp.ext + imp.ext.prebid - Prebid Bidders + Other Values",
+			givenImpExt: map[string]json.RawMessage{
+				"anyBidder": json.RawMessage(`"anyBidderValues"`),
+				"prebid":    json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`),
+				"context":   json.RawMessage(`"anyContext"`),
+				"skadn":     json.RawMessage(`"anySKAdNetwork"`),
+			},
+			givenImpExtPrebid: map[string]json.RawMessage{
+				"bidder":    json.RawMessage(`"anyBidder"`),
+				"someOther": json.RawMessage(`"value"`),
+			},
+			expected: map[string]json.RawMessage{
+				"prebid":  json.RawMessage(`{"someOther":"value"}`),
+				"context": json.RawMessage(`"anyContext"`),
+				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+			},
+			expectedError: "",
+		},
+		{
+			description: "Marshal Error - imp.ext.prebid",
+			givenImpExt: map[string]json.RawMessage{
+				"prebid":  json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`),
+				"context": json.RawMessage(`"anyContext"`),
+				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+			},
+			givenImpExtPrebid: map[string]json.RawMessage{
+				"malformed": json.RawMessage(`json`), // String value without quotes.
+			},
+			expected:      nil,
+			expectedError: "cannot marshal ext.prebid: json: error calling MarshalJSON for type json.RawMessage: invalid character 'j' looking for beginning of value",
+		},
+	}
+
+	for _, test := range testCases {
+		result, err := createSanitizedImpExt(test.givenImpExt, test.givenImpExtPrebid)
+
+		if test.expectedError == "" {
+			assert.NoError(t, err, test.description+":err")
+		} else {
+			assert.EqualError(t, err, test.expectedError, test.description+":err")
+		}
+
+		assert.Equal(t, test.expected, result, test.description)
+	}
+}
+
+func TestExtractBidderExts(t *testing.T) {
+	bidderAJSON := json.RawMessage(`{"paramA":"valueA"}}`)
+	bidderBJSON := json.RawMessage(`{"paramB":"valueB"}}`)
+
+	testCases := []struct {
+		description              string
+		givenImpExt              map[string]json.RawMessage
+		givenImpExtPrebidBidders map[string]json.RawMessage
+		expected                 map[string]json.RawMessage
+	}{
+		{
+			description:              "Nil",
+			givenImpExt:              nil,
+			givenImpExtPrebidBidders: nil,
+			expected:                 map[string]json.RawMessage{},
+		},
+		{
+			description:              "Empty",
+			givenImpExt:              map[string]json.RawMessage{},
+			givenImpExtPrebidBidders: map[string]json.RawMessage{},
+			expected:                 map[string]json.RawMessage{},
+		},
+		{
+			description:              "One - imp.ext.BIDDER",
+			givenImpExt:              map[string]json.RawMessage{"bidderA": bidderAJSON},
+			givenImpExtPrebidBidders: map[string]json.RawMessage{},
+			expected:                 map[string]json.RawMessage{"bidderA": bidderAJSON},
+		},
+		{
+			description:              "Many - imp.ext.BIDDER",
+			givenImpExt:              map[string]json.RawMessage{"bidderA": bidderAJSON, "bidderB": bidderBJSON},
+			givenImpExtPrebidBidders: map[string]json.RawMessage{},
+			expected:                 map[string]json.RawMessage{"bidderA": bidderAJSON, "bidderB": bidderBJSON},
+		},
+		{
+			description:              "Special Names Ignored - imp.ext.BIDDER",
+			givenImpExt:              map[string]json.RawMessage{"prebid": json.RawMessage(`{"prebid":"value1"}}`), "context": json.RawMessage(`{"firstPartyData":"value2"}}`), "skadn": json.RawMessage(`{"skAdNetwork":"value3"}}`)},
+			givenImpExtPrebidBidders: map[string]json.RawMessage{},
+			expected:                 map[string]json.RawMessage{},
+		},
+		{
+			description:              "One - imp.ext.prebid.bidder.BIDDER",
+			givenImpExt:              map[string]json.RawMessage{},
+			givenImpExtPrebidBidders: map[string]json.RawMessage{"bidderA": bidderAJSON},
+			expected:                 map[string]json.RawMessage{"bidderA": bidderAJSON},
+		},
+		{
+			description:              "Many - imp.ext.prebid.bidder.BIDDER",
+			givenImpExt:              map[string]json.RawMessage{},
+			givenImpExtPrebidBidders: map[string]json.RawMessage{"bidderA": bidderAJSON, "bidderB": bidderBJSON},
+			expected:                 map[string]json.RawMessage{"bidderA": bidderAJSON, "bidderB": bidderBJSON},
+		},
+		{
+			description:              "Special Names Not Treated Differently - imp.ext.prebid.bidder.BIDDER",
+			givenImpExt:              map[string]json.RawMessage{},
+			givenImpExtPrebidBidders: map[string]json.RawMessage{"prebid": json.RawMessage(`{"prebid":"value1"}}`), "context": json.RawMessage(`{"firstPartyData":"value2"}}`), "skadn": json.RawMessage(`{"skAdNetwork":"value3"}}`)},
+			expected:                 map[string]json.RawMessage{"prebid": json.RawMessage(`{"prebid":"value1"}}`), "context": json.RawMessage(`{"firstPartyData":"value2"}}`), "skadn": json.RawMessage(`{"skAdNetwork":"value3"}}`)},
+		},
+		{
+			description:              "Mixed - Both - imp.ext.BIDDER + imp.ext.prebid.bidder.BIDDER",
+			givenImpExt:              map[string]json.RawMessage{"bidderA": bidderAJSON},
+			givenImpExtPrebidBidders: map[string]json.RawMessage{"bidderB": bidderBJSON},
+			expected:                 map[string]json.RawMessage{"bidderA": bidderAJSON, "bidderB": bidderBJSON},
+		},
+		{
+			description:              "Mixed - Overwrites - imp.ext.BIDDER + imp.ext.prebid.bidder.BIDDER",
+			givenImpExt:              map[string]json.RawMessage{"bidderA": json.RawMessage(`{"shouldBe":"Ignored"}}`)},
+			givenImpExtPrebidBidders: map[string]json.RawMessage{"bidderA": bidderAJSON},
+			expected:                 map[string]json.RawMessage{"bidderA": bidderAJSON},
+		},
+	}
+
+	for _, test := range testCases {
+		result := extractBidderExts(test.givenImpExt, test.givenImpExtPrebidBidders)
+		assert.Equal(t, test.expected, result, test.description)
+	}
+}
+
 func TestCleanOpenRTBRequests(t *testing.T) {
 	testCases := []struct {
 		req              AuctionRequest
@@ -273,7 +637,10 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) {
 			description: "Invalid Consent",
 			reqExt:      json.RawMessage(`{"prebid":{"nosale":["*"]}}`),
 			reqRegsExt:  json.RawMessage(`{"us_privacy":"malformed"}`),
-			expectError: &errortypes.InvalidPrivacyConsent{"request.regs.ext.us_privacy must contain 4 characters"},
+			expectError: &errortypes.Warning{
+				Message:     "request.regs.ext.us_privacy must contain 4 characters",
+				WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
+			},
 		},
 		{
 			description: "Invalid No Sale Bidders",
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 9727a2d4..5b2e8046 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -136,31 +136,3 @@ func TestNewSyncerMap(t *testing.T) {
 		}
 	}
 }
-
-// Bidders may have an ID on the IAB-maintained global vendor list.
-// This makes sure that we don't have conflicting IDs among Bidders in our project,
-// since that's almost certainly a bug.
-func TestVendorIDUniqueness(t *testing.T) {
-	cfg := &config.Configuration{}
-	syncers := NewSyncerMap(cfg)
-
-	idMap := make(map[uint16]openrtb_ext.BidderName, len(syncers))
-	for name, syncer := range syncers {
-		id := syncer.GDPRVendorID()
-		if id == 0 {
-			continue
-		}
-
-		if oldName, ok := idMap[id]; ok {
-			t.Errorf("GDPR VendorList ID %d used by both %s and %s. These must be unique.", id, oldName, name)
-		}
-		idMap[id] = name
-	}
-}
-
-func assertStringsMatch(t *testing.T, expected string, actual string) {
-	t.Helper()
-	if expected != actual {
-		t.Errorf("Expected %s, got %s", expected, actual)
-	}
-}