Repository: prebid/prebid-server
Pull request #1209 (2020-03-02 14:44:02)

Districtm Dmx: new adapter 
DistrictM new Adapter support for prebid server.
This adapter only support mediatype banner for website and app.

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

SyntaxNode: Friendly ping to keep the PR open. I saw you responded to some of the feedback so I assume you're working on this when time allows. It will auto close if you can't circle back to it right now, but that's ok. We can always open it up again when you do.
stale[bot]: This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

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

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

stevealliance: @SyntaxNode please review when you can!
hhhjort: You still have some merge conflicts remaining. It looks like you have resolved the conflicts between the master branch and your branch of your cloned repository, but your cloned master is currently not up to date with our master, and thus merge conflicts with our master remain. You should probably update your master branch, and then resolve the conflicts again.

Patch:
diff --git a/adapters/adtelligent/adtelligent.go b/adapters/adtelligent/adtelligent.go
index 7f0262fd..78a71dcf 100644
--- a/adapters/adtelligent/adtelligent.go
+++ b/adapters/adtelligent/adtelligent.go
@@ -55,7 +55,6 @@ func (a *AdtelligentAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *
 
 	imps := request.Imp
 	request.Imp = make([]openrtb.Imp, 0, len(imps))
-
 	for sourceId, impIds := range imp2source {
 		request.Imp = request.Imp[:0]
 
diff --git a/adapters/beachfront/usersync.go b/adapters/beachfront/usersync.go
index a255d4d9..a6502331 100644
--- a/adapters/beachfront/usersync.go
+++ b/adapters/beachfront/usersync.go
@@ -7,6 +7,12 @@ import (
 	"github.com/prebid/prebid-server/usersync"
 )
 
+var VENDOR_ID uint16 = 335
+
 func NewBeachfrontSyncer(temp *template.Template) usersync.Usersyncer {
-	return adapters.NewSyncer("beachfront", 0, temp, adapters.SyncTypeIframe)
+	return adapters.NewSyncer(
+		"beachfront",
+		VENDOR_ID,
+		temp,
+		adapters.SyncTypeIframe)
 }
diff --git a/adapters/dmx/dmx.go b/adapters/dmx/dmx.go
new file mode 100644
index 00000000..6b4f698d
--- /dev/null
+++ b/adapters/dmx/dmx.go
@@ -0,0 +1,296 @@
+package dmx
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"net/http"
+	"net/url"
+	"strings"
+)
+
+type DmxAdapter struct {
+	endpoint string
+}
+
+func NewDmxBidder(endpoint string) *DmxAdapter {
+	return &DmxAdapter{endpoint: endpoint}
+}
+
+type dmxExt struct {
+	Bidder dmxParams `json:"bidder"`
+}
+
+type dmxParams struct {
+	TagId       string `json:"tagid,omitempty"`
+	DmxId       string `json:"dmxid,omitempty"`
+	MemberId    string `json:"memberid,omitempty"`
+	PublisherId string `json:"publisher_id,omitempty"`
+	SellerId    string `json:"seller_id,omitempty"`
+}
+
+func UserSellerOrPubId(str1, str2 string) string {
+	if str1 != "" {
+		return str1
+	}
+	return str2
+}
+
+func (adapter *DmxAdapter) MakeRequests(request *openrtb.BidRequest, req *adapters.ExtraRequestInfo) (reqsBidder []*adapters.RequestData, errs []error) {
+	var imps []openrtb.Imp
+	var rootExtInfo dmxExt
+	var publisherId string
+	var sellerId string
+	var userExt openrtb_ext.ExtUser
+	var anyHasId = false
+	var reqCopy openrtb.BidRequest = *request
+	var dmxReq *openrtb.BidRequest = &reqCopy
+
+	if request.User == nil {
+		if request.App == nil {
+			return nil, []error{errors.New("No user id or app id found. Could not send request to DMX.")}
+		}
+	}
+
+	if len(request.Imp) >= 1 {
+		err := json.Unmarshal(request.Imp[0].Ext, &rootExtInfo)
+		if err != nil {
+			errs = append(errs, err)
+		} else {
+			publisherId = UserSellerOrPubId(rootExtInfo.Bidder.PublisherId, rootExtInfo.Bidder.MemberId)
+			sellerId = rootExtInfo.Bidder.SellerId
+		}
+	}
+
+	if request.App != nil {
+		appCopy := *request.App
+		appPublisherCopy := *request.App.Publisher
+		dmxReq.App = &appCopy
+		dmxReq.App.Publisher = &appPublisherCopy
+		if dmxReq.App.ID != "" {
+			anyHasId = true
+		}
+	} else {
+		dmxReq.App = nil
+	}
+
+	if request.Site != nil {
+		siteCopy := *request.Site
+		sitePublisherCopy := *request.Site.Publisher
+		dmxReq.Site = &siteCopy
+		dmxReq.Site.Publisher = &sitePublisherCopy
+		if dmxReq.Site.Publisher != nil {
+			dmxReq.Site.Publisher.ID = publisherId
+		} else {
+			dmxReq.Site.Publisher = &openrtb.Publisher{ID: publisherId}
+		}
+	} else {
+		dmxReq.Site = nil
+	}
+
+	if request.User != nil {
+		userCopy := *request.User
+		dmxReq.User = &userCopy
+	} else {
+		dmxReq.User = nil
+	}
+
+	if dmxReq.User != nil {
+		if dmxReq.User.ID != "" {
+			anyHasId = true
+		}
+		if dmxReq.User.Ext != nil {
+			if err := json.Unmarshal(dmxReq.User.Ext, &userExt); err == nil {
+				if len(userExt.Eids) > 0 || (userExt.DigiTrust != nil && userExt.DigiTrust.ID != "") {
+					anyHasId = true
+				}
+			}
+		}
+	}
+
+	if anyHasId == false {
+		return nil, []error{errors.New("This request contained no identifier")}
+	}
+
+	for _, inst := range dmxReq.Imp {
+		var banner *openrtb.Banner
+		var video *openrtb.Video
+		var ins openrtb.Imp
+		var params dmxExt
+		const intVal int8 = 1
+		source := (*json.RawMessage)(&inst.Ext)
+		if err := json.Unmarshal(*source, &params); err != nil {
+			errs = append(errs, err)
+		}
+		if isDmxParams(params.Bidder) {
+			if inst.Banner != nil {
+				if len(inst.Banner.Format) != 0 {
+					banner = inst.Banner
+					if params.Bidder.PublisherId != "" || params.Bidder.MemberId != "" {
+						imps = fetchParams(params, inst, ins, imps, banner, nil, intVal)
+					} else {
+						return nil, []error{errors.New("Missing Params for auction to be send")}
+					}
+				}
+			}
+
+			if inst.Video != nil {
+				video = inst.Video
+				if params.Bidder.PublisherId != "" || params.Bidder.MemberId != "" {
+					imps = fetchParams(params, inst, ins, imps, nil, video, intVal)
+				} else {
+					return nil, []error{errors.New("Missing Params for auction to be send")}
+				}
+			}
+		}
+
+	}
+
+	dmxReq.Imp = imps
+
+	oJson, err := json.Marshal(dmxReq)
+
+	if err != nil {
+		errs = append(errs, err)
+		return nil, errs
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "Application/json;charset=utf-8")
+	reqBidder := &adapters.RequestData{
+		Method:  "POST",
+		Uri:     adapter.endpoint + addParams(sellerId), //adapter.endpoint,
+		Body:    oJson,
+		Headers: headers,
+	}
+	reqsBidder = append(reqsBidder, reqBidder)
+	return
+}
+
+func (adapter *DmxAdapter) MakeBids(request *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	var errs []error
+
+	if http.StatusNoContent == response.StatusCode {
+		return nil, nil
+	}
+
+	if http.StatusBadRequest == response.StatusCode {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code 400"),
+		}}
+	}
+
+	if http.StatusOK != response.StatusCode {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected response no status code"),
+		}}
+	}
+
+	var bidResp openrtb.BidResponse
+
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)
+
+	for _, sb := range bidResp.SeatBid {
+		for i := range sb.Bid {
+			bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, request.Imp)
+			if err != nil {
+				errs = append(errs, err)
+			} else {
+				b := &adapters.TypedBid{
+					Bid:     &sb.Bid[i],
+					BidType: bidType,
+				}
+				if b.BidType == openrtb_ext.BidTypeVideo {
+					b.Bid.AdM = videoImpInsertion(b.Bid)
+				}
+				bidResponse.Bids = append(bidResponse.Bids, b)
+			}
+		}
+	}
+	return bidResponse, errs
+
+}
+
+func fetchParams(params dmxExt, inst openrtb.Imp, ins openrtb.Imp, imps []openrtb.Imp, banner *openrtb.Banner, video *openrtb.Video, intVal int8) []openrtb.Imp {
+	if params.Bidder.TagId != "" {
+		ins = openrtb.Imp{
+			ID:     inst.ID,
+			TagID:  params.Bidder.TagId,
+			Ext:    inst.Ext,
+			Secure: &intVal,
+		}
+	}
+
+	if params.Bidder.DmxId != "" {
+		ins = openrtb.Imp{
+			ID:     inst.ID,
+			TagID:  params.Bidder.DmxId,
+			Ext:    inst.Ext,
+			Secure: &intVal,
+		}
+	}
+	if banner != nil {
+		ins.Banner = banner
+	}
+
+	if video != nil {
+		ins.Video = video
+	}
+
+	if ins.TagID == "" {
+		return imps
+	}
+	imps = append(imps, ins)
+	return imps
+}
+
+func addParams(str string) string {
+	if str != "" {
+		return "?sellerid=" + url.QueryEscape(str)
+	}
+	return ""
+}
+
+func getMediaTypeForImp(impID string, imps []openrtb.Imp) (openrtb_ext.BidType, error) {
+	mediaType := openrtb_ext.BidTypeBanner
+	for _, imp := range imps {
+		if imp.ID == impID {
+			if imp.Banner == nil && imp.Video != nil {
+				mediaType = openrtb_ext.BidTypeVideo
+			}
+			return mediaType, nil
+		}
+	}
+
+	// This shouldnt happen. Lets handle it just incase by returning an error.
+	return "", &errortypes.BadInput{
+		Message: fmt.Sprintf("Failed to find impression \"%s\" ", impID),
+	}
+}
+
+func videoImpInsertion(bid *openrtb.Bid) string {
+	adm := bid.AdM
+	nurl := bid.NURL
+	search := "</Impression>"
+	imp := "</Impression><Impression><![CDATA[%s]]></Impression>"
+	wrapped_nurl := fmt.Sprintf(imp, nurl)
+	results := strings.Replace(adm, search, wrapped_nurl, 1)
+	return results
+}
+
+func isDmxParams(t interface{}) bool {
+	switch t.(type) {
+	case dmxParams:
+		return true
+	default:
+		return false
+	}
+}
diff --git a/adapters/dmx/usersync.go b/adapters/dmx/usersync.go
new file mode 100644
index 00000000..98e56234
--- /dev/null
+++ b/adapters/dmx/usersync.go
@@ -0,0 +1,12 @@
+package dmx
+
+import (
+	"text/template"
+
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/usersync"
+)
+
+func NewDmxSyncer(temp *template.Template) usersync.Usersyncer {
+	return adapters.NewSyncer("dmx", 144, temp, adapters.SyncTypeRedirect)
+}
diff --git a/adapters/eplanning/eplanning.go b/adapters/eplanning/eplanning.go
index 5fb9ccf2..2a46b546 100644
--- a/adapters/eplanning/eplanning.go
+++ b/adapters/eplanning/eplanning.go
@@ -133,7 +133,6 @@ func (adapter *EPlanningAdapter) MakeRequests(request *openrtb.BidRequest, reqIn
 
 	uriObj.Path = uriObj.Path + fmt.Sprintf("/%s/%s/%s/%s", clientID, dfpClientID, requestTarget, sec)
 	query := url.Values{}
-	query.Set("r", "pbs")
 	query.Set("ncb", "1")
 	if request.App == nil {
 		query.Set("ur", pageURL)
diff --git a/adapters/telaria/telaria.go b/adapters/telaria/telaria.go
index 9edafa86..294d0d10 100644
--- a/adapters/telaria/telaria.go
+++ b/adapters/telaria/telaria.go
@@ -1,8 +1,6 @@
 package telaria
 
 import (
-	"bytes"
-	"compress/gzip"
 	"encoding/json"
 	"fmt"
 	"github.com/mxmCherry/openrtb"
@@ -79,7 +77,6 @@ func GetHeaders(request *openrtb.BidRequest) *http.Header {
 	headers.Add("Content-Type", "application/json;charset=utf-8")
 	headers.Add("Accept", "application/json")
 	headers.Add("X-Openrtb-Version", "2.5")
-	headers.Add("Accept-Encoding", "gzip")
 
 	if request.Device != nil {
 		if len(request.Device.UA) > 0 {
@@ -233,34 +230,6 @@ func (a *TelariaAdapter) MakeRequests(requestIn *openrtb.BidRequest, reqInfo *ad
 	}}, nil
 }
 
-// response isn't automatically decompressed. This method unzips the response if Content-Encoding is gzip
-func GetResponseBody(response *adapters.ResponseData) ([]byte, error) {
-
-	if "gzip" == response.Headers.Get("Content-Encoding") {
-		body := bytes.NewBuffer(response.Body)
-		r, readerErr := gzip.NewReader(body)
-		if readerErr != nil {
-			return nil, &errortypes.BadServerResponse{
-				Message: fmt.Sprintf("Error while trying to unzip data [ %d ]", response.StatusCode),
-			}
-		}
-		var resB bytes.Buffer
-		var err error
-		_, err = resB.ReadFrom(r)
-		if err != nil {
-			return nil, &errortypes.BadServerResponse{
-				Message: fmt.Sprintf("Error while trying to unzip data [ %d ]", response.StatusCode),
-			}
-		}
-
-		response.Headers.Del("Content-Encoding")
-
-		return resB.Bytes(), nil
-	} else {
-		return response.Body, nil
-	}
-}
-
 func (a *TelariaAdapter) CheckResponseStatusCodes(response *adapters.ResponseData) error {
 	if response.StatusCode == http.StatusNoContent {
 		return &errortypes.BadInput{Message: "Telaria: Invalid Bid Request received by the server"}
@@ -294,11 +263,7 @@ func (a *TelariaAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalR
 		return nil, []error{httpStatusError}
 	}
 
-	responseBody, err := GetResponseBody(response)
-
-	if err != nil {
-		return nil, []error{err}
-	}
+	responseBody := response.Body
 
 	var bidResp openrtb.BidResponse
 	if err := json.Unmarshal(responseBody, &bidResp); err != nil {
diff --git a/adapters/yieldlab/const.go b/adapters/yieldlab/const.go
new file mode 100644
index 00000000..cb66cb6d
--- /dev/null
+++ b/adapters/yieldlab/const.go
@@ -0,0 +1,7 @@
+package yieldlab
+
+const adSlotIdSeparator = ","
+const adsizeSeparator = "x"
+const adSourceBanner = "<script src=\"%v\"></script>"
+const adSourceURL = "https://ad.yieldlab.net/d/%v/%v/%v?%v"
+const creativeID = "%v%v%v"
diff --git a/adapters/yieldlab/types.go b/adapters/yieldlab/types.go
new file mode 100644
index 00000000..90612700
--- /dev/null
+++ b/adapters/yieldlab/types.go
@@ -0,0 +1,29 @@
+package yieldlab
+
+import (
+	"strconv"
+	"time"
+)
+
+type bidResponse struct {
+	ID         uint64 `json:"id"`
+	Price      uint   `json:"price"`
+	Advertiser string `json:"advertiser"`
+	Adsize     string `json:"adsize"`
+	Pid        uint64 `json:"pid"`
+	Did        uint64 `json:"did"`
+	Pvid       string `json:"pvid"`
+}
+
+type cacheBuster func() string
+
+type weekGenerator func() string
+
+var defaultCacheBuster cacheBuster = func() string {
+	return strconv.FormatInt(time.Now().Unix(), 10)
+}
+
+var defaultWeekGenerator weekGenerator = func() string {
+	_, week := time.Now().ISOWeek()
+	return strconv.Itoa(week)
+}
diff --git a/adapters/yieldlab/usersync.go b/adapters/yieldlab/usersync.go
new file mode 100644
index 00000000..3ee9a3fd
--- /dev/null
+++ b/adapters/yieldlab/usersync.go
@@ -0,0 +1,12 @@
+package yieldlab
+
+import (
+	"text/template"
+
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/usersync"
+)
+
+func NewYieldlabSyncer(temp *template.Template) usersync.Usersyncer {
+	return adapters.NewSyncer("yieldlab", 70, temp, adapters.SyncTypeRedirect)
+}
diff --git a/adapters/yieldlab/yieldlab.go b/adapters/yieldlab/yieldlab.go
new file mode 100644
index 00000000..20f36747
--- /dev/null
+++ b/adapters/yieldlab/yieldlab.go
@@ -0,0 +1,314 @@
+package yieldlab
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/url"
+	"path"
+	"strconv"
+	"strings"
+
+	"github.com/mxmCherry/openrtb"
+	"golang.org/x/text/currency"
+
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// YieldlabAdapter connects the Yieldlab API to prebid server
+type YieldlabAdapter struct {
+	endpoint    string
+	cacheBuster cacheBuster
+	getWeek     weekGenerator
+}
+
+// NewYieldlabBidder returns a new YieldlabBidder instance
+func NewYieldlabBidder(endpoint string) *YieldlabAdapter {
+	return &YieldlabAdapter{
+		endpoint:    endpoint,
+		cacheBuster: defaultCacheBuster,
+		getWeek:     defaultWeekGenerator,
+	}
+}
+
+// Builds endpoint url based on adapter-specific pub settings from imp.ext
+func (a *YieldlabAdapter) makeEndpointURL(req *openrtb.BidRequest, params *openrtb_ext.ExtImpYieldlab) (string, error) {
+	uri, err := url.Parse(a.endpoint)
+	if err != nil {
+		return "", fmt.Errorf("failed to parse yieldlab endpoint: %v", err)
+	}
+
+	uri.Path = path.Join(uri.Path, params.AdslotID)
+	q := uri.Query()
+	q.Set("content", "json")
+	q.Set("pvid", "true")
+	q.Set("ts", a.cacheBuster())
+	q.Set("t", a.makeTargetingValues(params))
+
+	if req.User != nil && req.User.BuyerUID != "" {
+		q.Set("ids", "ylid:"+req.User.BuyerUID)
+	}
+
+	if req.Device != nil {
+		q.Set("yl_rtb_ifa", req.Device.IFA)
+		q.Set("yl_rtb_devicetype", fmt.Sprintf("%v", req.Device.DeviceType))
+
+		if req.Device.ConnectionType != nil {
+			q.Set("yl_rtb_connectiontype", fmt.Sprintf("%v", req.Device.ConnectionType.Val()))
+		}
+
+		if req.Device.Geo != nil {
+			q.Set("lat", fmt.Sprintf("%v", req.Device.Geo.Lat))
+			q.Set("lon", fmt.Sprintf("%v", req.Device.Geo.Lon))
+		}
+	}
+
+	if req.App != nil {
+		q.Set("pubappname", req.App.Name)
+		q.Set("pubbundlename", req.App.Bundle)
+	}
+
+	gdpr, consent, err := a.getGDPR(req)
+	if err != nil {
+		return "", err
+	}
+	if gdpr != "" && consent != "" {
+		q.Set("gdpr", gdpr)
+		q.Set("consent", consent)
+	}
+
+	uri.RawQuery = q.Encode()
+
+	return uri.String(), nil
+}
+
+func (a *YieldlabAdapter) getGDPR(request *openrtb.BidRequest) (string, string, error) {
+	gdpr := ""
+	var extRegs openrtb_ext.ExtRegs
+	if request.Regs != nil {
+		if err := json.Unmarshal(request.Regs.Ext, &extRegs); err != nil {
+			return "", "", fmt.Errorf("failed to parse ExtRegs in Yieldlab GDPR check: %v", err)
+		}
+		if extRegs.GDPR != nil && (*extRegs.GDPR == 0 || *extRegs.GDPR == 1) {
+			gdpr = strconv.Itoa(int(*extRegs.GDPR))
+		}
+	}
+
+	consent := ""
+	if request.User != nil && request.User.Ext != nil {
+		var extUser openrtb_ext.ExtUser
+		if err := json.Unmarshal(request.User.Ext, &extUser); err != nil {
+			return "", "", fmt.Errorf("failed to parse ExtUser in Yieldlab GDPR check: %v", err)
+		}
+		consent = extUser.Consent
+	}
+
+	return gdpr, consent, nil
+}
+
+func (a *YieldlabAdapter) makeTargetingValues(params *openrtb_ext.ExtImpYieldlab) string {
+	values := url.Values{}
+	for k, v := range params.Targeting {
+		values.Set(k, v)
+	}
+	return values.Encode()
+}
+
+func (a *YieldlabAdapter) MakeRequests(request *openrtb.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	if len(request.Imp) == 0 {
+		return nil, []error{fmt.Errorf("invalid request %+v, no Impressions given", request)}
+	}
+
+	bidURL, err := a.makeEndpointURL(request, a.mergeParams(a.parseRequest(request)))
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	headers := http.Header{}
+	headers.Add("Accept", "application/json")
+	if request.Site != nil {
+		headers.Add("Referer", request.Site.Page)
+	}
+	if request.Device != nil {
+		headers.Add("User-Agent", request.Device.UA)
+		headers.Add("X-Forwarded-For", request.Device.IP)
+	}
+	if request.User != nil {
+		headers.Add("Cookie", "id="+request.User.BuyerUID)
+	}
+
+	return []*adapters.RequestData{{
+		Method:  "GET",
+		Uri:     bidURL,
+		Headers: headers,
+	}}, nil
+}
+
+// parseRequest extracts the Yieldlab request information from the request
+func (a *YieldlabAdapter) parseRequest(request *openrtb.BidRequest) []*openrtb_ext.ExtImpYieldlab {
+	params := make([]*openrtb_ext.ExtImpYieldlab, 0)
+
+	for i := 0; i < len(request.Imp); i++ {
+		bidderExt := new(adapters.ExtImpBidder)
+		if err := json.Unmarshal(request.Imp[i].Ext, bidderExt); err != nil {
+			continue
+		}
+
+		yieldlabExt := new(openrtb_ext.ExtImpYieldlab)
+		if err := json.Unmarshal(bidderExt.Bidder, yieldlabExt); err != nil {
+			continue
+		}
+
+		params = append(params, yieldlabExt)
+	}
+
+	return params
+}
+
+func (a *YieldlabAdapter) mergeParams(params []*openrtb_ext.ExtImpYieldlab) *openrtb_ext.ExtImpYieldlab {
+	var adSlotIds []string
+	targeting := make(map[string]string)
+
+	for _, p := range params {
+		adSlotIds = append(adSlotIds, p.AdslotID)
+		for k, v := range p.Targeting {
+			targeting[k] = v
+		}
+	}
+
+	return &openrtb_ext.ExtImpYieldlab{
+		AdslotID:  strings.Join(adSlotIds, adSlotIdSeparator),
+		Targeting: targeting,
+	}
+}
+
+// MakeBids make the bids for the bid response.
+func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if response.StatusCode != 200 {
+		return nil, []error{
+			&errortypes.BadServerResponse{
+				Message: fmt.Sprintf("failed to resolve bids from yieldlab response: Unexpected response code %v", response.StatusCode),
+			},
+		}
+	}
+
+	bids := make([]*bidResponse, 0)
+	if err := json.Unmarshal(response.Body, &bids); err != nil {
+		return nil, []error{
+			&errortypes.BadServerResponse{
+				Message: fmt.Sprintf("failed to parse bids response from yieldlab: %v", err),
+			},
+		}
+	}
+
+	params := a.parseRequest(internalRequest)
+
+	bidderResponse := &adapters.BidderResponse{
+		Currency: currency.EUR.String(),
+		Bids:     []*adapters.TypedBid{},
+	}
+
+	for i, bid := range bids {
+		width, height, err := splitSize(bid.Adsize)
+		if err != nil {
+			return nil, []error{err}
+		}
+
+		req := a.findBidReq(bid.ID, params)
+		if req == nil {
+			return nil, []error{
+				fmt.Errorf("failed to find yieldlab request for adslotID %v. This is most likely a programming issue", bid.ID),
+			}
+		}
+
+		var bidType openrtb_ext.BidType
+		responseBid := &openrtb.Bid{
+			ID:     strconv.FormatUint(bid.ID, 10),
+			Price:  float64(bid.Price) / 100,
+			ImpID:  internalRequest.Imp[i].ID,
+			CrID:   a.makeCreativeID(req, bid),
+			DealID: strconv.FormatUint(bid.Pid, 10),
+			W:      width,
+			H:      height,
+		}
+
+		if internalRequest.Imp[i].Video != nil {
+			bidType = openrtb_ext.BidTypeVideo
+			responseBid.NURL = a.makeAdSourceURL(internalRequest, req, bid)
+
+		} else if internalRequest.Imp[i].Banner != nil {
+			bidType = openrtb_ext.BidTypeBanner
+			responseBid.AdM = a.makeBannerAdSource(internalRequest, req, bid)
+		} else {
+			// Yieldlab adapter currently doesn't support Audio and Native ads
+			continue
+		}
+
+		bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
+			BidType: bidType,
+			Bid:     responseBid,
+		})
+	}
+
+	return bidderResponse, nil
+}
+
+func (a *YieldlabAdapter) findBidReq(adslotID uint64, params []*openrtb_ext.ExtImpYieldlab) *openrtb_ext.ExtImpYieldlab {
+	slotIdStr := strconv.FormatUint(adslotID, 10)
+	for _, p := range params {
+		if p.AdslotID == slotIdStr {
+			return p
+		}
+	}
+
+	return nil
+}
+
+func (a *YieldlabAdapter) makeBannerAdSource(req *openrtb.BidRequest, ext *openrtb_ext.ExtImpYieldlab, res *bidResponse) string {
+	return fmt.Sprintf(adSourceBanner, a.makeAdSourceURL(req, ext, res))
+}
+
+func (a *YieldlabAdapter) makeAdSourceURL(req *openrtb.BidRequest, ext *openrtb_ext.ExtImpYieldlab, res *bidResponse) string {
+	val := url.Values{}
+	val.Set("ts", a.cacheBuster())
+	val.Set("id", ext.ExtId)
+	val.Set("pvid", res.Pvid)
+
+	if req.User != nil {
+		val.Set("ids", "ylid:"+req.User.BuyerUID)
+	}
+
+	gdpr, consent, err := a.getGDPR(req)
+	if err == nil && gdpr != "" && consent != "" {
+		val.Set("gdpr", gdpr)
+		val.Set("consent", consent)
+	}
+
+	return fmt.Sprintf(adSourceURL, ext.AdslotID, ext.SupplyID, res.Adsize, val.Encode())
+}
+
+func (a *YieldlabAdapter) makeCreativeID(req *openrtb_ext.ExtImpYieldlab, bid *bidResponse) string {
+	return fmt.Sprintf(creativeID, req.AdslotID, bid.Pid, a.getWeek())
+}
+
+func splitSize(size string) (uint64, uint64, error) {
+	sizeParts := strings.Split(size, adsizeSeparator)
+	if len(sizeParts) != 2 {
+		return 0, 0, nil
+	}
+
+	width, err := strconv.ParseUint(sizeParts[0], 10, 64)
+	if err != nil {
+		return 0, 0, fmt.Errorf("failed to parse yieldlab adsize: %v", err)
+	}
+
+	height, err := strconv.ParseUint(sizeParts[1], 10, 64)
+	if err != nil {
+		return 0, 0, fmt.Errorf("failed to parse yieldlab adsize: %v", err)
+	}
+
+	return width, height, nil
+
+}
diff --git a/config/config.go b/config/config.go
index 5c66f4cd..e93aed46 100755
--- a/config/config.go
+++ b/config/config.go
@@ -142,6 +142,7 @@ type GDPR struct {
 	Timeouts                GDPRTimeouts `mapstructure:"timeouts_ms"`
 	NonStandardPublishers   []string     `mapstructure:"non_standard_publishers,flow"`
 	NonStandardPublisherMap map[string]int
+	TCF2                    TCF2 `mapstructure:"tcf2"`
 	AMPException            bool `mapstructure:"amp_exception"`
 }
 
@@ -165,6 +166,26 @@ func (t *GDPRTimeouts) ActiveTimeout() time.Duration {
 	return time.Duration(t.ActiveVendorlistFetch) * time.Millisecond
 }
 
+// TCF2 defines the TCF2 specific configurations for GDPR
+type TCF2 struct {
+	Enabled             bool                 `mapstructure:"enabled"`
+	Purpose1            PurposeDetail        `mapstructure:"purpose1"`
+	Purpose2            PurposeDetail        `mapstructure:"purpose2"`
+	Purpose7            PurposeDetail        `mapstructure:"purpose7"`
+	SpecialPurpose1     PurposeDetail        `mapstructure:"special_purpose1"`
+	PurposeOneTreatment PurposeOneTreatement `mapstructure:"purpose_one_treatement"`
+}
+
+// Making a purpose struct so purpose specific details can be added later.
+type PurposeDetail struct {
+	Enabled bool `mapstructure:"enabled"`
+}
+
+type PurposeOneTreatement struct {
+	Enabled       bool `mapstructure:"enabled"`
+	AccessAllowed bool `mapstructure:"access_allowed"`
+}
+
 type CCPA struct {
 	Enforce bool `mapstructure:"enforce"`
 }
@@ -500,7 +521,7 @@ func (cfg *Configuration) setDerivedDefaults() {
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdkernel, "https://sync.adkernel.com/user-sync?t=image&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadkernel%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BUID%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdkernelAdn, "https://tag.adkernel.com/syncr?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3DadkernelAdn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BUID%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdpone, "https://usersync.adpone.com/csync?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadpone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D")
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdtelligent, "https://sync.adtelligent.com/csync?t=p&ep=0&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdtelligent, "https://sync.adtelligent.com/csync?t=p&ep=0&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdmixer, "https://inv-nets.admixer.net/adxcm.aspx?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadmixer%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24")
 	// openrtb_ext.BidderAdOcean doesn't have a good default.
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdvangelists, "https://nep.advangelists.com/xp/user-sync?acctid={aid}&&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadvangelists%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
@@ -513,6 +534,7 @@ func (cfg *Configuration) setDerivedDefaults() {
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/match/bounce/current?version=1&networkId=72582&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderCpmstar, "https://server.cpmstar.com/usersync.aspx?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDatablocks, "https://sync.v5prebid.datablocks.net/s2ssync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDmx, "https://dmx.districtm.io/s/v1/img/s/10007?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D%24%7Bgdpr%7D%26gdpr_consent%3D%24%7Bgdpr_consent%7D%26uid%3D%24%7Buid%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEmxDigital, "https://cs.emxdgt.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Demx_digital%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEngageBDR, "https://match.bnmla.com/usersync/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dengagebdr%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEPlanning, "https://ads.us.e-planning.net/uspd/1/?du="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Deplanning%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
@@ -551,6 +573,7 @@ func (cfg *Configuration) setDerivedDefaults() {
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderValueImpression, "https://rtb.valueimpression.com/usersync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvalueimpression%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderVisx, "https://t.visx.net/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D")
 	// openrtb_ext.BidderVrtcal doesn't have a good default.
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldlab, "https://ad.yieldlab.net/mr?t=2&pid=9140838&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldlab%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25%25YL_UID%25%25")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldmo, "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldmo%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldone, "https://y.one.impact-ad.jp/hbs_sc?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderZeroClickFraud, "https://s.0cf.io/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dzeroclickfraud%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D")
@@ -689,9 +712,10 @@ func SetupViper(v *viper.Viper, filename string) {
 	// for them and specify all the parameters they need for them to work correctly.
 	v.SetDefault("adapters.audiencenetwork.disabled", true)
 	v.SetDefault("adapters.rubicon.disabled", true)
-
 	v.SetDefault("adapters.33across.endpoint", "http://ssc.33across.com/api/v1/hb")
 	v.SetDefault("adapters.33across.partner_id", "")
+	v.SetDefault("adapters.dmx.endpoint", "https://dmx.districtm.io/b/v2")
+	v.SetDefault("adapters.adtelligent.endpoint", "http://hb.adtelligent.com/auction")
 	v.SetDefault("adapters.adform.endpoint", "http://adx.adform.net/adx")
 	v.SetDefault("adapters.adgeneration.endpoint", "https://d.socdm.com/adsv/v1")
 	v.SetDefault("adapters.adhese.endpoint", "https://ads-{{.AccountID}}.adhese.com/json")
@@ -701,7 +725,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.adocean.endpoint", "https://{{.Host}}")
 	v.SetDefault("adapters.adoppler.endpoint", "http://app.trustedmarketplace.io/ads")
 	v.SetDefault("adapters.adpone.endpoint", "http://rtb.adpone.com/bid-request?src=prebid_server")
-	v.SetDefault("adapters.adtelligent.endpoint", "http://hb.adtelligent.com/auction")
+	v.SetDefault("adapters.adtelligent.endpoint", "http://ghb.adtelligent.com/pbs/ortb")
 	v.SetDefault("adapters.advangelists.endpoint", "http://nep.advangelists.com/xp/get?pubid={{.PublisherID}}")
 	v.SetDefault("adapters.aja.endpoint", "https://ad.as.amanad.adtdp.com/v1/bid/4")
 	v.SetDefault("adapters.applogy.endpoint", "http://rtb.applogy.com/v1/prebid")
@@ -717,7 +741,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}")
 	v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com")
 	v.SetDefault("adapters.engagebdr.endpoint", "http://dsp.bnmla.com/hb")
-	v.SetDefault("adapters.eplanning.endpoint", "https://ads.us.e-planning.net/hb/1")
+	v.SetDefault("adapters.eplanning.endpoint", "http://rtb.e-planning.net/pbs/1")
 	v.SetDefault("adapters.gamma.endpoint", "https://hb.gammaplatform.com/adx/request/")
 	v.SetDefault("adapters.gamoshi.endpoint", "https://rtb.gamoshi.io")
 	v.SetDefault("adapters.grid.endpoint", "http://grid.bidswitch.net/sp_bid?sp=prebid")
@@ -760,6 +784,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.visx.endpoint", "https://t.visx.net/s2s_bid?wrapperType=s2s_prebid_standard")
 	v.SetDefault("adapters.vrtcal.endpoint", "http://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804")
 	v.SetDefault("adapters.yeahmobi.endpoint", "https://{{.Host}}/prebid/bid")
+	v.SetDefault("adapters.yieldlab.endpoint", "https://ad.yieldlab.net/yp/")
 	v.SetDefault("adapters.yieldmo.endpoint", "https://ads.yieldmo.com/exchange/prebid-server")
 	v.SetDefault("adapters.yieldone.endpoint", "https://y.one.impact-ad.jp/hbs_imp")
 	v.SetDefault("adapters.zeroclickfraud.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}")
@@ -772,6 +797,14 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("gdpr.timeouts_ms.init_vendorlist_fetches", 0)
 	v.SetDefault("gdpr.timeouts_ms.active_vendorlist_fetch", 0)
 	v.SetDefault("gdpr.non_standard_publishers", []string{""})
+	v.SetDefault("gdpr.tcf2.enabled", true)
+	v.SetDefault("gdpr.tcf2.purpose1.enabled", true)
+	v.SetDefault("gdpr.tcf2.purpose2.enabled", true)
+	v.SetDefault("gdpr.tcf2.purpose4.enabled", true)
+	v.SetDefault("gdpr.tcf2.purpose7.enabled", true)
+	v.SetDefault("gdpr.tcf2.special_purpose1.enabled", true)
+	v.SetDefault("gdpr.tcf2.purpose_one_treatement.enabled", true)
+	v.SetDefault("gdpr.tcf2.purpose_one_treatement.access_allowed", true)
 	v.SetDefault("gdpr.amp_exception", false)
 	v.SetDefault("ccpa.enforce", false)
 	v.SetDefault("currency_converter.fetch_url", "https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json")
diff --git a/docs/bidders/beachfront.md b/docs/bidders/beachfront.md
index bb51db41..ecd7a8f9 100644
--- a/docs/bidders/beachfront.md
+++ b/docs/bidders/beachfront.md
@@ -1,7 +1,13 @@
 # Beachfront bidder
 
-To use the beachfront bidder you will need an appId from an exchange 
-account on [https://platform.beachfront.io](platform.beachfront.io).
+To use the beachfront bidder you will need an appId (Exchange Id) from an exchange 
+account on [platform.beachfront.io](https://platform.beachfront.io).
 
 For further information, please contact adops@beachfront.com.
 
+As seen in the JSON response from \{your PBS server\}\/bidder\/params [(example)](https://prebid.adnxs.com/pbs/v1/bidders/params), the beachfront bidder can take either an "appId" parameter, or an "appIds" parameter. If the request is for one media type, the appId parameter should be used with the value of the Exchange Id on the Beachfront platform.
+
+The appIds parameter is for requesting a mix of banner and video. It has two parameters, "banner", and "video" for the appIds of two appropriately configured exchanges on the platform. The appIds parameter can be sent with just one of its two parameters and it will behave like the appId parameter.
+
+If the request includes an appId configured for a video response, the videoResponseType parameter can be defined as "nurl", "adm" or "both". These will apply to all video returned. If it is not defined, the response type will be a nurl. The definitions for "nurl" vs. "adm" are here: (https://github.com/mxmCherry/openrtb/blob/master/openrtb2/bid.go).
+
diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go
index b91f01a7..39001611 100755
--- a/exchange/adapter_map.go
+++ b/exchange/adapter_map.go
@@ -29,6 +29,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/conversant"
 	"github.com/prebid/prebid-server/adapters/cpmstar"
 	"github.com/prebid/prebid-server/adapters/datablocks"
+	"github.com/prebid/prebid-server/adapters/dmx"
 	"github.com/prebid/prebid-server/adapters/emx_digital"
 	"github.com/prebid/prebid-server/adapters/engagebdr"
 	"github.com/prebid/prebid-server/adapters/eplanning"
@@ -73,6 +74,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/visx"
 	"github.com/prebid/prebid-server/adapters/vrtcal"
 	"github.com/prebid/prebid-server/adapters/yeahmobi"
+	"github.com/prebid/prebid-server/adapters/yieldlab"
 	"github.com/prebid/prebid-server/adapters/yieldmo"
 	"github.com/prebid/prebid-server/adapters/yieldone"
 	"github.com/prebid/prebid-server/adapters/zeroclickfraud"
@@ -106,6 +108,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter
 		openrtb_ext.BidderConsumable:   consumable.NewConsumableBidder(cfg.Adapters[string(openrtb_ext.BidderConsumable)].Endpoint),
 		openrtb_ext.BidderCpmstar:      cpmstar.NewCpmstarBidder(cfg.Adapters[string(openrtb_ext.BidderCpmstar)].Endpoint),
 		openrtb_ext.BidderDatablocks:   datablocks.NewDatablocksBidder(cfg.Adapters[string(openrtb_ext.BidderDatablocks)].Endpoint),
+		openrtb_ext.BidderDmx:          dmx.NewDmxBidder(cfg.Adapters[string(openrtb_ext.BidderDmx)].Endpoint),
 		openrtb_ext.BidderEmxDigital:   emx_digital.NewEmxDigitalBidder(cfg.Adapters[string(openrtb_ext.BidderEmxDigital)].Endpoint),
 		openrtb_ext.BidderEngageBDR:    engagebdr.NewEngageBDRBidder(client, cfg.Adapters[string(openrtb_ext.BidderEngageBDR)].Endpoint),
 		openrtb_ext.BidderEPlanning:    eplanning.NewEPlanningBidder(client, cfg.Adapters[string(openrtb_ext.BidderEPlanning)].Endpoint),
@@ -153,6 +156,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter
 		openrtb_ext.BidderUcfunnel:         ucfunnel.NewUcfunnelBidder(cfg.Adapters[string(openrtb_ext.BidderUcfunnel)].Endpoint),
 		openrtb_ext.BidderUnruly:           unruly.NewUnrulyBidder(client, cfg.Adapters[string(openrtb_ext.BidderUnruly)].Endpoint),
 		openrtb_ext.BidderValueImpression:  valueimpression.NewValueImpressionBidder(cfg.Adapters[string(openrtb_ext.BidderValueImpression)].Endpoint),
+		openrtb_ext.BidderYieldlab:         yieldlab.NewYieldlabBidder(cfg.Adapters[string(openrtb_ext.BidderYieldlab)].Endpoint),
 		openrtb_ext.BidderVerizonMedia:     verizonmedia.NewVerizonMediaBidder(client, cfg.Adapters[string(openrtb_ext.BidderVerizonMedia)].Endpoint),
 		openrtb_ext.BidderVisx:             visx.NewVisxBidder(cfg.Adapters[string(openrtb_ext.BidderVisx)].Endpoint),
 		openrtb_ext.BidderVrtcal:           vrtcal.NewVrtcalBidder(cfg.Adapters[string(openrtb_ext.BidderVrtcal)].Endpoint),
diff --git a/exchange/utils.go b/exchange/utils.go
index d961089c..f602d1e8 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -60,10 +60,12 @@ func cleanOpenRTBRequests(ctx context.Context,
 			coreBidder := resolveBidder(bidder.String(), aliases)
 
 			var publisherID = labels.PubID
-			ok, err := gDPR.PersonalInfoAllowed(ctx, coreBidder, publisherID, consent)
+			ok, geo, err := gDPR.PersonalInfoAllowed(ctx, coreBidder, publisherID, consent)
 			privacyEnforcement.GDPR = !ok && err == nil
+			privacyEnforcement.GDPRGeo = !geo && err == nil
 		} else {
 			privacyEnforcement.GDPR = false
+			privacyEnforcement.GDPRGeo = false
 		}
 
 		privacyEnforcement.Apply(bidReq, ampGDPRException)
diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go
index 9390d942..0dfa12f5 100644
--- a/gdpr/gdpr.go
+++ b/gdpr/gdpr.go
@@ -23,7 +23,7 @@ type Permissions interface {
 	// Determines whether or not to send PI information to a bidder, or mask it out.
 	//
 	// If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent.
-	PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error)
+	PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error)
 
 	// Exposes the AMP execption flag
 	AMPException() bool
diff --git a/gdpr/impl.go b/gdpr/impl.go
index 8743d7f2..60db804a 100644
--- a/gdpr/impl.go
+++ b/gdpr/impl.go
@@ -2,11 +2,13 @@ package gdpr
 
 import (
 	"context"
+	"fmt"
 
 	"github.com/prebid/go-gdpr/api"
 	tcf1constants "github.com/prebid/go-gdpr/consentconstants"
 	consentconstants "github.com/prebid/go-gdpr/consentconstants/tcf2"
 	"github.com/prebid/go-gdpr/vendorconsent"
+	tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2"
 	"github.com/prebid/go-gdpr/vendorlist"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
@@ -40,10 +42,10 @@ func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	return false, nil
 }
 
-func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) {
+func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) {
 	_, ok := p.cfg.NonStandardPublisherMap[PublisherID]
 	if ok {
-		return true, nil
+		return true, true, nil
 	}
 
 	id, ok := p.vendorIDs[bidder]
@@ -52,10 +54,10 @@ func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrt
 	}
 
 	if consent == "" {
-		return p.cfg.UsersyncIfAmbiguous, nil
+		return p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, nil
 	}
 
-	return false, nil
+	return false, false, nil
 }
 
 func (p *permissionsImpl) AMPException() bool {
@@ -78,38 +80,104 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, consen
 	}
 
 	// InfoStorageAccess is the same across TCF 1 and TCF 2
+	if parsedConsent.Version() == 2 {
+		if !p.cfg.TCF2.Purpose1.Enabled {
+			// We are not enforcing purpose 1
+			return true, nil
+		}
+		consent, ok := parsedConsent.(tcf2.ConsentMetadata)
+		if !ok {
+			err := fmt.Errorf("Unable to access TCF2 parsed consent")
+			return false, err
+		}
+		return p.checkPurpose(consent, vendor, vendorID, consentconstants.InfoStorageAccess), nil
+	}
 	if vendor.Purpose(consentconstants.InfoStorageAccess) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && parsedConsent.VendorConsent(vendorID) {
 		return true, nil
 	}
 	return false, nil
 }
 
-func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent string) (bool, error) {
+func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent string) (bool, bool, error) {
 	// If we're not given a consent string, respect the preferences in the app config.
 	if consent == "" {
-		return p.cfg.UsersyncIfAmbiguous, nil
+		return p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, nil
 	}
 
 	parsedConsent, vendor, err := p.parseVendor(ctx, vendorID, consent)
 	if err != nil {
-		return false, err
+		return false, false, err
 	}
 
 	if vendor == nil {
-		return false, nil
+		return false, false, nil
 	}
 
 	if parsedConsent.Version() == 2 {
-		// Need to add the location special purpose once the library supports it.
+		if p.cfg.TCF2.Enabled {
+			return p.allowPITCF2(parsedConsent, vendor, vendorID)
+		}
 		if (vendor.Purpose(consentconstants.InfoStorageAccess) || vendor.LegitimateInterest(consentconstants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && (vendor.Purpose(consentconstants.PersonalizationProfile) || vendor.LegitimateInterest(consentconstants.PersonalizationProfile)) && parsedConsent.PurposeAllowed(consentconstants.PersonalizationProfile) && parsedConsent.VendorConsent(vendorID) {
-			return true, nil
+			return true, true, nil
 		}
 	} else {
 		if (vendor.Purpose(tcf1constants.InfoStorageAccess) || vendor.LegitimateInterest(tcf1constants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(tcf1constants.InfoStorageAccess) && (vendor.Purpose(tcf1constants.AdSelectionDeliveryReporting) || vendor.LegitimateInterest(tcf1constants.AdSelectionDeliveryReporting)) && parsedConsent.PurposeAllowed(tcf1constants.AdSelectionDeliveryReporting) && parsedConsent.VendorConsent(vendorID) {
-			return true, nil
+			return true, true, nil
 		}
 	}
-	return false, nil
+	return false, false, nil
+}
+
+func (p *permissionsImpl) allowPITCF2(parsedConsent api.VendorConsents, vendor api.Vendor, vendorID uint16) (allowPI bool, allowGeo bool, err error) {
+	consent, ok := parsedConsent.(tcf2.ConsentMetadata)
+	err = nil
+	allowPI = false
+	allowGeo = false
+	if !ok {
+		err = fmt.Errorf("Unable to access TCF2 parsed consent")
+		return
+	}
+	if p.cfg.TCF2.SpecialPurpose1.Enabled {
+		allowGeo = consent.SpecialFeatureOptIn(1) && vendor.SpecialPurpose(1)
+	} else {
+		allowGeo = true
+	}
+	// Set to true so any purpose check can flip it to false
+	allowPI = true
+	if p.cfg.TCF2.Purpose1.Enabled {
+		allowPI = allowPI && p.checkPurpose(consent, vendor, vendorID, consentconstants.InfoStorageAccess)
+	}
+	if p.cfg.TCF2.Purpose2.Enabled {
+		allowPI = allowPI && p.checkPurpose(consent, vendor, vendorID, consentconstants.BasicAdserving)
+	}
+	if p.cfg.TCF2.Purpose7.Enabled {
+		allowPI = allowPI && p.checkPurpose(consent, vendor, vendorID, consentconstants.AdPerformance)
+	}
+	return
+}
+
+const pubRestrictNotAllowed = 0
+const pubRestrictRequireConsent = 1
+const pubRestrictRequireLegitInterest = 2
+
+func (p *permissionsImpl) checkPurpose(consent tcf2.ConsentMetadata, vendor api.Vendor, vendorID uint16, purpose tcf1constants.Purpose) bool {
+	if purpose == consentconstants.InfoStorageAccess && p.cfg.TCF2.PurposeOneTreatment.Enabled && consent.PurposeOneTreatment() {
+		return p.cfg.TCF2.PurposeOneTreatment.AccessAllowed
+	}
+	if consent.CheckPubRestriction(uint8(purpose), pubRestrictNotAllowed, vendorID) {
+		return false
+	}
+	if consent.CheckPubRestriction(uint8(purpose), pubRestrictRequireConsent, vendorID) {
+		return vendor.PurposeStrict(purpose) && consent.PurposeAllowed(purpose) && consent.VendorConsent(vendorID)
+	}
+	if consent.CheckPubRestriction(uint8(purpose), pubRestrictRequireLegitInterest, vendorID) {
+		// Need LITransparency here
+		return vendor.LegitimateInterestStrict(purpose) && consent.PurposeLITransparency(purpose) && consent.VendorLegitInterest(vendorID)
+	}
+	purposeAllowed := vendor.Purpose(purpose) && consent.PurposeAllowed(purpose) && consent.VendorConsent(vendorID)
+	legitInterest := vendor.LegitimateInterest(purpose) && consent.PurposeLITransparency(purpose) && consent.VendorLegitInterest(vendorID)
+
+	return purposeAllowed || legitInterest
 }
 
 func (p *permissionsImpl) parseVendor(ctx context.Context, vendorID uint16, consent string) (parsedConsent api.VendorConsents, vendor api.Vendor, err error) {
@@ -146,8 +214,8 @@ func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.B
 	return true, nil
 }
 
-func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) {
-	return true, nil
+func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) {
+	return true, true, nil
 }
 
 func (a AlwaysAllow) AMPException() bool {
diff --git a/go.mod b/go.mod
index 89cc69e4..0224057e 100644
--- a/go.mod
+++ b/go.mod
@@ -7,23 +7,17 @@ require (
 	github.com/DATA-DOG/go-sqlmock v1.3.0
 	github.com/NYTimes/gziphandler v1.1.1
 	github.com/OneOfOne/xxhash v1.2.5 // indirect
-	github.com/aerospike/aerospike-client-go v2.7.2+incompatible
 	github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf
 	github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect
 	github.com/blang/semver v3.5.1+incompatible
-	github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
 	github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
 	github.com/cespare/xxhash v1.0.0 // indirect
 	github.com/chasex/glog v0.0.0-20160217080310-c62392af379c
 	github.com/coocood/freecache v1.0.1
-	github.com/didip/tollbooth v4.0.2+incompatible
 	github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5
 	github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd
-	github.com/go-redis/redis v6.15.7+incompatible
-	github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5
 	github.com/gofrs/uuid v3.2.0+incompatible
 	github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
-	github.com/golang/snappy v0.0.1
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/influxdata/influxdb v1.6.1 // indirect
 	github.com/julienschmidt/httprouter v1.1.0
@@ -37,9 +31,8 @@ require (
 	github.com/mxmCherry/openrtb v11.0.0+incompatible
 	github.com/onsi/ginkgo v1.10.1 // indirect
 	github.com/onsi/gomega v1.7.0 // indirect
-	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
 	github.com/pelletier/go-toml v1.2.0 // indirect
-	github.com/prebid/go-gdpr v0.7.0
+	github.com/prebid/go-gdpr v0.8.2
 	github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf
 	github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed
 	github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910
@@ -48,27 +41,24 @@ require (
 	github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165
 	github.com/rs/cors v1.5.0
 	github.com/sergi/go-diff v1.0.0 // indirect
-	github.com/sirupsen/logrus v1.4.2
 	github.com/spaolacci/murmur3 v1.1.0 // indirect
 	github.com/spf13/afero v1.1.1 // indirect
 	github.com/spf13/cast v1.2.0 // indirect
 	github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834 // indirect
 	github.com/spf13/pflag v1.0.2 // indirect
 	github.com/spf13/viper v1.1.0
+	github.com/stretchr/objx v0.1.1 // indirect
 	github.com/stretchr/testify v1.5.1
-	github.com/valyala/fasthttp v1.9.0
 	github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303
 	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 	github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609
-	github.com/xorcare/pointer v1.1.0
 	github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d
 	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
 	github.com/yudai/pp v2.0.1+incompatible // indirect
-	github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb // indirect
 	golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
 	golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
+	golang.org/x/sys v0.0.0-20190422165155-953cdadca894 // indirect
 	golang.org/x/text v0.3.0
-	golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
 	gopkg.in/yaml.v2 v2.2.2
 )
diff --git a/go.sum b/go.sum
index f929408f..5d941b89 100644
--- a/go.sum
+++ b/go.sum
@@ -6,57 +6,35 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq
 github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
 github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI=
 github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
-github.com/aerospike/aerospike-client-go v2.7.2+incompatible h1:bWbRf8trg1FhKF7u43KLGNfOH60RlvIgQjpaS107DZ8=
-github.com/aerospike/aerospike-client-go v2.7.2+incompatible/go.mod h1:zj8LBEnWBDOVEIJt8LvaRvDG5ARAoa5dBeHaB472NRc=
 github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
 github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
-github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
 github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
 github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
-github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
-github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
-github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
-github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
 github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA=
 github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
 github.com/cespare/xxhash v1.0.0 h1:naDmySfoNg0nKS62/ujM6e71ZgM2AoVdaqGwMG0w18A=
 github.com/cespare/xxhash v1.0.0/go.mod h1:fX/lfQBkSCDXZSUgv6jVIu/EVA3/JNseAX5asI4c4T4=
 github.com/chasex/glog v0.0.0-20160217080310-c62392af379c h1:eXqCBUHfmjbeDqcuvzjsd+bM6A+bnwo5N9FVbV6m5/s=
 github.com/chasex/glog v0.0.0-20160217080310-c62392af379c/go.mod h1:omJZNg0Qu76bxJd+ExohVo8uXzNcGOk2bv7vel460xk=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/coocood/freecache v1.0.1 h1:oFyo4msX2c0QIKU+kuMJUwsKamJ+AKc2JJrKcMszJ5M=
 github.com/coocood/freecache v1.0.1/go.mod h1:ePwxCDzOYvARfHdr1pByNct1at3CoKnsipOHwKlNbzI=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/didip/tollbooth v4.0.2+incompatible h1:fVSa33JzSz0hoh2NxpwZtksAzAgd7zjmGO20HCZtF4M=
-github.com/didip/tollbooth v4.0.2+incompatible/go.mod h1:A9b0665CE6l1KmzpDws2++elm/CsuWBMa5Jv4WY0PEY=
 github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
 github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
 github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd h1:biTJQdqouE5by89AAffXG8++TY+9Fsdrg5rinbt3tHk=
 github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
-github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
-github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5 h1:ZZVxQRCm4ewuoqqLBJ7LHpsk4OGx2PkyCsRKLq4oHgE=
-github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=
 github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
 github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
-github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
@@ -67,17 +45,6 @@ github.com/julienschmidt/httprouter v1.1.0 h1:7wLdtIiIpzOkC9u6sXOozpBauPdskj3ru4
 github.com/julienschmidt/httprouter v1.1.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
-github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs=
-github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
-github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
@@ -99,14 +66,12 @@ github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
 github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
-github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prebid/go-gdpr v0.7.0 h1:m4E/FjUhTBMciDsd3lQlbzFyXLzNK+JQkFmInJpFAwc=
-github.com/prebid/go-gdpr v0.7.0/go.mod h1:FPY0uxSrl9/Mz237LnPo3ge4aCG0wQ9FWf2b4WhwNn0=
+github.com/prebid/go-gdpr v0.8.2 h1:mN2jKYZZpJkCYFQB/nDTJoPpuGYblOYP2UUzOzRggII=
+github.com/prebid/go-gdpr v0.8.2/go.mod h1:FPY0uxSrl9/Mz237LnPo3ge4aCG0wQ9FWf2b4WhwNn0=
 github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf h1:CcE+KN1tCtWKsUFH5IzdQxHIgP609VSIVe5Hywg2phs=
 github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf/go.mod h1:k5xrl5ZpnumN1S2x8w8cMiFYsgRuVyAeFJz+BkSi+98=
 github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed h1:0dloFFFNNDG7c+8qtkYw2FdADrWy9s5cI8wHp6tK3Mg=
@@ -123,8 +88,6 @@ github.com/rs/cors v1.5.0 h1:dgSHE6+ia18arGOTIYQKKGWLvEbGvmbNE6NfxhoNHUY=
 github.com/rs/cors v1.5.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
 github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.1 h1:Lt3ihYMlE+lreX1GS4Qw4ZsNpYQLxIXKBTEOXm3nt6I=
@@ -140,16 +103,10 @@ github.com/spf13/viper v1.1.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7Sr
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw=
-github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
-github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
 github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303 h1:Va10CytCCYRm4xBTses5ZDeDjeIQjhaiC9nRCe/yflI=
 github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303/go.mod h1:Xdcad1nGVhQfhoV0go+/4WaI/RZkWlvfjkVCdpMTxPY=
 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
@@ -158,16 +115,12 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
 github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609 h1:BcMExZAULPkihVZ7UJXK7t8rwGqisXFw75tILnafhBY=
 github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
-github.com/xorcare/pointer v1.1.0 h1:sFwXOhRF8QZ0tyVZrtxWGIoVZNEmRzBCaFWdONPQIUM=
-github.com/xorcare/pointer v1.1.0/go.mod h1:6KLhkOh6YbuvZkT4YbxIbR/wzLBjyMxOiNzZhJTor2Y=
 github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d h1:yJIizrfO599ot2kQ6Af1enICnwBD3XoxgX3MrMwot2M=
 github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
 github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
 github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
 github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
 github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
-github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
-github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -178,7 +131,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -186,14 +138,10 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/p
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
-gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index f2f8e7c6..b3ecddb0 100755
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -46,6 +46,7 @@ const (
 	BidderConversant       BidderName = "conversant"
 	BidderCpmstar          BidderName = "cpmstar"
 	BidderDatablocks       BidderName = "datablocks"
+	BidderDmx              BidderName = "dmx"
 	BidderEmxDigital       BidderName = "emx_digital"
 	BidderEngageBDR        BidderName = "engagebdr"
 	BidderEPlanning        BidderName = "eplanning"
@@ -91,6 +92,7 @@ const (
 	BidderVisx             BidderName = "visx"
 	BidderVrtcal           BidderName = "vrtcal"
 	BidderYeahmobi         BidderName = "yeahmobi"
+	BidderYieldlab         BidderName = "yieldlab"
 	BidderYieldmo          BidderName = "yieldmo"
 	BidderYieldone         BidderName = "yieldone"
 	BidderZeroClickFraud   BidderName = "zeroclickfraud"
@@ -121,6 +123,7 @@ var BidderMap = map[string]BidderName{
 	"conversant":        BidderConversant,
 	"cpmstar":           BidderCpmstar,
 	"datablocks":        BidderDatablocks,
+	"dmx":               BidderDmx,
 	"emx_digital":       BidderEmxDigital,
 	"engagebdr":         BidderEngageBDR,
 	"eplanning":         BidderEPlanning,
@@ -166,6 +169,7 @@ var BidderMap = map[string]BidderName{
 	"visx":              BidderVisx,
 	"vrtcal":            BidderVrtcal,
 	"yeahmobi":          BidderYeahmobi,
+	"yieldlab":          BidderYieldlab,
 	"yieldmo":           BidderYieldmo,
 	"yieldone":          BidderYieldone,
 	"zeroclickfraud":    BidderZeroClickFraud,
diff --git a/openrtb_ext/imp_yieldlab.go b/openrtb_ext/imp_yieldlab.go
new file mode 100644
index 00000000..604b7e8c
--- /dev/null
+++ b/openrtb_ext/imp_yieldlab.go
@@ -0,0 +1,10 @@
+package openrtb_ext
+
+// ExtImpYieldlab defines the contract for bidrequest.imp[i].ext.yieldlab
+type ExtImpYieldlab struct {
+	AdslotID  string            `json:"adslotId"`
+	SupplyID  string            `json:"supplyId"`
+	AdSize    string            `json:"adSize"`
+	Targeting map[string]string `json:"targeting"`
+	ExtId     string            `json:"extId"`
+}
diff --git a/privacy/enforcement.go b/privacy/enforcement.go
index 96d03ef4..d302192e 100644
--- a/privacy/enforcement.go
+++ b/privacy/enforcement.go
@@ -6,14 +6,15 @@ import (
 
 // Enforcement represents the privacy policies to enforce for an OpenRTB bid request.
 type Enforcement struct {
-	CCPA  bool
-	COPPA bool
-	GDPR  bool
+	CCPA    bool
+	COPPA   bool
+	GDPR    bool
+	GDPRGeo bool
 }
 
 // Any returns true if at least one privacy policy requires enforcement.
 func (e Enforcement) Any() bool {
-	return e.CCPA || e.COPPA || e.GDPR
+	return e.CCPA || e.COPPA || e.GDPR || e.GDPRGeo
 }
 
 // Apply cleans personally identifiable information from an OpenRTB bid request.
@@ -45,7 +46,7 @@ func (e Enforcement) getGeoScrubStrategy() ScrubStrategyGeo {
 		return ScrubStrategyGeoFull
 	}
 
-	if e.GDPR || e.CCPA {
+	if e.GDPRGeo || e.CCPA {
 		return ScrubStrategyGeoReducedPrecision
 	}
 
@@ -60,5 +61,11 @@ func (e Enforcement) getUserScrubStrategy(ampGDPRException bool) ScrubStrategyUs
 	if e.GDPR && ampGDPRException {
 		return ScrubStrategyUserNone
 	}
-	return ScrubStrategyUserID
+
+	// If no user scrubbing is needed, then return none, else scrub ID (COPPA checked above)
+	if e.CCPA || e.GDPR {
+		return ScrubStrategyUserID
+	}
+
+	return ScrubStrategyUserNone
 }
diff --git a/static/bidder-info/dmx.yaml b/static/bidder-info/dmx.yaml
new file mode 100644
index 00000000..d6e54178
--- /dev/null
+++ b/static/bidder-info/dmx.yaml
@@ -0,0 +1,11 @@
+maintainer:
+  email: "steve@districtm.net"
+capabilities:
+  site:
+    mediaTypes:
+      - banner
+      - video
+  app:
+    mediaTypes:
+      - banner
+      - video
diff --git a/static/bidder-info/yieldlab.yaml b/static/bidder-info/yieldlab.yaml
new file mode 100644
index 00000000..654e6c74
--- /dev/null
+++ b/static/bidder-info/yieldlab.yaml
@@ -0,0 +1,11 @@
+maintainer:
+  email: "solutions@yieldlab.de"
+capabilities:
+  site:
+    mediaTypes:
+      - banner
+      - video
+  app:
+    mediaTypes:
+      - banner
+      - video
diff --git a/static/bidder-params/dmx.json b/static/bidder-params/dmx.json
new file mode 100644
index 00000000..4c0df65e
--- /dev/null
+++ b/static/bidder-params/dmx.json
@@ -0,0 +1,22 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "District M DMX Adapter Params",
+  "description": "A schema which validates params accepted by the DMX adapter",
+  "type": "object",
+  "properties": {
+    "memberid" : {
+      "type": "string",
+      "description": "Represent boost MemberId from districtm UI"
+    },
+    "tagid": {
+      "type": "string",
+      "description": "Represent the placement ID, this value is optional"
+    },
+    "bidfloor": {
+      "type": "string",
+      "description": "The minimum price acceptable for a bid"
+    }
+  },
+
+  "required": ["memberid"]
+}
\ No newline at end of file
diff --git a/static/bidder-params/yieldlab.json b/static/bidder-params/yieldlab.json
new file mode 100644
index 00000000..900d65da
--- /dev/null
+++ b/static/bidder-params/yieldlab.json
@@ -0,0 +1,33 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Yieldlab Adapter Params",
+  "description": "A schema which validates params accepted by the Yieldlab adapter",
+  "type": "object",
+  "properties": {
+    "adslotId": {
+      "type": "string",
+      "description": "Yieldlab ID of the ad slot"
+    },
+    "supplyId": {
+      "type": "string",
+      "description": "Yieldlab ID of the supply"
+    },
+    "adSize": {
+      "type": "string",
+      "description": "Size of the adslot in pixel, e.g. 200x50"
+    },
+    "extId": {
+      "type": "string",
+      "description": "External ID used for reporting"
+    },
+    "targeting": {
+      "type": "object",
+      "description": "Targeting information in key value pairs"
+    }
+  },
+  "required": [
+    "adslotId",
+    "supplyId",
+    "adSize"
+  ]
+}
diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go
index 3f12ee7f..5dccf855 100755
--- a/usersync/usersyncers/syncer.go
+++ b/usersync/usersyncers/syncer.go
@@ -24,6 +24,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/conversant"
 	"github.com/prebid/prebid-server/adapters/cpmstar"
 	"github.com/prebid/prebid-server/adapters/datablocks"
+	"github.com/prebid/prebid-server/adapters/dmx"
 	"github.com/prebid/prebid-server/adapters/emx_digital"
 	"github.com/prebid/prebid-server/adapters/engagebdr"
 	"github.com/prebid/prebid-server/adapters/eplanning"
@@ -61,6 +62,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/verizonmedia"
 	"github.com/prebid/prebid-server/adapters/visx"
 	"github.com/prebid/prebid-server/adapters/vrtcal"
+	"github.com/prebid/prebid-server/adapters/yieldlab"
 	"github.com/prebid/prebid-server/adapters/yieldmo"
 	"github.com/prebid/prebid-server/adapters/yieldone"
 	"github.com/prebid/prebid-server/adapters/zeroclickfraud"
@@ -93,6 +95,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderConversant, conversant.NewConversantSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderCpmstar, cpmstar.NewCpmstarSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderDatablocks, datablocks.NewDatablocksSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderDmx, dmx.NewDmxSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderEmxDigital, emx_digital.NewEMXDigitalSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderEngageBDR, engagebdr.NewEngageBDRSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderEPlanning, eplanning.NewEPlanningSyncer)
@@ -131,6 +134,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderVerizonMedia, verizonmedia.NewVerizonMediaSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderVisx, visx.NewVisxSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderVrtcal, vrtcal.NewVrtcalSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldlab, yieldlab.NewYieldlabSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldmo, yieldmo.NewYieldmoSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldone, yieldone.NewYieldoneSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderZeroClickFraud, zeroclickfraud.NewZeroClickFraudSyncer)


Test patch:
diff --git a/adapters/adapterstest/test_json.go b/adapters/adapterstest/test_json.go
index 7602ab16..8fdb9c5d 100644
--- a/adapters/adapterstest/test_json.go
+++ b/adapters/adapterstest/test_json.go
@@ -208,7 +208,7 @@ func diffErrorLists(t *testing.T, description string, actual []error, expected [
 	t.Helper()
 
 	if len(expected) != len(actual) {
-		t.Fatalf("%s had wrong error count. Expected %d, got %d", description, len(expected), len(actual))
+		t.Fatalf("%s had wrong error count. Expected %d, got %d (%v)", description, len(expected), len(actual), actual)
 	}
 	for i := 0; i < len(actual); i++ {
 		if expected[i].Comparison == "literal" {
diff --git a/adapters/adtelligent/adtelligent_test.go b/adapters/adtelligent/adtelligent_test.go
index 63655da6..b8894c5e 100644
--- a/adapters/adtelligent/adtelligent_test.go
+++ b/adapters/adtelligent/adtelligent_test.go
@@ -7,5 +7,5 @@ import (
 )
 
 func TestJsonSamples(t *testing.T) {
-	adapterstest.RunJSONBidderTest(t, "adtelligenttest", NewAdtelligentBidder("http://hb.adtelligent.com/auction"))
+	adapterstest.RunJSONBidderTest(t, "adtelligenttest", NewAdtelligentBidder("http://ghb.adtelligent.com/pbs/ortb"))
 }
diff --git a/adapters/adtelligent/adtelligenttest/exemplary/media-type-mapping.json b/adapters/adtelligent/adtelligenttest/exemplary/media-type-mapping.json
index 67ad2fd2..553ec618 100644
--- a/adapters/adtelligent/adtelligenttest/exemplary/media-type-mapping.json
+++ b/adapters/adtelligent/adtelligenttest/exemplary/media-type-mapping.json
@@ -24,7 +24,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "http://hb.adtelligent.com/auction?aid=1000",
+        "uri": "http://ghb.adtelligent.com/pbs/ortb?aid=1000",
         "body": {
           "id": "test-request-id",
           "imp": [
diff --git a/adapters/adtelligent/adtelligenttest/exemplary/simple-banner.json b/adapters/adtelligent/adtelligenttest/exemplary/simple-banner.json
index 6648229d..a06477b4 100644
--- a/adapters/adtelligent/adtelligenttest/exemplary/simple-banner.json
+++ b/adapters/adtelligent/adtelligenttest/exemplary/simple-banner.json
@@ -30,7 +30,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "http://hb.adtelligent.com/auction?aid=1000",
+        "uri": "http://ghb.adtelligent.com/pbs/ortb?aid=1000",
         "body": {
           "id": "test-request-id",
           "imp": [
diff --git a/adapters/adtelligent/adtelligenttest/exemplary/simple-video.json b/adapters/adtelligent/adtelligenttest/exemplary/simple-video.json
index 97769651..f108cc94 100644
--- a/adapters/adtelligent/adtelligenttest/exemplary/simple-video.json
+++ b/adapters/adtelligent/adtelligenttest/exemplary/simple-video.json
@@ -24,7 +24,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "http://hb.adtelligent.com/auction?aid=1000",
+        "uri": "http://ghb.adtelligent.com/pbs/ortb?aid=1000",
         "body": {
           "id": "test-request-id",
           "imp": [
diff --git a/adapters/adtelligent/adtelligenttest/supplemental/explicit-dimensions.json b/adapters/adtelligent/adtelligenttest/supplemental/explicit-dimensions.json
index 9dc279bc..6155e9bc 100644
--- a/adapters/adtelligent/adtelligenttest/supplemental/explicit-dimensions.json
+++ b/adapters/adtelligent/adtelligenttest/supplemental/explicit-dimensions.json
@@ -25,7 +25,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "http://hb.adtelligent.com/auction?aid=1000",
+        "uri": "http://ghb.adtelligent.com/pbs/ortb?aid=1000",
         "body": {
           "id": "test-request-id",
           "imp": [
diff --git a/adapters/adtelligent/adtelligenttest/supplemental/wrong-impression-mapping.json b/adapters/adtelligent/adtelligenttest/supplemental/wrong-impression-mapping.json
index 94df34af..2e5aeff3 100644
--- a/adapters/adtelligent/adtelligenttest/supplemental/wrong-impression-mapping.json
+++ b/adapters/adtelligent/adtelligenttest/supplemental/wrong-impression-mapping.json
@@ -24,7 +24,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "http://hb.adtelligent.com/auction?aid=1000",
+        "uri": "http://ghb.adtelligent.com/pbs/ortb?aid=1000",
         "body": {
           "id": "test-request-id",
           "imp": [
diff --git a/adapters/beachfront/usersync_test.go b/adapters/beachfront/usersync_test.go
index f0c4b381..0267ac05 100644
--- a/adapters/beachfront/usersync_test.go
+++ b/adapters/beachfront/usersync_test.go
@@ -30,6 +30,6 @@ 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, 0, syncer.GDPRVendorID())
+	assert.EqualValues(t, uint16(335), syncer.GDPRVendorID())
 	assert.Equal(t, false, syncInfo.SupportCORS)
 }
diff --git a/adapters/dmx/dmx_test.go b/adapters/dmx/dmx_test.go
new file mode 100644
index 00000000..e9f195eb
--- /dev/null
+++ b/adapters/dmx/dmx_test.go
@@ -0,0 +1,782 @@
+package dmx
+
+import (
+	"encoding/json"
+	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/adapters"
+	"strings"
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+)
+
+var (
+	bidRequest string
+)
+
+func TestFetchParams(t *testing.T) {
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+	var arrImp []openrtb.Imp
+	var imps = fetchParams(
+		dmxExt{Bidder: dmxParams{
+			TagId:       "222",
+			PublisherId: "5555",
+		}},
+		openrtb.Imp{ID: "32"},
+		openrtb.Imp{ID: "32"},
+		arrImp,
+		&openrtb.Banner{W: &width, H: &height, Format: []openrtb.Format{
+			{W: 300, H: 250},
+		}},
+		nil,
+		1)
+	var imps2 = fetchParams(
+		dmxExt{Bidder: dmxParams{
+			DmxId:    "222",
+			MemberId: "5555",
+		}},
+		openrtb.Imp{ID: "32"},
+		openrtb.Imp{ID: "32"},
+		arrImp,
+		&openrtb.Banner{W: &width, H: &height, Format: []openrtb.Format{
+			{W: 300, H: 250},
+		}},
+		nil,
+		1)
+	if len(imps) == 0 {
+		t.Errorf("should increment the length by one")
+	}
+
+	if len(imps2) == 0 {
+		t.Errorf("should increment the length by one")
+	}
+
+}
+func TestJsonSamples(t *testing.T) {
+	adapterstest.RunJSONBidderTest(t, "dmxtest", new(DmxAdapter))
+}
+
+func TestMakeRequestsOtherPlacement(t *testing.T) {
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		User: &openrtb.User{ID: "bscakucbkasucbkasunscancasuin"},
+		Imp:  []openrtb.Imp{imp1},
+		Site: &openrtb.Site{
+			Publisher: &openrtb.Publisher{
+				ID: "10007",
+			},
+		},
+		ID: "1234",
+	}
+
+	actualAdapterRequests, err := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+
+	if actualAdapterRequests == nil {
+		t.Errorf("request should be nil")
+	}
+	if len(err) != 0 {
+		t.Errorf("We should have no error")
+	}
+
+}
+
+func TestMakeRequestsInvalid(t *testing.T) {
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		Imp: []openrtb.Imp{imp1},
+		Site: &openrtb.Site{
+			Publisher: &openrtb.Publisher{
+				ID: "10007",
+			},
+		},
+		ID: "1234",
+	}
+
+	actualAdapterRequests, err := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+
+	if len(actualAdapterRequests) != 0 {
+		t.Errorf("request should be nil")
+	}
+	if len(err) == 0 {
+		t.Errorf("We should have no error")
+	}
+
+}
+
+func TestMakeRequestNoSite(t *testing.T) {
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		Imp: []openrtb.Imp{imp1},
+		App: &openrtb.App{ID: "cansanuabnua", Publisher: &openrtb.Publisher{ID: "whatever"}},
+		ID:  "1234",
+	}
+
+	actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+
+	if len(actualAdapterRequests) != 1 {
+		t.Errorf("openrtb type should be an Array when it's an App")
+	}
+	var the_body openrtb.BidRequest
+	if err := json.Unmarshal(actualAdapterRequests[0].Body, &the_body); err != nil {
+		t.Errorf("failed to read bid request")
+	}
+
+	if the_body.App == nil {
+		t.Errorf("app property should be populated")
+	}
+
+	if the_body.App.Publisher.ID == "" {
+		t.Errorf("Missing publisher ID must be in")
+	}
+}
+
+func TestMakeRequestsApp(t *testing.T) {
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		Imp: []openrtb.Imp{imp1},
+		Site: &openrtb.Site{
+			Publisher: &openrtb.Publisher{
+				ID: "10007",
+			},
+		},
+		App: &openrtb.App{ID: "cansanuabnua", Publisher: &openrtb.Publisher{ID: "whatever"}},
+		ID:  "1234",
+	}
+
+	actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+
+	if len(actualAdapterRequests) != 1 {
+		t.Errorf("openrtb type should be an Array when it's an App")
+	}
+	var the_body openrtb.BidRequest
+	if err := json.Unmarshal(actualAdapterRequests[0].Body, &the_body); err != nil {
+		t.Errorf("failed to read bid request")
+	}
+
+	if the_body.App == nil {
+		t.Errorf("app property should be populated")
+	}
+
+}
+
+func TestMakeRequestsNoUser(t *testing.T) {
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		Imp: []openrtb.Imp{imp1},
+		Site: &openrtb.Site{
+			Publisher: &openrtb.Publisher{
+				ID: "10007",
+			},
+		},
+		ID: "1234",
+	}
+
+	actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+
+	if actualAdapterRequests != nil {
+		t.Errorf("openrtb type should be empty")
+	}
+
+}
+
+func TestMakeRequests(t *testing.T) {
+	//server := httptest.NewServer(http.HandlerFunc(DummyDmxServer))
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+	imp2 := openrtb.Imp{
+		ID:  "imp2",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+	imp3 := openrtb.Imp{
+		ID:  "imp3",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		Imp: []openrtb.Imp{imp1, imp2, imp3},
+		Site: &openrtb.Site{
+			Publisher: &openrtb.Publisher{
+				ID: "10007",
+			},
+		},
+		User: &openrtb.User{ID: "districtmID"},
+		ID:   "1234",
+	}
+
+	actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+
+	if len(actualAdapterRequests) != 1 {
+		t.Errorf("should have 1 request")
+	}
+	var the_body openrtb.BidRequest
+	if err := json.Unmarshal(actualAdapterRequests[0].Body, &the_body); err != nil {
+		t.Errorf("failed to read bid request")
+	}
+
+	if len(the_body.Imp) != 3 {
+		t.Errorf("must have 3 bids")
+	}
+
+}
+
+func TestMakeBidVideo(t *testing.T) {
+	var w, h int = 640, 480
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Video: &openrtb.Video{
+			W:     width,
+			H:     height,
+			MIMEs: []string{"video/mp4"},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		Imp: []openrtb.Imp{imp1},
+		Site: &openrtb.Site{
+			Publisher: &openrtb.Publisher{
+				ID: "10007",
+			},
+		},
+		User: &openrtb.User{ID: "districtmID"},
+		ID:   "1234",
+	}
+
+	actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+
+	if len(actualAdapterRequests) != 1 {
+		t.Errorf("should have 1 request")
+	}
+	var the_body openrtb.BidRequest
+	if err := json.Unmarshal(actualAdapterRequests[0].Body, &the_body); err != nil {
+		t.Errorf("failed to read bid request")
+	}
+
+	if len(the_body.Imp) != 1 {
+		t.Errorf("must have 1 bids")
+	}
+}
+
+func TestMakeBidsNoContent(t *testing.T) {
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		Imp: []openrtb.Imp{imp1},
+		Site: &openrtb.Site{
+			Publisher: &openrtb.Publisher{
+				ID: "10007",
+			},
+		},
+		User: &openrtb.User{ID: "districtmID"},
+		ID:   "1234",
+	}
+
+	actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+
+	_, err204 := adapter.MakeBids(&inputRequest, actualAdapterRequests[0], &adapters.ResponseData{StatusCode: 204})
+
+	if err204 != nil {
+		t.Errorf("Was expecting nil")
+	}
+
+	_, err400 := adapter.MakeBids(&inputRequest, actualAdapterRequests[0], &adapters.ResponseData{StatusCode: 400})
+
+	if err400 == nil {
+		t.Errorf("Was expecting error")
+	}
+
+	_, err500 := adapter.MakeBids(&inputRequest, actualAdapterRequests[0], &adapters.ResponseData{StatusCode: 500})
+
+	if err500 == nil {
+		t.Errorf("Was expecting error")
+	}
+
+	bidResponse := &adapters.ResponseData{
+		StatusCode: 200,
+		Body: []byte(`{
+  "id": "JdSgvXjee0UZ",
+  "seatbid": [
+    {
+      "bid": [
+        {
+          "id": "16-40dbf1ef_0gKywr9JnzPAW4bE-1",
+          "impid": "imp1",
+          "price": 2.3456,
+          "adm": "<some html here \/>",
+          "nurl": "dmxnotificationurlhere",
+          "adomain": [
+            "brand.com",
+            "advertiser.net"
+          ],
+          "cid": "12345",
+          "crid": "232303",
+          "cat": [
+            "IAB20-3"
+          ],
+          "attr": [
+            2
+          ],
+          "w": 300,
+          "h": 600,
+          "language": "en"
+        }
+      ],
+    "seat": "10001"
+    }
+  ],
+  "cur": "USD"
+}`),
+	}
+
+	bidResponseNoMatch := &adapters.ResponseData{
+		StatusCode: 200,
+		Body: []byte(`{
+  "id": "JdSgvXjee0UZ",
+  "seatbid": [
+    {
+      "bid": [
+        {
+          "id": "16-40dbf1ef_0gKywr9JnzPAW4bE-1",
+          "impid": "djvnsvns",
+          "price": 2.3456,
+          "adm": "<some html here \/>",
+          "nurl": "dmxnotificationurlhere",
+          "adomain": [
+            "brand.com",
+            "advertiser.net"
+          ],
+          "cid": "12345",
+          "crid": "232303",
+          "cat": [
+            "IAB20-3"
+          ],
+          "attr": [
+            2
+          ],
+          "w": 300,
+          "h": 600,
+          "language": "en"
+        }
+      ],
+    "seat": "10001"
+    }
+  ],
+  "cur": "USD"
+}`),
+	}
+
+	bids, _ := adapter.MakeBids(&inputRequest, actualAdapterRequests[0], bidResponse)
+	if bids == nil {
+		t.Errorf("ads not parse")
+	}
+	bidsNoMatching, _ := adapter.MakeBids(&inputRequest, actualAdapterRequests[0], bidResponseNoMatch)
+	if bidsNoMatching == nil {
+		t.Errorf("ads not parse")
+	}
+
+}
+func TestUserExtEmptyObject(t *testing.T) {
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		Imp: []openrtb.Imp{imp1, imp1, imp1},
+		Site: &openrtb.Site{
+			Publisher: &openrtb.Publisher{
+				ID: "10007",
+			},
+		},
+		User: &openrtb.User{Ext: json.RawMessage(`{}`)},
+		ID:   "1234",
+	}
+
+	actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+	if len(actualAdapterRequests) != 0 {
+		t.Errorf("should have 0 request")
+	}
+}
+func TestUserEidsOnly(t *testing.T) {
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		Imp: []openrtb.Imp{imp1, imp1, imp1},
+		Site: &openrtb.Site{
+			Publisher: &openrtb.Publisher{
+				ID: "10007",
+			},
+		},
+		User: &openrtb.User{Ext: json.RawMessage(`{"eids": [{
+                "source": "adserver.org",
+                "uids": [{
+                    "id": "111111111111",
+                    "ext": {
+                        "rtiPartner": "TDID"
+                    }
+                }]
+            },{
+                "source": "netid.de",
+                "uids": [{
+                    "id": "11111111"
+                }]
+            }]
+            }`)},
+		ID: "1234",
+	}
+
+	actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+	if len(actualAdapterRequests) != 1 {
+		t.Errorf("should have 1 request")
+	}
+}
+
+func TestUserDigitrustOnly(t *testing.T) {
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		Imp: []openrtb.Imp{imp1, imp1, imp1},
+		Site: &openrtb.Site{
+			Publisher: &openrtb.Publisher{
+				ID: "10007",
+			},
+		},
+		User: &openrtb.User{Ext: json.RawMessage(`{
+            "digitrust": {
+                "id": "11111111111",
+                "keyv": 4
+            }}`)},
+		ID: "1234",
+	}
+
+	actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+	if len(actualAdapterRequests) != 1 {
+		t.Errorf("should have 1 request")
+	}
+}
+
+func TestUsersEids(t *testing.T) {
+	var w, h int = 300, 250
+
+	var width, height uint64 = uint64(w), uint64(h)
+
+	adapter := NewDmxBidder("https://dmx.districtm.io/b/v2")
+	imp1 := openrtb.Imp{
+		ID:  "imp1",
+		Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"),
+		Banner: &openrtb.Banner{
+			W: &width,
+			H: &height,
+			Format: []openrtb.Format{
+				{W: 300, H: 250},
+			},
+		}}
+
+	inputRequest := openrtb.BidRequest{
+		Imp: []openrtb.Imp{imp1, imp1, imp1},
+		Site: &openrtb.Site{
+			Publisher: &openrtb.Publisher{
+				ID: "10007",
+			},
+		},
+		User: &openrtb.User{ID: "districtmID", Ext: json.RawMessage(`{"eids": [{
+                "source": "adserver.org",
+                "uids": [{
+                    "id": "111111111111",
+                    "ext": {
+                        "rtiPartner": "TDID"
+                    }
+                }]
+            },{
+                "source": "pubcid.org",
+                "uids": [{
+                    "id":"11111111"
+                }]
+            },
+            {
+                "source": "id5-sync.com",
+                "uids": [{
+                    "id": "ID5-12345"
+                }]
+            },
+            {
+                "source": "parrable.com",
+                "uids": [{
+                    "id": "01.1563917337.test-eid"
+                }]
+            },{
+                "source": "identityLink",
+                "uids": [{
+                    "id": "11111111"
+                }]
+            },{
+                "source": "criteo",
+                "uids": [{
+                    "id": "11111111"
+                }]
+            },{
+                "source": "britepool.com",
+                "uids": [{
+                    "id": "11111111"
+                }]
+            },{
+                "source": "liveintent.com",
+                "uids": [{
+                    "id": "11111111"
+                }]
+            },{
+                "source": "netid.de",
+                "uids": [{
+                    "id": "11111111"
+                }]
+            }],
+            "digitrust": {
+                "id": "11111111111",
+                "keyv": 4
+            }}`)},
+		ID: "1234",
+	}
+
+	actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{})
+	if len(actualAdapterRequests) != 1 {
+		t.Errorf("should have 1 request")
+	}
+	var the_body openrtb.BidRequest
+	if err := json.Unmarshal(actualAdapterRequests[0].Body, &the_body); err != nil {
+		t.Errorf("failed to read bid request")
+	}
+
+	if len(the_body.Imp) != 3 {
+		t.Errorf("must have 3 bids")
+	}
+}
+func TestVideoImpInsertion(t *testing.T) {
+	var bidResp openrtb.BidResponse
+	var bid openrtb.Bid
+	payload := []byte(`{
+    "id": "some-request-id",
+    "seatbid": [
+        {
+            "bid": [
+                {
+                    "id": "video1",
+                    "impid": "video1",
+                    "price": 5.01,
+                    "nurl": "https://demo.arripiblik.com/359585167267151",
+                    "adm": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><VAST version=\"3.0\"><Ad id=\"5f3c0f61_1aoDbsYiNHbYYpJ2qPtKgERbGiH\"><Wrapper><AdSystem>BidSwitch</AdSystem><VASTAdTagURI><![CDATA[https://bid.g.doubleclick.net/dbm/vast?dbm_c=AKAmf-Bsi1tzgaMFvkO8rVFA2uRjshf-8MKbfsVGvhoqjxCwhfgWnsfVqpOaKDWTYGa5YSdXijhgb8o2TGCuSC2sqawX0WP-Fw&dbm_d=AKAmf-B7vNPyDI7QTdv8f2N0jQJ9hMssfJqj7g1dhwwGRPxcWD8AbrxPgDmysYMj6IOE719Jb9IfX5eUQ7M9cki4w7q3XEI1L1AsUAZYc-HOzPIRQnOOTzypKOmzyfTjoC-r1KNgDeUPv1Z6g4BK6RH9vmRm0ML6wj4S04oJjZDzTE71QGWMZSfAhQhMDzlnSsj0JI1ruWhr_yGER4Qt61oAHC4aUnHWb7V2c7m0Z80mFoDtasDsEjF-QUJrA9LTg_taatSS1emAT2SpSyM98e_66b4YE6dOJG4hqWHl1QAsiDNQOsXZ_oEAGVdnXgS_3VIhdmHy2svbTTtvlzoGTj7tRCrDnmKdJq5dcDZgsZfCeNDBeQQXAzV1L824G5B7MyHKPHksgakRqbAr6y2-VPUuL0eeFGERNBAzQp6r2i6D3w9a3JrsdWPUw_j-6ph9Qw5T-tx8cbZ5zYH1a1RlAIYXuH15Wg-lofEOLFAud59ASP0El_xZK99fcbqcqjqAbaAkzLoTADGRv6ZYZj4wXirZ6R0PAFX4PtXawiRDt9e1oxnweT5_BKx77DtKe21yxGc2QEsQmxmxzSusDYxiCuZuWh7m1Pzp3_WsRTHKEl8T0KZAwtJDn5GjuaR_wxpztusRBtakyhb25e8xICiJvynehLCiTC3jEF5us6_M-y1RD6i3J6VJ5idGBqvYnNk2K_pwsKc0zRbC1hmSNp35xkccXFz9wK1acUq5dEUnRK--49OsfydcdfOKlxMHAbMgB4LJ3RpweBMNt1eeeKNrFRdFDXIYamkdWbr19QhAMjHghibLg6zfgxpxe2Ee0-yQqXx2Dp4pEc-11NUzoIvZuuYIj65YFPXAh9eYU3k8V6iGJcYpoac5nKdZI6qZipNWQ6ZktnUTJ60XR_wk1MlSgVJaGMlagtzuFGDL0joKYDoU898nea406NrqLUsCznXLxBqCmTXtxFZuCDTJ_kwMsyg1J5l8Jbi7McCUsIAI9jm_JhCnIAFE8x_mAANEgPW2KpVKsZP2OufTizw-8ZLS-djLzB-9EMKkOnT8EB6A--PwXyJkQUTqifgfRsg_W3bnVAX2NwpjSC-NuZ2tPIO83O_2M3EctszXJe0A-7cPmhJ68U6LDNm-SFvan4C5gnvi0eHMOJDtMGbCRCy8hqi0VIlU2IZkaL7D8TwAUDFp8zL0fABsnwLUe-14arGfs_4NIIZoJdQXoBG9uz0e6sLY4RjcjK_s60weaoBYxO8kSL-3TsrD-oMVJrCA4NOjnGxobu4H-kYqDkW4bNjA3yQ0LQVQuJUw6RUBZXTSz_9IleIQSey4060dlwA1O1zESsb3oQGa3S7E25Lof3noX5Gq2SwfT8JEoVUI03ORusmqMrnvgqlNP_kq0MPUlb_zzssb5V3jlLVsu9V8svhTIo5nz6zF-Ydp6pqn68bT-ohQzyLPJLJEm1CBEwOvur2TmmyQ-W7MriJCm_uUYfRJGJCXw44whnFKbp3CZeUmFHWLK4lauabyo3csEE5DJkMClctxMxSRp6uZOyiYlOqi8S2YJBvL_NEppzrN2W1cfKEQyNIFj1WsGx62tPY-TzOgArnEetNS3mUHQi5F1Vz6QUdmuIKQpseDqU_GVhAOVCTeh6MNpjM59SXmMFY4ram7U6WZTIniRjX27m-XVaAmzqKH-s0GlmvpO4Fssb9wQc7jHDcqXcsyqbhDTrlIuezBMxjfykPDi2hs96PRv4N5ADvEuKAPkVAKZ2pbt6lr1pZLnSFjXxIlLYXpULQwObVxcdbt0Sw4m6aHtBTyvJnz6bS7z4wpbW87My7swTWwqtBdhEo_jm4_ZHph0FB9_kw46lrWtAIk_qU6AOoE2lAQ8VrRXAOn9LUVeu5JuLCnuVNvMygzMRMn5KvUjGuxMtD53xTcAXgmbd_5Q0eryr4lMKlDFop2Djei9pyxJR1rTQ4xTfHG3kB-MgdyGZLuFuHtILkPjuVQXhNCuBFMe6dRAtuDvptk1y8VAULdda0YNDLo960PxeTFjbqrRz5wfeUqnu1FV6h9cERcJBGSZlAPH254XNcCwwYyyYr0oD2yQZxmSlr2slQpaW-XdwdGie3vlrmYhfe4-5VrDMHymfKIM-ZUuD3sq6V03Efg14JDMghu9IHO0xbefQuTFBCen1AHBVy4mwJm0Kxp6jEgdNXb7Taa0AF2zwzORu0W0P8lTtWFv3Pg5JWplYorfdJSQGr7hDuxLpyeRiJjzft9liSp22APvHumvJCNgNe0DrWddzW0sRMZOLAFQn1F8cQwLX7mIFEplD3MEQht2HYe4gR8sN44lHQw6CmYV0Ai_tjL1oS17BShey-JkVSPdWVp9r3VfNV1releGX3u-12HtheEvOSsv7qQxIU30Ui10QjnSony-ORz48c4_wbhGmucwUx37Ggf5XhmvVjJUEMDlhYtCj2-rtik2-nskOeSNYKo9hb0DPcahsBEiL4vz7fhgEiivFV0FxiHq0pEukcmOFgE6V7X_upwKli65YEOxrH_GZn2DmjjcUJHD0UNLHOe4D7DV16K7YC9HfodCtzeX0LWZnMCKXv76UiXy_sVITPm4G6Td4AXliC2zwiI_URnQqdCyhRIA40E2qjU31ED56DpkOSo1WiU4fv2mv-n5lyb99lzL2srVV0KFYcvwDOyOnNByXmFw18w6-9snWvYtCDY_AbWtvlSI_60OxCOxipqxGVvA4HXWlFUvOl3OtqRf6apMZIaEpFDcL4N0NGuItEKRb04ZE6lv1zNOxC9WBQHZV_pqK9RQ7YbKrWfcL_c79XQji0Bd3mE6k08AfsjYugcWy-wsjmq467VhLWe4OcSAB8B2Qq_eZl5RPrM6LayPWpuz637TeHNfMwuta9R394K36rAtWRf3Ns_oDJz6g&cid=CAASEuRoF_6hDi9IvsHr8we-jyLVhw]]></VASTAdTagURI><Error><![CDATA[https://gce-sc.bidswitch.net/vast_error/O2lw3vm7UGHsHYVBXTjm9Y-DOn5osUxLug71FGBtENsDQCc-y60Y47eIRTQ6TYpRGzvXKzk3o6ZC1FRWXCtQvUdjR-4JrsHILqJGIQjWxB8dGjZlUsDqwpLZNdcECrS16f-XsuNngtxHNKHFdGQ-tfvCihmYUYAPTATvkDSVXDLrwPLAIbEC11ElAmyUUlFqqYMFHvu71ibAo55IU_pQCfvScb0WcKunBti5lcbnSaOny5VE6Kc2fwHXAzG2Tbu4wyRxenfkeMPAaq5aGvIFL2fWlgz9_Y7tUaUq-k6YZxzlTJ3QbwnmbvL3LejwjkG6BnhR5DuJ-X_EWhKWmna0YsxXA-vnFBmqcH6pyuKQ5C93ZApV8xq_N86vccZ2nVTOI6DLL-8N8cVBKvmp2lL0vAPLU2A5uEaaoX8xuw322lv8ksG1McwGwlFoaWkzjBsJivY2eNeFxXCsFBF6BYiBKLCWy62iecTnQfJykTx3orDsnQez89JPW52DQSsxSb0_YPpUpfclYrBe_FAMle4AOBpZs8ib7hsGXxNOkgJdtwz8_bIIlv-U4Hmoym9ulx0svFV78boErVYrNBf6D43tHl027bfkZgXC0CsVpdmZKUHSpcI3mBQKPC5Qb-tCgVpsLK4xUMvFvJFLXIX1yjj062J4ZH9fqm3kNZdfESq4XIVOlx7aMYoDxxnBrSjthW0KKvruGzicYm7c8Nwp19xjTQwR83Cks2FPzxbAm3jAmP0vaNhS7_xkBpN_nVvxis18qaY1YYt4KQAqtxr90Y9rG0wOcmQQf52sbXzd4DqVs3H4PM4v_ZmqJOSFpvDczzKPasn0mV43TyaG-UOnkX1nFqXjMnty8-gpGPqkO5RyhKfP1vTQxpVDI_VaEiOPdNCUwXmhoxJJophsVX4FNBg_kwF-FL2d59j2Oi6gBPASSZ94dr8CMgRC1YZ_SRLwr90h/]]></Error><Impression><![CDATA[https://gce-sc.bidswitch.net/imp/1.9432/BSWhttps_A_B_Badx.g.doubleclick.net_Bpagead_Badview_Cai_RCuMI3Z7KdXrLWMIK5jAan44DIDI3a871cpZKH-u8K7u__b7-kYEAEgg__3mH2DJ3oqLwKTYD8gBBagDAcgDE5gEAKoE4wFP0EQmjUWxRmz76rmwUIT95PKyz7RN57CiJGB-sRzdZcyC4Y-6HUFDwGRTcvbxghpdmUAckrP4ZCE8BqxDB1S1GfTJ0RedqQ__jF1fZSAbUS__sk29Fo0aVpGDQHonn9DXIHSQyqD6KjtPGBdK2zNBgGcCet519-DXoAADunurUqLNITnWZbV__-W3GHtah6ZJB4grkIP3pZqbvCc7p__No0or0BTzAsyVKwm5CUZ6DfDm-GCy3V6SLjPVNtFoo6E89XbxIlSqsXWb8T__YtxVd9k-4at6Qt1sk8jx0SIvu5rcTbeoZ__8AElq-XqskC4AQDiAXksbqxIJIFBggDEAIYAZIFBggbEAIYAZIFCggiEAMYAUjPrVOSBQYIHRAEGAGSBQYIHRABGAGSBQYIHhABGAGQBgGgBk-AB8afu7cBqAeOzhuoB9XJG6gHk9gbqAe6BqgH8NkbqAfy2RuoB-zVG6gHpr4bqAfs1RvYBwDyBwkQ6J17GOTk3WbSCAcIgGEQARgf8ggXYmlkZGVyLWRpc3RyaWN0bV8xNzM2MzSACgTICwGwE9mS0QjIE8jXmAjQEwDYEwqIFALYFAE_Jsigh_Rw8Q7gjefARc_Jcmd_RChdjYS1wdWItNzM1MDg5NzEzODA5OTk1OBAAGAE_Jpr_R38_A_I_WAUCTION__PRICE_X_Jcid_RCAASEuRoF__6hDi9IvsHr8we-jyLVhw_Jtpd_RAGWhJmvwUOaWQX5xLWpPeUyjI1k5ciEvQjY2Irxt7p0U1kq77g/O2lw3vm7UGHsHYVBXTjm9Y-DOn5osUxLug71FGBtENsDQCc-y60Y47eIRTQ6TYpRGzvXKzk3o6ZC1FRWXCtQvUdjR-4JrsHILqJGIQjWxB8dGjZlUsDqwpLZNdcECrS16f-XsuNngtxHNKHFdGQ-tfvCihmYUYAPTATvkDSVXDLrwPLAIbEC11ElAmyUUlFqqYMFHvu71ibAo55IU_pQCfvScb0WcKunBti5lcbnSaOny5VE6Kc2fwHXAzG2Tbu4wyRxenfkeMPAaq5aGvIFL2fWlgz9_Y7tUaUq-k6YZxzlTJ3QbwnmbvL3LejwjkG6BnhR5DuJ-X_EWhKWmna0YsxXA-vnFBmqcH6pyuKQ5C93ZApV8xq_N86vccZ2nVTOI6DLL-8N8cVBKvmp2lL0vAPLU2A5uEaaoX8xuw322lv8ksG1McwGwlFoaWkzjBsJivY2eNeFxXCsFBF6BYiBKLCWy62iecTnQfJykTx3orDsnQez89JPW52DQSsxSb0_YPpUpfclYrBe_FAMle4AOBpZs8ib7hsGXxNOkgJdtwz8_bIIlv-U4Hmoym9ulx0svFV78boErVYrNBf6D43tHl027bfkZgXC0CsVpdmZKUHSpcI3mBQKPC5Qb-tCgVpsLK4xUMvFvJFLXIX1yjj062J4ZH9fqm3kNZdfESq4XIVOlx7aMYoDxxnBrSjthW0KKvruGzicYm7c8Nwp19xjTQwR83Cks2FPzxbAm3jAmP0vaNhS7_xkBpN_nVvxis18qaY1YYt4KQAqtxr90Y9rG0wOcmQQf52sbXzd4DqVs3H4PM4v_ZmqJOSFpvDczzKPasn0mV43TyaG-UOnkX1nFqXjMnty8-gpGPqkO5RyhKfP1vTQxpVDI_VaEiOPdNCUwXmhoxJJophsVX4FNBg_kwF-FL2d59j2Oi6gBPASSZ94dr8CMgRC1YZ_SRLwr90h/]]></Impression><Impression><![CDATA[https://us-east-sync.bidswitch.net/sync_cors?ssp=districtm&dsp_id=16&imp=1]]></Impression><Creatives></Creatives></Wrapper></Ad></VAST><!-- DMX - seat 10009 - crid 16_215446116 -->",
+                    "crid": "76575664756",
+                    "dealid": "dmx-deal-hp-24",
+                    "w": 640,
+                    "h": 480,
+                    "ext": {
+                        "prebid": {
+                            "type": "video"
+                        }
+                    }
+                },
+                {
+                    "id": "some-impression-id",
+                    "impid": "some-impression-id",
+                    "price": 5.01,
+                    "adm": "<img src='https://via.placeholder.com/300x250.png?text=dmx+2.0+300x250' height='250' width='300'/>",
+                    "crid": "1346943998",
+                    "dealid": "dmx-deal-hp-24",
+                    "w": 300,
+                    "h": 250,
+                    "ext": {
+                        "prebid": {
+                            "type": "banner"
+                        }
+                    }
+                },
+                {
+                    "id": "some-impression-id2",
+                    "impid": "some-impression-id2",
+                    "price": 5.01,
+                    "adm": "<img src='https://via.placeholder.com/728x90.png?text=dmx+2.0+728x90' height='90' width='728'/>",
+                    "crid": "1424798162",
+                    "dealid": "dmx-deal-hp-24",
+                    "w": 728,
+                    "h": 90,
+                    "ext": {
+                        "prebid": {
+                            "type": "banner"
+                        }
+                    }
+                }
+            ],
+            "seat": "dmx"
+        }
+    ]
+}`)
+
+	err := json.Unmarshal(payload, &bidResp)
+	if err != nil {
+		t.Errorf("Payload is invalid")
+	}
+	bid = openrtb.Bid(bidResp.SeatBid[0].Bid[0])
+	data := videoImpInsertion(&bid)
+	find := strings.Index(data, "demo.arripiblik.com")
+	if find == -1 {
+		t.Errorf("String was not found")
+	}
+
+}
diff --git a/adapters/dmx/dmxtest/exemplary/simple-app.json b/adapters/dmx/dmxtest/exemplary/simple-app.json
new file mode 100644
index 00000000..a2d57163
--- /dev/null
+++ b/adapters/dmx/dmxtest/exemplary/simple-app.json
@@ -0,0 +1,138 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app":{
+      "bundle":"302324249",
+      "id":"ed6207cefff74c14878963566683c070",
+      "name":"Skout - iOS Match Buy",
+      "publisher":{
+        "id":"10400"
+      },
+      "storeurl":"https://itunes.apple.com/app/id302324249"
+    },
+    "imp": [
+      {
+
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250,
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "dmxid": "123454",
+            "publisher_id": "10400"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "",
+        "body": {
+          "id": "test-request-id",
+          "app":{
+            "bundle":"302324249",
+            "id":"ed6207cefff74c14878963566683c070",
+            "name":"Skout - iOS Match Buy",
+            "publisher":{
+              "id":"10400"
+            },
+            "storeurl":"https://itunes.apple.com/app/id302324249"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "tagid": "123454",
+              "secure": 1,
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ],
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "publisher_id": "10400",
+                  "dmxid": "123454"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "958",
+              "bid": [{
+                "id": "7706636740145184841",
+                "impid": "test-imp-id",
+                "price": 1.75,
+                "adid": "29681110",
+                "adm": "<div>banner-ads</div>",
+                "adomain": ["dmx.districtm.io"],
+                "iurl": "https://dmx.districtm.io/b/v2",
+                "cid": "958",
+                "crid": "29681110",
+                "h": 250,
+                "w": 300
+              }]
+            }
+          ],
+          "bidid": "5778926625248726496",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7706636740145184841",
+            "impid": "test-imp-id",
+            "price": 1.75,
+            "adm": "<div>banner-ads</div>",
+            "adid": "29681110",
+            "adomain": ["dmx.districtm.io"],
+            "iurl": "https://dmx.districtm.io/b/v2",
+            "cid": "958",
+            "crid": "29681110",
+            "w": 300,
+            "h": 250
+
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/dmx/dmxtest/exemplary/simple-banner.json b/adapters/dmx/dmxtest/exemplary/simple-banner.json
new file mode 100644
index 00000000..03ea6246
--- /dev/null
+++ b/adapters/dmx/dmxtest/exemplary/simple-banner.json
@@ -0,0 +1,126 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "user": {
+      "id": "fhacacnasicnaic"
+    },
+    "imp": [
+      {
+
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250,
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "dmxid": "123454",
+            "publisher_id": "10400"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "",
+        "body": {
+          "id": "test-request-id",
+          "user": {
+            "id": "fhacacnasicnaic"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "tagid": "123454",
+              "secure": 1,
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ],
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "publisher_id": "10400",
+                  "dmxid": "123454"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "958",
+              "bid": [{
+                "id": "7706636740145184841",
+                "impid": "test-imp-id",
+                "price": 1.75,
+                "adid": "29681110",
+                "adm": "<div>banner-ads</div>",
+                "adomain": ["dmx.districtm.io"],
+                "iurl": "https://dmx.districtm.io/b/v2",
+                "cid": "958",
+                "crid": "29681110",
+                "h": 250,
+                "w": 300
+              }]
+            }
+          ],
+          "bidid": "5778926625248726496",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7706636740145184841",
+            "impid": "test-imp-id",
+            "price": 1.75,
+            "adm": "<div>banner-ads</div>",
+            "adid": "29681110",
+            "adomain": ["dmx.districtm.io"],
+            "iurl": "https://dmx.districtm.io/b/v2",
+            "cid": "958",
+            "crid": "29681110",
+            "w": 300,
+            "h": 250
+
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/dmx/dmxtest/exemplary/simple-video.json b/adapters/dmx/dmxtest/exemplary/simple-video.json
new file mode 100644
index 00000000..b4c53188
--- /dev/null
+++ b/adapters/dmx/dmxtest/exemplary/simple-video.json
@@ -0,0 +1,112 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "user": {
+      "id": "whateveryouwant"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": ["video/mp4"],
+          "minduration": 15,
+          "maxduration": 30,
+          "protocols": [2, 3, 5, 6, 7, 8],
+          "w": 940,
+          "h": 560
+        },
+        "ext": {
+          "bidder": {
+            "tagid": "12345",
+            "publisher_id": "10400"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "",
+        "body": {
+          "id": "test-request-id",
+          "user": {
+            "id": "whateveryouwant"
+          },
+          "imp": [
+            {
+              "ext": {
+                "bidder": {
+                  "tagid": "12345",
+                  "publisher_id": "10400"
+                }
+              },
+              "id": "test-imp-id",
+              "tagid": "12345",
+              "secure": 1,
+              "video": {
+                "mimes": ["video/mp4"],
+                "minduration": 15,
+                "maxduration": 30,
+                "protocols": [2, 3, 5, 6, 7, 8],
+                "w": 940,
+                "h": 560
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "958",
+              "bid": [{
+                "id": "7706636740145184841",
+                "impid": "test-imp-id",
+                "price": 1.90,
+                "adid": "29681110",
+                "adm": "ads",
+                "adomain": ["dmx.districtm.io"],
+                "iurl": "https://dmx.districtm.io/b/v2",
+                "cid": "958",
+                "crid": "29681110",
+                "h": 250,
+                "w": 300
+              }]
+            }
+          ],
+          "bidid": "5778926625248726496",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "7706636740145184841",
+            "impid": "test-imp-id",
+            "price": 1.90,
+            "adm": "ads",
+            "adid": "29681110",
+            "adomain": ["dmx.districtm.io"],
+            "iurl": "https://dmx.districtm.io/b/v2",
+            "cid": "958",
+            "crid": "29681110",
+            "w": 300,
+            "h": 250
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/dmx/dmxtest/params/race/banner.json b/adapters/dmx/dmxtest/params/race/banner.json
new file mode 100644
index 00000000..1c0adff7
--- /dev/null
+++ b/adapters/dmx/dmxtest/params/race/banner.json
@@ -0,0 +1,4 @@
+{
+  "tagid": "25251",
+  "publisher_id": "100152"
+}
\ No newline at end of file
diff --git a/adapters/dmx/dmxtest/params/race/video.json b/adapters/dmx/dmxtest/params/race/video.json
new file mode 100644
index 00000000..3bbd83bd
--- /dev/null
+++ b/adapters/dmx/dmxtest/params/race/video.json
@@ -0,0 +1,4 @@
+{
+  "tagid": "25255",
+  "publisher_id": "100151"
+}
\ No newline at end of file
diff --git a/adapters/dmx/usersync_test.go b/adapters/dmx/usersync_test.go
new file mode 100644
index 00000000..e4e3c7d8
--- /dev/null
+++ b/adapters/dmx/usersync_test.go
@@ -0,0 +1,20 @@
+package dmx
+
+import (
+	"github.com/prebid/prebid-server/privacy"
+	"testing"
+	"text/template"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestDmxSyncer(t *testing.T) {
+	temp := template.Must(template.New("sync-template").Parse("https://dmx.districtm.io/s/v1/img/s/10007"))
+	syncer := NewDmxSyncer(temp)
+	syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{})
+	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/eplanning/eplanning_test.go b/adapters/eplanning/eplanning_test.go
index d2c331d4..28fdf6c4 100644
--- a/adapters/eplanning/eplanning_test.go
+++ b/adapters/eplanning/eplanning_test.go
@@ -8,7 +8,7 @@ import (
 )
 
 func TestJsonSamples(t *testing.T) {
-	eplanningAdapter := NewEPlanningBidder(new(http.Client), "https://ads.us.e-planning.net/hb/1")
+	eplanningAdapter := NewEPlanningBidder(new(http.Client), "http://rtb.e-planning.net/pbs/1")
 	eplanningAdapter.testing = true
 	adapterstest.RunJSONBidderTest(t, "eplanningtest", eplanningAdapter)
 }
diff --git a/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json b/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json
index e602877f..55683121 100644
--- a/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json
+++ b/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json
@@ -28,7 +28,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=300x250%3A300x250&ncb=1&r=pbs&ur=FILE",
+        "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=300x250%3A300x250&ncb=1&ur=FILE",
         "body": {}
       },
       "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/exemplary/simple-banner.json b/adapters/eplanning/eplanningtest/exemplary/simple-banner.json
index 0403e59a..04eca985 100644
--- a/adapters/eplanning/eplanningtest/exemplary/simple-banner.json
+++ b/adapters/eplanning/eplanningtest/exemplary/simple-banner.json
@@ -31,7 +31,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadun_itco_de%3A600x300&ip=123.123.123.123&ncb=1&r=pbs&uid=2154987&ur=FILE",
+        "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadun_itco_de%3A600x300&ip=123.123.123.123&ncb=1&uid=2154987&ur=FILE",
         "body": {}
       },
       "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/exemplary/two-banners.json b/adapters/eplanning/eplanningtest/exemplary/two-banners.json
index 8c4ca021..72aeb64b 100644
--- a/adapters/eplanning/eplanningtest/exemplary/two-banners.json
+++ b/adapters/eplanning/eplanningtest/exemplary/two-banners.json
@@ -39,7 +39,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300%2B300x250%3A300x250&ip=123.123.123.123&ncb=1&r=pbs&ur=FILE",
+          "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300%2B300x250%3A300x250&ip=123.123.123.123&ncb=1&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json b/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json
index e6e25566..413c973d 100644
--- a/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json
+++ b/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json
@@ -39,7 +39,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/mx.com.xeu/ROS?app=1&appid=%5Ba-f0-9%5D%7B16%7D&appn=MobileExchange&e=testadunitcode%3A600x300&ifa=3B8E2335-Z049&ip=123.123.123.123&ncb=1&r=pbs",
+          "uri": "http://rtb.e-planning.net/pbs/1/12345/1/mx.com.xeu/ROS?app=1&appid=%5Ba-f0-9%5D%7B16%7D&appn=MobileExchange&e=testadunitcode%3A600x300&ifa=3B8E2335-Z049&ip=123.123.123.123&ncb=1",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json b/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json
index f1bc29e1..05547d81 100644
--- a/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json
+++ b/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json
@@ -19,7 +19,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcodenosize%3A1x1&ncb=1&r=pbs&ur=FILE",
+          "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcodenosize%3A1x1&ncb=1&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json b/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json
index 978989e2..57048882 100644
--- a/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json
+++ b/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json
@@ -21,7 +21,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE",
+          "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json b/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json
index 7198f4ee..4ba2d44b 100644
--- a/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json
+++ b/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json
@@ -21,7 +21,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE",
+          "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json b/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json
index 938ede62..9e8eae8c 100644
--- a/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json
+++ b/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json
@@ -21,7 +21,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE",
+          "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/server-error-code.json b/adapters/eplanning/eplanningtest/supplemental/server-error-code.json
index eaa2a677..08f46d9e 100644
--- a/adapters/eplanning/eplanningtest/supplemental/server-error-code.json
+++ b/adapters/eplanning/eplanningtest/supplemental/server-error-code.json
@@ -21,7 +21,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE",
+          "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/server-no-content.json b/adapters/eplanning/eplanningtest/supplemental/server-no-content.json
index d1feb865..20a2b1cf 100644
--- a/adapters/eplanning/eplanningtest/supplemental/server-no-content.json
+++ b/adapters/eplanning/eplanningtest/supplemental/server-no-content.json
@@ -21,7 +21,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE",
+          "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json b/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json
index 2f43b9aa..62890d91 100644
--- a/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json
+++ b/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json
@@ -25,7 +25,7 @@
     "httpCalls": [
       {
         "expectedRequest": {
-          "uri": "https://ads.us.e-planning.net/hb/1/12345/1/www.publisher.com/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=http%3A%2F%2Fwww.publisher.com%2Fawesome%2Fsite%3Fwith%3Dsome%26parameters%3Dhere",
+          "uri": "http://rtb.e-planning.net/pbs/1/12345/1/www.publisher.com/ROS?e=testadunitcode%3A600x300&ncb=1&ur=http%3A%2F%2Fwww.publisher.com%2Fawesome%2Fsite%3Fwith%3Dsome%26parameters%3Dhere",
           "body": {}
         },
         "mockResponse": {
diff --git a/adapters/telaria/telariatest/exemplary/video-app.json b/adapters/telaria/telariatest/exemplary/video-app.json
index 09bcf998..fa755cc9 100644
--- a/adapters/telaria/telariatest/exemplary/video-app.json
+++ b/adapters/telaria/telariatest/exemplary/video-app.json
@@ -51,7 +51,6 @@
         "Content-Type": ["application/json;charset=utf-8"],
         "Accept": ["application/json"],
         "X-Openrtb-Version": ["2.5"],
-        "Accept-Encoding": ["gzip"],
         "User-Agent": ["test-user-agent"],
         "X-Forwarded-For": ["123.123.123.123"],
         "Accept-Language": ["en"],
diff --git a/adapters/telaria/telariatest/exemplary/video-web.json b/adapters/telaria/telariatest/exemplary/video-web.json
index 5dc26b0f..c671d2f5 100644
--- a/adapters/telaria/telariatest/exemplary/video-web.json
+++ b/adapters/telaria/telariatest/exemplary/video-web.json
@@ -45,7 +45,6 @@
         "Content-Type": ["application/json;charset=utf-8"],
         "Accept": ["application/json"],
         "X-Openrtb-Version": ["2.5"],
-        "Accept-Encoding": ["gzip"],
         "User-Agent": ["test-user-agent"],
         "X-Forwarded-For": ["123.123.123.123"],
         "Accept-Language": ["en"],
diff --git a/adapters/telaria/telariatest/supplemental/invalid-response.json b/adapters/telaria/telariatest/supplemental/invalid-response.json
index 8e4f1ee8..3f614a07 100644
--- a/adapters/telaria/telariatest/supplemental/invalid-response.json
+++ b/adapters/telaria/telariatest/supplemental/invalid-response.json
@@ -45,7 +45,6 @@
         "Content-Type": ["application/json;charset=utf-8"],
         "Accept": ["application/json"],
         "X-Openrtb-Version": ["2.5"],
-        "Accept-Encoding": ["gzip"],
         "User-Agent": ["test-user-agent"],
         "X-Forwarded-For": ["123.123.123.123"],
         "Accept-Language": ["en"],
diff --git a/adapters/yieldlab/params_test.go b/adapters/yieldlab/params_test.go
new file mode 100644
index 00000000..8c230c15
--- /dev/null
+++ b/adapters/yieldlab/params_test.go
@@ -0,0 +1,63 @@
+package yieldlab
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/yieldlab.json
+//
+// These also validate the format of the external API: request.imp[i].ext.yieldlab
+
+// TestValidParams makes sure that the yieldlab schema accepts all imp.ext fields which we intend to support.
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderYieldlab, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected yieldlab params: %s", validParam)
+		}
+	}
+}
+
+// TestInvalidParams makes sure that the yieldlab schema rejects all the imp.ext fields we don't support.
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderYieldlab, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"adslotId": "123","supplyId":"23456","adSize":"100x100"}`,
+	`{"adslotId": "123","supplyId":"23456","adSize":"100x100","extId":"asdf"}`,
+	`{"adslotId": "123","supplyId":"23456","adSize":"100x100","extId":"asdf","targeting":{"a":"b"}}`,
+	`{"adslotId": "123","supplyId":"23456","adSize":"100x100","targeting":{"a":"b"}}`,
+	`{"adslotId": "123","supplyId":"23456","adSize":"100x100","targeting":{"a":"b"}}`,
+}
+
+var invalidParams = []string{
+	`{"supplyId":"23456","adSize":"100x100"}`,
+	`{"adslotId": "123","adSize":"100x100","extId":"asdf"}`,
+	`{"adslotId": "123","supplyId":"23456","extId":"asdf","targeting":{"a":"b"}}`,
+	`{"adslotId": "123","supplyId":"23456"}`,
+	`{"adSize":"100x100","supplyId":"23456"}`,
+	`{"adslotId": "123","adSize":"100x100"}`,
+	`{"supplyId":"23456"}`,
+	`{"adslotId": "123"}`,
+	`{}`,
+	`[]`,
+	`{"a":"b"}`,
+	`null`,
+}
diff --git a/adapters/yieldlab/usersync_test.go b/adapters/yieldlab/usersync_test.go
new file mode 100644
index 00000000..3892c16b
--- /dev/null
+++ b/adapters/yieldlab/usersync_test.go
@@ -0,0 +1,26 @@
+package yieldlab
+
+import (
+	"testing"
+	"text/template"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/prebid/prebid-server/privacy"
+	"github.com/prebid/prebid-server/privacy/gdpr"
+)
+
+func TestYieldlabSyncer(t *testing.T) {
+	temp := template.Must(template.New("sync-template").Parse("https://ad.yieldlab.net/mr?t=2&pid=9140838&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirectUri=http%3A%2F%2Flocalhost%2F%2Fsetuid%3Fbidder%3Dyieldlab%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25%25YL_UID%25%25"))
+	syncer := NewYieldlabSyncer(temp)
+	syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{
+		GDPR: gdpr.Policy{
+			Signal: "0",
+		},
+	})
+	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/yieldlab/yieldlab_test.go b/adapters/yieldlab/yieldlab_test.go
new file mode 100644
index 00000000..b6ca0507
--- /dev/null
+++ b/adapters/yieldlab/yieldlab_test.go
@@ -0,0 +1,128 @@
+package yieldlab
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+)
+
+const testURL = "https://ad.yieldlab.net/testing/"
+
+var testCacheBuster cacheBuster = func() string {
+	return "testing"
+}
+
+var testWeekGenerator weekGenerator = func() string {
+	return "33"
+}
+
+func newTestYieldlabBidder(endpoint string) *YieldlabAdapter {
+	return &YieldlabAdapter{
+		endpoint:    endpoint,
+		cacheBuster: testCacheBuster,
+		getWeek:     testWeekGenerator,
+	}
+}
+
+func TestNewYieldlabBidder(t *testing.T) {
+	bid := NewYieldlabBidder(testURL)
+	assert.NotNil(t, bid)
+	assert.Equal(t, bid.endpoint, testURL)
+	assert.NotNil(t, bid.cacheBuster)
+	assert.NotNil(t, bid.getWeek)
+}
+
+func TestJsonSamples(t *testing.T) {
+	adapterstest.RunJSONBidderTest(t, "yieldlabtest", newTestYieldlabBidder(testURL))
+}
+
+func Test_splitSize(t *testing.T) {
+	type args struct {
+		size string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    uint64
+		want1   uint64
+		wantErr bool
+	}{
+		{
+			name: "valid",
+			args: args{
+				size: "300x800",
+			},
+			want:    300,
+			want1:   800,
+			wantErr: false,
+		},
+		{
+			name: "empty",
+			args: args{
+				size: "",
+			},
+			want:    0,
+			want1:   0,
+			wantErr: false,
+		},
+		{
+			name: "invalid",
+			args: args{
+				size: "test",
+			},
+			want:    0,
+			want1:   0,
+			wantErr: false,
+		},
+		{
+			name: "invalid_height",
+			args: args{
+				size: "200xtest",
+			},
+			want:    0,
+			want1:   0,
+			wantErr: true,
+		},
+		{
+			name: "invalid_width",
+			args: args{
+				size: "testx200",
+			},
+			want:    0,
+			want1:   0,
+			wantErr: true,
+		},
+		{
+			name: "invalid_separator",
+			args: args{
+				size: "200y200",
+			},
+			want:    0,
+			want1:   0,
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, got1, err := splitSize(tt.args.size)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("splitSize() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != tt.want {
+				t.Errorf("splitSize() got = %v, want %v", got, tt.want)
+			}
+			if got1 != tt.want1 {
+				t.Errorf("splitSize() got1 = %v, want %v", got1, tt.want1)
+			}
+		})
+	}
+}
+
+func TestYieldlabAdapter_makeEndpointURL_invalidEndpoint(t *testing.T) {
+	bid := NewYieldlabBidder("test$:/something§")
+	_, err := bid.makeEndpointURL(nil, nil)
+	assert.Error(t, err)
+}
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/banner.json b/adapters/yieldlab/yieldlabtest/exemplary/banner.json
new file mode 100644
index 00000000..8dd94404
--- /dev/null
+++ b/adapters/yieldlab/yieldlabtest/exemplary/banner.json
@@ -0,0 +1,111 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 728,
+              "h": 90
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adslotId": "12345",
+            "supplyId": "123456789",
+            "adSize": "728x90",
+            "targeting": {
+              "key1": "value1",
+              "key2": "value2"
+            },
+            "extId": "abc"
+          }
+        }
+      }
+    ],
+    "user": {
+      "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c"
+    },
+    "device": {
+      "ifa": "hello-ads",
+      "devicetype": 4,
+      "connectiontype": 6,
+      "geo": {
+        "lat": 51.499488,
+        "lon": -0.128953
+      },
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
+      "ip": "169.254.13.37",
+      "h": 1098,
+      "w": 814
+    },
+    "site": {
+      "id": "fake-site-id",
+      "publisher": {
+        "id": "1"
+      },
+      "page": "http://localhost:9090/gdpr.html"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Cookie": [
+            "id=34a53e82-0dc3-4815-8b7e-b725ede0361c"
+          ],
+          "Referer": [
+            "http://localhost:9090/gdpr.html"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "169.254.13.37"
+          ]
+        },
+        "uri": "https://ad.yieldlab.net/testing/12345?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "id": 12345,
+            "price": 201,
+            "advertiser": "yieldlab",
+            "adsize": "728x90",
+            "pid": 1234,
+            "did": 5678,
+            "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5"
+          }
+        ]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "adm": "<script src=\"https://ad.yieldlab.net/d/12345/123456789/728x90?id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing\"></script>",
+            "crid": "12345123433",
+            "dealid": "1234",
+            "id": "12345",
+            "impid": "test-imp-id",
+            "price": 2.01,
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/gdpr.json b/adapters/yieldlab/yieldlabtest/exemplary/gdpr.json
new file mode 100644
index 00000000..381ba688
--- /dev/null
+++ b/adapters/yieldlab/yieldlabtest/exemplary/gdpr.json
@@ -0,0 +1,119 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 728,
+              "h": 90
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adslotId": "12345",
+            "supplyId": "123456789",
+            "adSize": "728x90",
+            "targeting": {
+              "key1": "value1",
+              "key2": "value2"
+            },
+            "extId": "abc"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ifa": "hello-ads",
+      "devicetype": 4,
+      "connectiontype": 6,
+      "geo": {
+        "lat": 51.499488,
+        "lon": -0.128953
+      },
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
+      "ip": "169.254.13.37",
+      "h": 1098,
+      "w": 814
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 1
+      }
+    },
+    "site": {
+      "id": "fake-site-id",
+      "publisher": {
+        "id": "1"
+      },
+      "page": "http://localhost:9090/gdpr.html"
+    },
+    "user": {
+      "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c",
+      "ext": {
+        "consent": "BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Cookie": [
+            "id=34a53e82-0dc3-4815-8b7e-b725ede0361c"
+          ],
+          "Referer": [
+            "http://localhost:9090/gdpr.html"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "169.254.13.37"
+          ]
+        },
+        "uri": "https://ad.yieldlab.net/testing/12345?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&content=json&gdpr=1&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "id": 12345,
+            "price": 201,
+            "advertiser": "yieldlab",
+            "adsize": "728x90",
+            "pid": 1234,
+            "did": 5678,
+            "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5"
+          }
+        ]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "adm": "<script src=\"https://ad.yieldlab.net/d/12345/123456789/728x90?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&gdpr=1&id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing\"></script>",
+            "crid": "12345123433",
+            "dealid": "1234",
+            "id": "12345",
+            "impid": "test-imp-id",
+            "price": 2.01,
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/video.json b/adapters/yieldlab/yieldlabtest/exemplary/video.json
new file mode 100644
index 00000000..9e970ae7
--- /dev/null
+++ b/adapters/yieldlab/yieldlabtest/exemplary/video.json
@@ -0,0 +1,136 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 728,
+              "h": 90
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adslotId": "12345",
+            "supplyId": "123456789",
+            "adSize": "728x90",
+            "targeting": {
+              "key1": "value1",
+              "key2": "value2"
+            },
+            "extId": "abc"
+          }
+        },
+        "video": {
+          "context": "instream",
+          "mimes": [
+            "video/mp4"
+          ],
+          "playerSize": [
+            [
+              400,
+              600
+            ]
+          ],
+          "minduration": 1,
+          "maxduration": 2,
+          "protocols": [
+            1,
+            2
+          ],
+          "w": 1,
+          "h": 2,
+          "startdelay": 1,
+          "placement": 1,
+          "playbackmethod": [
+            2
+          ]
+        }
+      }
+    ],
+    "user": {
+      "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c"
+    },
+    "device": {
+      "ifa": "hello-ads",
+      "devicetype": 4,
+      "connectiontype": 6,
+      "geo": {
+        "lat": 51.499488,
+        "lon": -0.128953
+      },
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
+      "ip": "169.254.13.37",
+      "h": 1098,
+      "w": 814
+    },
+    "site": {
+      "id": "fake-site-id",
+      "publisher": {
+        "id": "1"
+      },
+      "page": "http://localhost:9090/gdpr.html"
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Cookie": [
+            "id=34a53e82-0dc3-4815-8b7e-b725ede0361c"
+          ],
+          "Referer": [
+            "http://localhost:9090/gdpr.html"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "169.254.13.37"
+          ]
+        },
+        "uri": "https://ad.yieldlab.net/testing/12345?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "id": 12345,
+            "price": 201,
+            "advertiser": "yieldlab",
+            "adsize": "728x90",
+            "pid": 1234,
+            "did": 5678,
+            "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5"
+          }
+        ]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "crid": "12345123433",
+            "dealid": "1234",
+            "id": "12345",
+            "impid": "test-imp-id",
+            "nurl": "https://ad.yieldlab.net/d/12345/123456789/728x90?id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing",
+            "price": 2.01,
+            "w": 728,
+            "h": 90
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/video_app.json b/adapters/yieldlab/yieldlabtest/exemplary/video_app.json
new file mode 100644
index 00000000..67d526b3
--- /dev/null
+++ b/adapters/yieldlab/yieldlabtest/exemplary/video_app.json
@@ -0,0 +1,136 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 728,
+              "h": 90
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adslotId": "12345",
+            "supplyId": "123456789",
+            "adSize": "728x90",
+            "targeting": {
+              "key1": "value1",
+              "key2": "value2"
+            },
+            "extId": "abc"
+          }
+        },
+        "video": {
+          "context": "instream",
+          "mimes": [
+            "video/mp4"
+          ],
+          "playerSize": [
+            [
+              400,
+              600
+            ]
+          ],
+          "minduration": 1,
+          "maxduration": 2,
+          "protocols": [
+            1,
+            2
+          ],
+          "w": 1,
+          "h": 2,
+          "startdelay": 1,
+          "placement": 1,
+          "playbackmethod": [
+            2
+          ]
+        }
+      }
+    ],
+    "user": {
+      "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "device": {
+      "ifa": "hello-ads",
+      "devicetype": 4,
+      "connectiontype": 6,
+      "geo": {
+        "lat": 51.499488,
+        "lon": -0.128953
+      },
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
+      "ip": "169.254.13.37",
+      "h": 1098,
+      "w": 814
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Cookie": [
+            "id=34a53e82-0dc3-4815-8b7e-b725ede0361c"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "169.254.13.37"
+          ]
+        },
+        "uri": "https://ad.yieldlab.net/testing/12345?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pubappname=Awesome+App&pubbundlename=com.app.awesome&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "id": 12345,
+            "price": 201,
+            "advertiser": "yieldlab",
+            "adsize": "728x90",
+            "pid": 1234,
+            "did": 5678,
+            "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5"
+          }
+        ]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "crid": "12345123433",
+            "dealid": "1234",
+            "id": "12345",
+            "impid": "test-imp-id",
+            "nurl": "https://ad.yieldlab.net/d/12345/123456789/728x90?id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing",
+            "price": 2.01,
+            "w": 728,
+            "h": 90
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go
index 3035a6d4..5e9e9639 100644
--- a/endpoints/auction_test.go
+++ b/endpoints/auction_test.go
@@ -407,6 +407,7 @@ type auctionMockPermissions struct {
 	allowBidderSync  bool
 	allowHostCookies bool
 	allowPI          bool
+	allowGeo         bool
 }
 
 func (m *auctionMockPermissions) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) {
@@ -417,8 +418,8 @@ func (m *auctionMockPermissions) BidderSyncAllowed(ctx context.Context, bidder o
 	return m.allowBidderSync, nil
 }
 
-func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) {
-	return m.allowPI, nil
+func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) {
+	return m.allowPI, m.allowGeo, nil
 }
 
 func (m *auctionMockPermissions) AMPException() bool {
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index bb766aa9..824e32f1 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -254,8 +254,8 @@ func (g *gdprPerms) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.Bi
 	return ok, nil
 }
 
-func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) {
-	return true, nil
+func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) {
+	return true, true, nil
 }
 
 func (g *gdprPerms) AMPException() bool {
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 8499ac1c..3f47b257 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -437,8 +437,8 @@ func (g *mockPermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	return false, nil
 }
 
-func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) {
-	return g.allowPI, nil
+func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) {
+	return g.allowPI, g.allowPI, nil
 }
 
 func (g *mockPermsSetUID) AMPException() bool {
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 53d6b85c..acbf25ff 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -24,11 +24,11 @@ func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	return true, nil
 }
 
-func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) {
+func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) {
 	if bidder == "appnexus" {
-		return true, nil
+		return true, true, nil
 	}
-	return false, nil
+	return false, false, nil
 }
 
 func (p *permissionsMock) AMPException() bool {
diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go
index 8b89577d..f05f25e8 100644
--- a/gdpr/impl_test.go
+++ b/gdpr/impl_test.go
@@ -10,6 +10,9 @@ import (
 	"github.com/prebid/prebid-server/openrtb_ext"
 
 	"github.com/prebid/go-gdpr/vendorlist"
+	"github.com/prebid/go-gdpr/vendorlist2"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestNoConsentButAllowByDefault(t *testing.T) {
@@ -55,10 +58,10 @@ func TestNoConsentAndRejectByDefault(t *testing.T) {
 func TestAllowedSyncs(t *testing.T) {
 	vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{
 		2: {
-			purposes: []uint8{1},
+			purposes: []int{1},
 		},
 		3: {
-			purposes: []uint8{1},
+			purposes: []int{1},
 		},
 	})
 	perms := permissionsImpl{
@@ -91,10 +94,10 @@ func TestAllowedSyncs(t *testing.T) {
 func TestProhibitedPurposes(t *testing.T) {
 	vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{
 		2: {
-			purposes: []uint8{1}, // cookie reads/writes
+			purposes: []int{1}, // cookie reads/writes
 		},
 		3: {
-			purposes: []uint8{3}, // ad personalization
+			purposes: []int{3}, // ad personalization
 		},
 	})
 	perms := permissionsImpl{
@@ -127,10 +130,10 @@ func TestProhibitedPurposes(t *testing.T) {
 func TestProhibitedVendors(t *testing.T) {
 	vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{
 		2: {
-			purposes: []uint8{1}, // cookie reads/writes
+			purposes: []int{1}, // cookie reads/writes
 		},
 		3: {
-			purposes: []uint8{3}, // ad personalization
+			purposes: []int{3}, // ad personalization
 		},
 	})
 	perms := permissionsImpl{
@@ -179,10 +182,10 @@ func TestMalformedConsent(t *testing.T) {
 func TestAllowPersonalInfo(t *testing.T) {
 	vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{
 		2: {
-			purposes: []uint8{1}, // cookie reads/writes
+			purposes: []int{1}, // cookie reads/writes
 		},
 		3: {
-			purposes: []uint8{1, 3}, // ad personalization
+			purposes: []int{1, 3}, // ad personalization
 		},
 	})
 	perms := permissionsImpl{
@@ -204,21 +207,377 @@ func TestAllowPersonalInfo(t *testing.T) {
 	}
 
 	// PI needs both purposes to succeed
-	allowPI, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA")
+	allowPI, _, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA")
 	assertNilErr(t, err)
 	assertBoolsEqual(t, false, allowPI)
 
-	allowPI, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderPubmatic, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA")
+	allowPI, _, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderPubmatic, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA")
 	assertNilErr(t, err)
 	assertBoolsEqual(t, true, allowPI)
 
 	// Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array
 	perms.cfg.NonStandardPublisherMap = map[string]int{"appNexusAppID": 1}
-	allowPI, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA")
+	allowPI, _, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA")
 	assertNilErr(t, err)
 	assertBoolsEqual(t, true, allowPI)
 }
 
+var tcf2BasicPurposes = map[uint16]*purposes{
+	2:  {purposes: []int{1}},       //cookie reads/writes
+	6:  {purposes: []int{1, 2, 4}}, // ad personalization
+	8:  {purposes: []int{1, 7}},
+	10: {purposes: []int{2, 4, 7}},
+	32: {purposes: []int{1, 2, 4, 7}},
+}
+var tcf2LegitInterests = map[uint16]*purposes{
+	6: {purposes: []int{7}},
+	8: {purposes: []int{2, 4}},
+}
+var tcf2SpecialPuproses = map[uint16]*purposes{
+	6:  {purposes: []int{1}},
+	10: {purposes: []int{1}},
+}
+var tcf2FlexPurposes = map[uint16]*purposes{
+	6: {purposes: []int{1, 2, 4, 7}},
+}
+var tcf2Config = config.GDPR{
+	HostVendorID: 2,
+	TCF2: config.TCF2{
+		Enabled:         true,
+		Purpose1:        config.PurposeDetail{Enabled: true},
+		Purpose2:        config.PurposeDetail{Enabled: true},
+		Purpose7:        config.PurposeDetail{Enabled: true},
+		SpecialPurpose1: config.PurposeDetail{Enabled: true},
+	},
+}
+
+type tcf2TestDef struct {
+	description string
+	bidder      openrtb_ext.BidderName
+	consent     string
+	allowPI     bool
+	allowGeo    bool
+}
+
+func TestAllowPersonalInfoTCF2(t *testing.T) {
+	vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses)
+	perms := permissionsImpl{
+		cfg: tcf2Config,
+		vendorIDs: map[openrtb_ext.BidderName]uint16{
+			openrtb_ext.BidderAppnexus: 2,
+			openrtb_ext.BidderPubmatic: 6,
+			openrtb_ext.BidderRubicon:  8,
+		},
+		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
+			tCF1: nil,
+			tCF2: listFetcher(map[uint16]vendorlist.VendorList{
+				34: parseVendorListDataV2(t, vendorListData),
+			}),
+		},
+	}
+
+	// COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 6, 8
+	// PI needs all purposes to succeed
+	testDefs := []tcf2TestDef{
+		{
+			description: "Appnexus vendor test, insufficient purposes claimed",
+			bidder:      openrtb_ext.BidderAppnexus,
+			consent:     "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA",
+			allowPI:     false,
+			allowGeo:    false,
+		},
+		{
+			description: "Pubmatic vendor test, flex purposes claimed",
+			bidder:      openrtb_ext.BidderPubmatic,
+			consent:     "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA",
+			allowPI:     true,
+			allowGeo:    true,
+		},
+		{
+			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
+			bidder:      openrtb_ext.BidderRubicon,
+			consent:     "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA",
+			allowPI:     true,
+			allowGeo:    false,
+		},
+	}
+
+	for _, td := range testDefs {
+		allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent)
+		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
+		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
+		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
+	}
+}
+
+func TestAllowPersonalInfoWhitelistTCF2(t *testing.T) {
+	vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses)
+	perms := permissionsImpl{
+		cfg: tcf2Config,
+		vendorIDs: map[openrtb_ext.BidderName]uint16{
+			openrtb_ext.BidderAppnexus: 2,
+			openrtb_ext.BidderPubmatic: 6,
+			openrtb_ext.BidderRubicon:  8,
+		},
+		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
+			tCF1: nil,
+			tCF2: listFetcher(map[uint16]vendorlist.VendorList{
+				34: parseVendorListDataV2(t, vendorListData),
+			}),
+		},
+	}
+	// Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array
+	perms.cfg.NonStandardPublisherMap = map[string]int{"appNexusAppID": 1}
+	allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed")
+	assert.EqualValuesf(t, true, allowPI, "AllowPI failure")
+	assert.EqualValuesf(t, true, allowGeo, "AllowGeo failure")
+
+}
+
+func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) {
+	vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses)
+	perms := permissionsImpl{
+		cfg: tcf2Config,
+		vendorIDs: map[openrtb_ext.BidderName]uint16{
+			openrtb_ext.BidderAppnexus: 2,
+			openrtb_ext.BidderPubmatic: 32,
+			openrtb_ext.BidderRubicon:  8,
+		},
+		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
+			tCF1: nil,
+			tCF2: listFetcher(map[uint16]vendorlist.VendorList{
+				15: parseVendorListDataV2(t, vendorListData),
+			}),
+		},
+	}
+
+	// COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA - vendors 1-10 legit interest only,
+	// Pub restriction on purpose 7, consent only ... no allowPI will pass, no Special purpose 1 consent
+	testDefs := []tcf2TestDef{
+		{
+			description: "Appnexus vendor test, insufficient purposes claimed",
+			bidder:      openrtb_ext.BidderAppnexus,
+			consent:     "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA",
+			allowPI:     false,
+			allowGeo:    false,
+		},
+		{
+			description: "Pubmatic vendor test, flex purposes claimed",
+			bidder:      openrtb_ext.BidderPubmatic,
+			consent:     "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA",
+			allowPI:     false,
+			allowGeo:    false,
+		},
+		{
+			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
+			bidder:      openrtb_ext.BidderRubicon,
+			consent:     "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA",
+			allowPI:     false,
+			allowGeo:    false,
+		},
+	}
+
+	for _, td := range testDefs {
+		allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent)
+		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
+		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
+		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
+	}
+}
+
+func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) {
+	vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses)
+	perms := permissionsImpl{
+		cfg: tcf2Config,
+		vendorIDs: map[openrtb_ext.BidderName]uint16{
+			openrtb_ext.BidderAppnexus: 2,
+			openrtb_ext.BidderPubmatic: 10,
+			openrtb_ext.BidderRubicon:  8,
+		},
+		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
+			tCF1: nil,
+			tCF2: listFetcher(map[uint16]vendorlist.VendorList{
+				34: parseVendorListDataV2(t, vendorListData),
+			}),
+		},
+	}
+	perms.cfg.TCF2.PurposeOneTreatment.Enabled = true
+	perms.cfg.TCF2.PurposeOneTreatment.AccessAllowed = true
+
+	// COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA Purpose one flag set
+	testDefs := []tcf2TestDef{
+		{
+			description: "Appnexus vendor test, insufficient purposes claimed",
+			bidder:      openrtb_ext.BidderAppnexus,
+			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
+			allowPI:     false,
+			allowGeo:    false,
+		},
+		{
+			description: "Pubmatic vendor test, flex purposes claimed",
+			bidder:      openrtb_ext.BidderPubmatic,
+			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
+			allowPI:     true,
+			allowGeo:    true,
+		},
+		{
+			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
+			bidder:      openrtb_ext.BidderRubicon,
+			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
+			allowPI:     true,
+			allowGeo:    false,
+		},
+	}
+
+	for _, td := range testDefs {
+		allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent)
+		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
+		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
+		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
+	}
+}
+
+func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) {
+	vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses)
+	perms := permissionsImpl{
+		cfg: tcf2Config,
+		vendorIDs: map[openrtb_ext.BidderName]uint16{
+			openrtb_ext.BidderAppnexus: 2,
+			openrtb_ext.BidderPubmatic: 10,
+			openrtb_ext.BidderRubicon:  8,
+		},
+		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
+			tCF1: nil,
+			tCF2: listFetcher(map[uint16]vendorlist.VendorList{
+				34: parseVendorListDataV2(t, vendorListData),
+			}),
+		},
+	}
+	perms.cfg.TCF2.PurposeOneTreatment.Enabled = true
+	perms.cfg.TCF2.PurposeOneTreatment.AccessAllowed = false
+
+	// COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA Purpose one flag set
+	testDefs := []tcf2TestDef{
+		{
+			description: "Appnexus vendor test, insufficient purposes claimed",
+			bidder:      openrtb_ext.BidderAppnexus,
+			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
+			allowPI:     false,
+			allowGeo:    false,
+		},
+		{
+			description: "Pubmatic vendor test, flex purposes claimed",
+			bidder:      openrtb_ext.BidderPubmatic,
+			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
+			allowPI:     false,
+			allowGeo:    true,
+		},
+		{
+			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
+			bidder:      openrtb_ext.BidderRubicon,
+			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
+			allowPI:     false,
+			allowGeo:    false,
+		},
+	}
+
+	for _, td := range testDefs {
+		allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent)
+		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
+		assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description)
+		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
+	}
+}
+
+func TestAllowSyncTCF2(t *testing.T) {
+	vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses)
+	perms := permissionsImpl{
+		cfg: tcf2Config,
+		vendorIDs: map[openrtb_ext.BidderName]uint16{
+			openrtb_ext.BidderAppnexus: 2,
+			openrtb_ext.BidderPubmatic: 6,
+			openrtb_ext.BidderRubicon:  8,
+		},
+		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
+			tCF1: nil,
+			tCF2: listFetcher(map[uint16]vendorlist.VendorList{
+				34: parseVendorListDataV2(t, vendorListData),
+			}),
+		},
+	}
+
+	// COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 6, 8
+	allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	assert.NoErrorf(t, err, "Error processing HostCookiesAllowed")
+	assert.EqualValuesf(t, true, allowSync, "HostCookiesAllowed failure")
+
+	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	assert.NoErrorf(t, err, "Error processing BidderSyncAllowed")
+	assert.EqualValuesf(t, true, allowSync, "BidderSyncAllowed failure")
+}
+
+func TestProhibitedPurposeSyncTCF2(t *testing.T) {
+	basicPurposes := tcf2BasicPurposes
+	basicPurposes[8] = &purposes{purposes: []int{7}}
+	vendorListData := mockVendorListDataTCF2(t, 2, basicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses)
+	perms := permissionsImpl{
+		cfg: tcf2Config,
+		vendorIDs: map[openrtb_ext.BidderName]uint16{
+			openrtb_ext.BidderAppnexus: 2,
+			openrtb_ext.BidderPubmatic: 6,
+			openrtb_ext.BidderRubicon:  8,
+		},
+		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
+			tCF1: nil,
+			tCF2: listFetcher(map[uint16]vendorlist.VendorList{
+				34: parseVendorListDataV2(t, vendorListData),
+			}),
+		},
+	}
+	perms.cfg.HostVendorID = 8
+
+	// COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 6, 8
+	allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	assert.NoErrorf(t, err, "Error processing HostCookiesAllowed")
+	assert.EqualValuesf(t, false, allowSync, "HostCookiesAllowed failure")
+
+	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	assert.NoErrorf(t, err, "Error processing BidderSyncAllowed")
+	assert.EqualValuesf(t, false, allowSync, "BidderSyncAllowed failure")
+}
+
+func TestProhibitedVendorSyncTCF2(t *testing.T) {
+	basicPurposes := tcf2BasicPurposes
+	basicPurposes[10] = &purposes{purposes: []int{1}}
+	vendorListData := mockVendorListDataTCF2(t, 2, basicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses)
+	perms := permissionsImpl{
+		cfg: tcf2Config,
+		vendorIDs: map[openrtb_ext.BidderName]uint16{
+			openrtb_ext.BidderAppnexus: 2,
+			openrtb_ext.BidderPubmatic: 6,
+			openrtb_ext.BidderRubicon:  8,
+			openrtb_ext.BidderOpenx:    10,
+		},
+		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
+			tCF1: nil,
+			tCF2: listFetcher(map[uint16]vendorlist.VendorList{
+				34: parseVendorListDataV2(t, vendorListData),
+			}),
+		},
+	}
+	perms.cfg.HostVendorID = 10
+
+	// COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 4, 6
+	allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	assert.NoErrorf(t, err, "Error processing HostCookiesAllowed")
+	assert.EqualValuesf(t, false, allowSync, "HostCookiesAllowed failure")
+
+	allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA")
+	assert.NoErrorf(t, err, "Error processing BidderSyncAllowed")
+	assert.EqualValuesf(t, false, allowSync, "BidderSyncAllowed failure")
+}
+
 func parseVendorListData(t *testing.T, data string) vendorlist.VendorList {
 	t.Helper()
 	parsed, err := vendorlist.ParseEagerly([]byte(data))
@@ -228,6 +587,15 @@ func parseVendorListData(t *testing.T, data string) vendorlist.VendorList {
 	return parsed
 }
 
+func parseVendorListDataV2(t *testing.T, data string) vendorlist.VendorList {
+	t.Helper()
+	parsed, err := vendorlist2.ParseEagerly([]byte(data))
+	if err != nil {
+		t.Fatalf("Failed to parse vendor list data. %v", err)
+	}
+	return parsed
+}
+
 func listFetcher(lists map[uint16]vendorlist.VendorList) func(context.Context, uint16) (vendorlist.VendorList, error) {
 	return func(ctx context.Context, id uint16) (vendorlist.VendorList, error) {
 		data, ok := lists[id]
diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go
index 8197fa26..824f9178 100644
--- a/gdpr/vendorlist-fetching_test.go
+++ b/gdpr/vendorlist-fetching_test.go
@@ -15,12 +15,12 @@ import (
 func TestVendorFetch(t *testing.T) {
 	vendorListOne := mockVendorListData(t, 1, map[uint16]*purposes{
 		32: {
-			purposes: []uint8{1, 2},
+			purposes: []int{1, 2},
 		},
 	})
 	vendorListTwo := mockVendorListData(t, 2, map[uint16]*purposes{
 		32: {
-			purposes: []uint8{1, 2, 3},
+			purposes: []int{1, 2, 3},
 		},
 	})
 	server := httptest.NewServer(http.HandlerFunc(mockServer(2, map[int]string{
@@ -47,12 +47,12 @@ func TestVendorFetch(t *testing.T) {
 func TestLazyFetch(t *testing.T) {
 	firstVendorList := mockVendorListData(t, 1, map[uint16]*purposes{
 		32: {
-			purposes: []uint8{1, 2},
+			purposes: []int{1, 2},
 		},
 	})
 	secondVendorList := mockVendorListData(t, 2, map[uint16]*purposes{
 		3: {
-			purposes: []uint8{1},
+			purposes: []int{1},
 		},
 	})
 	server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{
@@ -73,7 +73,7 @@ func TestLazyFetch(t *testing.T) {
 func TestInitialTimeout(t *testing.T) {
 	list := mockVendorListData(t, 1, map[uint16]*purposes{
 		32: {
-			purposes: []uint8{1, 2},
+			purposes: []int{1, 2},
 		},
 	})
 	server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{
@@ -91,12 +91,12 @@ func TestInitialTimeout(t *testing.T) {
 func TestFetchThrottling(t *testing.T) {
 	vendorListTwo := mockVendorListData(t, 2, map[uint16]*purposes{
 		32: {
-			purposes: []uint8{1, 2},
+			purposes: []int{1, 2},
 		},
 	})
 	vendorListThree := mockVendorListData(t, 3, map[uint16]*purposes{
 		32: {
-			purposes: []uint8{1, 2},
+			purposes: []int{1, 2},
 		},
 	})
 	server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{
@@ -174,8 +174,8 @@ func mockServer(latestVersion int, responses map[int]string) func(http.ResponseW
 
 func mockVendorListData(t *testing.T, version uint16, vendors map[uint16]*purposes) string {
 	type vendorContract struct {
-		ID       uint16  `json:"id"`
-		Purposes []uint8 `json:"purposeIds"`
+		ID       uint16 `json:"id"`
+		Purposes []int  `json:"purposeIds"`
 	}
 
 	type vendorListContract struct {
@@ -203,6 +203,72 @@ func mockVendorListData(t *testing.T, version uint16, vendors map[uint16]*purpos
 	return string(data)
 }
 
+type purposeMap map[uint16]*purposes
+
+func mockVendorListDataTCF2(t *testing.T, version uint16, basicPurposes purposeMap, legitInterests purposeMap, flexPurposes purposeMap, specialPurposes purposeMap) string {
+	type vendorContract struct {
+		ID               uint16 `json:"id"`
+		Purposes         []int  `json:"purposes"`
+		LegIntPurposes   []int  `json:"legIntPurposes"`
+		FlexiblePurposes []int  `json:"flexiblePurposes"`
+		SpecialPurposes  []int  `json:"specialPurposes"`
+	}
+
+	type vendorListContract struct {
+		Version uint16                    `json:"vendorListVersion"`
+		Vendors map[string]vendorContract `json:"vendors"`
+	}
+
+	vendors := make(map[string]vendorContract, len(basicPurposes))
+	for id, purpose := range basicPurposes {
+		sid := strconv.Itoa(int(id))
+		vendor, ok := vendors[sid]
+		if !ok {
+			vendor = vendorContract{ID: id}
+		}
+		vendor.Purposes = purpose.purposes
+		vendors[sid] = vendor
+	}
+
+	for id, purpose := range legitInterests {
+		sid := strconv.Itoa(int(id))
+		vendor, ok := vendors[sid]
+		if !ok {
+			vendor = vendorContract{ID: id}
+		}
+		vendor.LegIntPurposes = purpose.purposes
+		vendors[sid] = vendor
+	}
+
+	for id, purpose := range flexPurposes {
+		sid := strconv.Itoa(int(id))
+		vendor, ok := vendors[sid]
+		if !ok {
+			vendor = vendorContract{ID: id}
+		}
+		vendor.FlexiblePurposes = purpose.purposes
+		vendors[sid] = vendor
+	}
+
+	for id, purpose := range specialPurposes {
+		sid := strconv.Itoa(int(id))
+		vendor, ok := vendors[sid]
+		if !ok {
+			vendor = vendorContract{ID: id}
+		}
+		vendor.SpecialPurposes = purpose.purposes
+		vendors[sid] = vendor
+	}
+
+	obj := vendorListContract{
+		Version: version,
+		Vendors: vendors,
+	}
+	data, err := json.Marshal(obj)
+	assertNilErr(t, err)
+	return string(data)
+}
+
 func testURLMaker(server *httptest.Server) func(uint16, uint8) string {
 	url := server.URL
 	return func(version uint16, TCFVer uint8) string {
@@ -220,5 +286,5 @@ func testConfig() config.GDPR {
 }
 
 type purposes struct {
-	purposes []uint8
+	purposes []int
 }
diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go
index 25e08b5e..0e82648d 100644
--- a/privacy/enforcement_test.go
+++ b/privacy/enforcement_test.go
@@ -17,27 +17,40 @@ func TestAny(t *testing.T) {
 		{
 			description: "All False",
 			enforcement: Enforcement{
-				CCPA:  false,
-				COPPA: false,
-				GDPR:  false,
+				CCPA:    false,
+				COPPA:   false,
+				GDPR:    false,
+				GDPRGeo: false,
 			},
 			expected: false,
 		},
 		{
 			description: "All True",
 			enforcement: Enforcement{
-				CCPA:  true,
-				COPPA: true,
-				GDPR:  true,
+				CCPA:    true,
+				COPPA:   true,
+				GDPR:    true,
+				GDPRGeo: true,
 			},
 			expected: true,
 		},
 		{
 			description: "Mixed",
 			enforcement: Enforcement{
-				CCPA:  false,
-				COPPA: true,
-				GDPR:  false,
+				CCPA:    false,
+				COPPA:   true,
+				GDPR:    false,
+				GDPRGeo: false,
+			},
+			expected: true,
+		},
+		{
+			description: "GDPRGeo only",
+			enforcement: Enforcement{
+				CCPA:    false,
+				COPPA:   false,
+				GDPR:    false,
+				GDPRGeo: true,
 			},
 			expected: true,
 		},
@@ -62,9 +75,10 @@ func TestApply(t *testing.T) {
 		{
 			description: "All Enforced",
 			enforcement: Enforcement{
-				CCPA:  true,
-				COPPA: true,
-				GDPR:  true,
+				CCPA:    true,
+				COPPA:   true,
+				GDPR:    true,
+				GDPRGeo: true,
 			},
 			ampGDPRException:   false,
 			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
@@ -75,9 +89,10 @@ func TestApply(t *testing.T) {
 		{
 			description: "CCPA Only",
 			enforcement: Enforcement{
-				CCPA:  true,
-				COPPA: false,
-				GDPR:  false,
+				CCPA:    true,
+				COPPA:   false,
+				GDPR:    false,
+				GDPRGeo: false,
 			},
 			ampGDPRException:   false,
 			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
@@ -88,9 +103,10 @@ func TestApply(t *testing.T) {
 		{
 			description: "COPPA Only",
 			enforcement: Enforcement{
-				CCPA:  false,
-				COPPA: true,
-				GDPR:  false,
+				CCPA:    false,
+				COPPA:   true,
+				GDPR:    false,
+				GDPRGeo: false,
 			},
 			ampGDPRException:   false,
 			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
@@ -101,9 +117,10 @@ func TestApply(t *testing.T) {
 		{
 			description: "GDPR Only",
 			enforcement: Enforcement{
-				CCPA:  false,
-				COPPA: false,
-				GDPR:  true,
+				CCPA:    false,
+				COPPA:   false,
+				GDPR:    true,
+				GDPRGeo: true,
 			},
 			ampGDPRException:   false,
 			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
@@ -114,9 +131,10 @@ func TestApply(t *testing.T) {
 		{
 			description: "GDPR Only, ampGDPRException",
 			enforcement: Enforcement{
-				CCPA:  false,
-				COPPA: false,
-				GDPR:  true,
+				CCPA:    false,
+				COPPA:   false,
+				GDPR:    true,
+				GDPRGeo: true,
 			},
 			ampGDPRException:   true,
 			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
@@ -127,9 +145,10 @@ func TestApply(t *testing.T) {
 		{
 			description: "CCPA Only, ampGDPRException",
 			enforcement: Enforcement{
-				CCPA:  true,
-				COPPA: false,
-				GDPR:  false,
+				CCPA:    true,
+				COPPA:   false,
+				GDPR:    false,
+				GDPRGeo: false,
 			},
 			ampGDPRException:   true,
 			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
@@ -140,9 +159,10 @@ func TestApply(t *testing.T) {
 		{
 			description: "COPPA and GDPR, ampGDPRException",
 			enforcement: Enforcement{
-				CCPA:  false,
-				COPPA: true,
-				GDPR:  true,
+				CCPA:    false,
+				COPPA:   true,
+				GDPR:    true,
+				GDPRGeo: true,
 			},
 			ampGDPRException:   true,
 			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
@@ -150,6 +170,34 @@ func TestApply(t *testing.T) {
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoFull,
 		},
+		{
+			description: "GDPR Only, no Geo",
+			enforcement: Enforcement{
+				CCPA:    false,
+				COPPA:   false,
+				GDPR:    true,
+				GDPRGeo: false,
+			},
+			ampGDPRException:   false,
+			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceGeo:  ScrubStrategyGeoNone,
+			expectedUser:       ScrubStrategyUserID,
+			expectedUserGeo:    ScrubStrategyGeoNone,
+		},
+		{
+			description: "GDPR Only, Geo only",
+			enforcement: Enforcement{
+				CCPA:    false,
+				COPPA:   false,
+				GDPR:    false,
+				GDPRGeo: true,
+			},
+			ampGDPRException:   false,
+			expectedDeviceIPv6: ScrubStrategyIPV6None,
+			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
+			expectedUser:       ScrubStrategyUserNone,
+			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
+		},
 	}
 
 	for _, test := range testCases {
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 20fce80c..ddd067e8 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -32,6 +32,7 @@ func TestNewSyncerMap(t *testing.T) {
 			string(openrtb_ext.BidderConversant):       syncConfig,
 			string(openrtb_ext.BidderCpmstar):          syncConfig,
 			string(openrtb_ext.BidderDatablocks):       syncConfig,
+			string(openrtb_ext.BidderDmx):              syncConfig,
 			string(openrtb_ext.BidderEmxDigital):       syncConfig,
 			string(openrtb_ext.BidderEngageBDR):        syncConfig,
 			string(openrtb_ext.BidderEPlanning):        syncConfig,
@@ -67,6 +68,7 @@ func TestNewSyncerMap(t *testing.T) {
 			string(openrtb_ext.BidderUcfunnel):         syncConfig,
 			string(openrtb_ext.BidderUnruly):           syncConfig,
 			string(openrtb_ext.BidderValueImpression):  syncConfig,
+			string(openrtb_ext.BidderYieldlab):         syncConfig,
 			string(openrtb_ext.BidderVerizonMedia):     syncConfig,
 			string(openrtb_ext.BidderVisx):             syncConfig,
 			string(openrtb_ext.BidderVrtcal):           syncConfig,