Repository: prebid/prebid-server
Pull request #1680 (2021-01-26 13:02:54)

New Adapter: Epom

epomrnd: PR into prebid documentation project is here:
https://github.com/prebid/prebid.github.io/pull/2667

Patch:
diff --git a/adapters/bidder.go b/adapters/bidder.go
index 507a065b..9800a49c 100644
--- a/adapters/bidder.go
+++ b/adapters/bidder.go
@@ -7,7 +7,7 @@ import (
 
 	"github.com/mxmCherry/openrtb"
 	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
@@ -53,12 +53,6 @@ type TimeoutBidder interface {
 	MakeTimeoutNotification(req *RequestData) (*RequestData, []error)
 }
 
-func BadInput(msg string) *errortypes.BadInput {
-	return &errortypes.BadInput{
-		Message: msg,
-	}
-}
-
 // BidderResponse wraps the server's response with the list of bids and the currency used by the bidder.
 //
 // Currency declaration is not mandatory but helps to detect an eventual currency mismatch issue.
@@ -141,4 +135,8 @@ func (r *RequestData) SetBasicAuth(username string, password string) {
 	r.Headers.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(username+":"+password)))
 }
 
+type ExtraRequestInfo struct {
+	PbsEntryPoint metrics.RequestType
+}
+
 type Builder func(openrtb_ext.BidderName, config.Adapter) (Bidder, error)
diff --git a/adapters/epom/epom.go b/adapters/epom/epom.go
new file mode 100644
index 00000000..4cb9a364
--- /dev/null
+++ b/adapters/epom/epom.go
@@ -0,0 +1,127 @@
+package epom
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	endpoint string
+}
+
+// Builder builds a new instance of the Epom adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	bidder := &adapter{
+		endpoint: config.Endpoint,
+	}
+	return bidder, nil
+}
+
+func (a *adapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) (requests []*adapters.RequestData, errors []error) {
+	rq, errs := a.makeRequest(request)
+
+	if len(errs) > 0 {
+		return nil, errs
+	}
+
+	if rq != nil {
+		requests = append(requests, rq)
+	}
+
+	return requests, nil
+}
+
+func (a *adapter) makeRequest(request *openrtb.BidRequest) (*adapters.RequestData, []error) {
+	if request.Device == nil || request.Device.IP == "" {
+		return nil, []error{&errortypes.BadInput{
+			Message: "ipv4 address is required field",
+		}}
+	}
+
+	reqJSON, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	return &adapters.RequestData{
+		Method:  "POST",
+		Uri:     a.endpoint,
+		Body:    reqJSON,
+		Headers: headers,
+	}, nil
+}
+
+func (a *adapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if response.StatusCode >= http.StatusInternalServerError {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Dsp server internal error", response.StatusCode),
+		}}
+	}
+
+	if response.StatusCode >= http.StatusBadRequest {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: %d. Bad request to dsp", response.StatusCode),
+		}}
+	}
+
+	if response.StatusCode != http.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d", response.StatusCode),
+		}}
+	}
+
+	var bidResp openrtb.BidResponse
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+
+	//additional no content check
+	if len(bidResp.SeatBid) == 0 || len(bidResp.SeatBid[0].Bid) == 0 {
+		return nil, []error{&errortypes.Warning{
+			Message: "No bids in response",
+		}}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid))
+
+	var errs []error
+	for _, seatBid := range bidResp.SeatBid {
+		for _, bid := range seatBid.Bid {
+			b := &adapters.TypedBid{
+				Bid:     &bid,
+				BidType: getMediaTypeForImp(bid.ImpID, internalRequest.Imp),
+			}
+			bidResponse.Bids = append(bidResponse.Bids, b)
+		}
+	}
+	return bidResponse, errs
+}
+
+func getMediaTypeForImp(impID string, imps []openrtb.Imp) openrtb_ext.BidType {
+	for _, imp := range imps {
+		if imp.ID == impID {
+			if imp.Banner != nil {
+				return openrtb_ext.BidTypeBanner
+			} else if imp.Video != nil {
+				return openrtb_ext.BidTypeVideo
+			} else if imp.Native != nil {
+				return openrtb_ext.BidTypeNative
+			}
+		}
+	}
+	return openrtb_ext.BidTypeBanner
+}
diff --git a/adapters/info.go b/adapters/info.go
deleted file mode 100644
index 19897ac7..00000000
--- a/adapters/info.go
+++ /dev/null
@@ -1,267 +0,0 @@
-package adapters
-
-import (
-	"fmt"
-	"io/ioutil"
-	"strings"
-
-	"github.com/golang/glog"
-	"github.com/mxmCherry/openrtb"
-	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/metrics"
-	"github.com/prebid/prebid-server/openrtb_ext"
-	yaml "gopkg.in/yaml.v2"
-)
-
-// EnforceBidderInfo decorates the input Bidder by making sure that all the requests
-// to it are in sync with its static/bidder-info/{bidder}.yaml file.
-//
-// It adjusts incoming requests in the following ways:
-//   1. If App or Site traffic is not supported by the info file, then requests from
-//      those sources will be rejected before the delegate is called.
-//   2. If a given MediaType is not supported for the platform, then it will be set
-//      to nil before the request is forwarded to the delegate.
-//   3. Any Imps which have no MediaTypes left will be removed.
-//   4. If there are no valid Imps left, the delegate won't be called at all.
-func EnforceBidderInfo(bidder Bidder, info BidderInfo) Bidder {
-	return &InfoAwareBidder{
-		Bidder: bidder,
-		info:   parseBidderInfo(info),
-	}
-}
-
-type InfoAwareBidder struct {
-	Bidder
-	info parsedBidderInfo
-}
-
-func (i *InfoAwareBidder) MakeRequests(request *openrtb.BidRequest, reqInfo *ExtraRequestInfo) ([]*RequestData, []error) {
-	var allowedMediaTypes parsedSupports
-
-	if request.Site != nil {
-		if !i.info.site.enabled {
-			return nil, []error{BadInput("this bidder does not support site requests")}
-		}
-		allowedMediaTypes = i.info.site
-	}
-	if request.App != nil {
-		if !i.info.app.enabled {
-			return nil, []error{BadInput("this bidder does not support app requests")}
-		}
-		allowedMediaTypes = i.info.app
-	}
-
-	// Filtering imps is quite expensive (array filter with large, non-pointer elements)... but should be rare,
-	// because it only happens if the publisher makes a really bad request.
-	//
-	// To avoid allocating new arrays and copying in the normal case, we'll make one pass to
-	// see if any imps need to be removed, and another to do the removing if necessary.
-	numToFilter, errs := i.pruneImps(request.Imp, allowedMediaTypes)
-
-	// If all imps in bid request come with unsupported media types, exit
-	if numToFilter == len(request.Imp) {
-		return nil, append(errs, BadInput("Bid request didn't contain media types supported by the bidder"))
-	}
-
-	if numToFilter != 0 {
-		// Filter out imps with unsupported media types
-		filteredImps, newErrs := i.filterImps(request.Imp, numToFilter)
-		request.Imp = filteredImps
-		errs = append(errs, newErrs...)
-	}
-	reqs, delegateErrs := i.Bidder.MakeRequests(request, reqInfo)
-	return reqs, append(errs, delegateErrs...)
-}
-
-// pruneImps trims invalid media types from each imp, and returns true if any of the
-// Imps have _no_ valid Media Types left.
-func (i *InfoAwareBidder) pruneImps(imps []openrtb.Imp, allowedTypes parsedSupports) (int, []error) {
-	numToFilter := 0
-	var errs []error
-	for i := 0; i < len(imps); i++ {
-		if !allowedTypes.banner && imps[i].Banner != nil {
-			imps[i].Banner = nil
-			errs = append(errs, BadInput(fmt.Sprintf("request.imp[%d] uses banner, but this bidder doesn't support it", i)))
-		}
-		if !allowedTypes.video && imps[i].Video != nil {
-			imps[i].Video = nil
-			errs = append(errs, BadInput(fmt.Sprintf("request.imp[%d] uses video, but this bidder doesn't support it", i)))
-		}
-		if !allowedTypes.audio && imps[i].Audio != nil {
-			imps[i].Audio = nil
-			errs = append(errs, BadInput(fmt.Sprintf("request.imp[%d] uses audio, but this bidder doesn't support it", i)))
-		}
-		if !allowedTypes.native && imps[i].Native != nil {
-			imps[i].Native = nil
-			errs = append(errs, BadInput(fmt.Sprintf("request.imp[%d] uses native, but this bidder doesn't support it", i)))
-		}
-		if !hasAnyTypes(&imps[i]) {
-			numToFilter = numToFilter + 1
-		}
-	}
-	return numToFilter, errs
-}
-
-func parseAllowedTypes(allowedTypes []openrtb_ext.BidType) (allowBanner bool, allowVideo bool, allowAudio bool, allowNative bool) {
-	for _, allowedType := range allowedTypes {
-		switch allowedType {
-		case openrtb_ext.BidTypeBanner:
-			allowBanner = true
-		case openrtb_ext.BidTypeVideo:
-			allowVideo = true
-		case openrtb_ext.BidTypeAudio:
-			allowAudio = true
-		case openrtb_ext.BidTypeNative:
-			allowNative = true
-		}
-	}
-	return
-}
-
-func hasAnyTypes(imp *openrtb.Imp) bool {
-	return imp.Banner != nil || imp.Video != nil || imp.Audio != nil || imp.Native != nil
-}
-
-func (i *InfoAwareBidder) filterImps(imps []openrtb.Imp, numToFilter int) ([]openrtb.Imp, []error) {
-	newImps := make([]openrtb.Imp, 0, len(imps)-numToFilter)
-	errs := make([]error, 0, numToFilter)
-	for i := 0; i < len(imps); i++ {
-		if hasAnyTypes(&imps[i]) {
-			newImps = append(newImps, imps[i])
-		} else {
-			errs = append(errs, BadInput(fmt.Sprintf("request.imp[%d] has no supported MediaTypes. It will be ignored", i)))
-		}
-	}
-	return newImps, errs
-}
-
-type BidderInfos map[string]BidderInfo
-
-// ParseBidderInfos reads all the static/bidder-info/{bidder}.yaml files from the filesystem.
-// The map it returns will have a key for every element of the bidders array.
-// If a {bidder}.yaml file does not exist for some bidder, it will panic.
-func ParseBidderInfos(cfg map[string]config.Adapter, infoDir string, bidders []openrtb_ext.BidderName) BidderInfos {
-	bidderInfos := make(map[string]BidderInfo, len(bidders))
-	for _, bidderName := range bidders {
-		bidderString := string(bidderName)
-		fileData, err := ioutil.ReadFile(infoDir + "/" + bidderString + ".yaml")
-		if err != nil {
-			glog.Fatalf("error reading from file %s: %v", infoDir+"/"+bidderString+".yaml", err)
-		}
-
-		var parsedInfo BidderInfo
-		if err := yaml.Unmarshal(fileData, &parsedInfo); err != nil {
-			glog.Fatalf("error parsing yaml in file %s: %v", infoDir+"/"+bidderString+".yaml", err)
-		}
-
-		if isEnabledBidder(cfg, bidderString) {
-			parsedInfo.Status = StatusActive
-		} else {
-			parsedInfo.Status = StatusDisabled
-		}
-
-		bidderInfos[bidderString] = parsedInfo
-	}
-	return bidderInfos
-}
-
-func (infos BidderInfos) IsActive(bidder openrtb_ext.BidderName) bool {
-	return infos[string(bidder)].Status == StatusActive
-}
-
-func (infos BidderInfos) HasAppSupport(bidder openrtb_ext.BidderName) bool {
-	return infos[string(bidder)].Capabilities.App != nil
-}
-
-func (infos BidderInfos) HasSiteSupport(bidder openrtb_ext.BidderName) bool {
-	return infos[string(bidder)].Capabilities.Site != nil
-}
-
-func (infos BidderInfos) SupportsAppMediaType(bidder openrtb_ext.BidderName, mediaType openrtb_ext.BidType) bool {
-	return containsMediaType(infos[string(bidder)].Capabilities.App.MediaTypes, mediaType)
-}
-
-func (infos BidderInfos) SupportsWebMediaType(bidder openrtb_ext.BidderName, mediaType openrtb_ext.BidType) bool {
-	return containsMediaType(infos[string(bidder)].Capabilities.Site.MediaTypes, mediaType)
-}
-
-// isEnabledBidder Checks that a bidder config exists and is not disabled
-func isEnabledBidder(cfg map[string]config.Adapter, bidder string) bool {
-	a, ok := cfg[strings.ToLower(bidder)]
-	return ok && !a.Disabled
-}
-
-// BidderStatus represents a bidder status in PBS, can be either active or disabled
-type BidderStatus string
-
-const (
-	StatusUnknown  BidderStatus = ""
-	StatusActive   BidderStatus = "ACTIVE"
-	StatusDisabled BidderStatus = "DISABLED"
-)
-
-type BidderInfo struct {
-	Status                  BidderStatus      `yaml:"status" json:"status"`
-	Maintainer              *MaintainerInfo   `yaml:"maintainer" json:"maintainer"`
-	Capabilities            *CapabilitiesInfo `yaml:"capabilities" json:"capabilities"`
-	AliasOf                 string            `json:"aliasOf,omitempty"`
-	ModifyingVastXmlAllowed bool              `yaml:"modifyingVastXmlAllowed" json:"-" xml:"-"`
-	Debug                   *DebugInfo        `yaml:"debug,omitempty" json:"-" xml:"-"`
-}
-
-type DebugInfo struct {
-	Allow bool `yaml:"allow" json:"allow"`
-}
-
-type MaintainerInfo struct {
-	Email string `yaml:"email" json:"email"`
-}
-
-type CapabilitiesInfo struct {
-	App  *PlatformInfo `yaml:"app" json:"app"`
-	Site *PlatformInfo `yaml:"site" json:"site"`
-}
-
-type PlatformInfo struct {
-	MediaTypes []openrtb_ext.BidType `yaml:"mediaTypes" json:"mediaTypes"`
-}
-
-func containsMediaType(haystack []openrtb_ext.BidType, needle openrtb_ext.BidType) bool {
-	for i := 0; i < len(haystack); i++ {
-		if needle == haystack[i] {
-			return true
-		}
-	}
-	return false
-}
-
-// Structs to handle parsed bidder info, so we aren't reparsing every request
-type parsedBidderInfo struct {
-	app  parsedSupports
-	site parsedSupports
-}
-
-type parsedSupports struct {
-	enabled bool
-	banner  bool
-	video   bool
-	audio   bool
-	native  bool
-}
-
-func parseBidderInfo(info BidderInfo) parsedBidderInfo {
-	var parsedInfo parsedBidderInfo
-	if info.Capabilities != nil && info.Capabilities.App != nil {
-		parsedInfo.app.enabled = true
-		parsedInfo.app.banner, parsedInfo.app.video, parsedInfo.app.audio, parsedInfo.app.native = parseAllowedTypes(info.Capabilities.App.MediaTypes)
-	}
-	if info.Capabilities != nil && info.Capabilities.Site != nil {
-		parsedInfo.site.enabled = true
-		parsedInfo.site.banner, parsedInfo.site.video, parsedInfo.site.audio, parsedInfo.site.native = parseAllowedTypes(info.Capabilities.Site.MediaTypes)
-	}
-	return parsedInfo
-}
-
-type ExtraRequestInfo struct {
-	PbsEntryPoint metrics.RequestType
-}
diff --git a/adapters/infoawarebidder.go b/adapters/infoawarebidder.go
new file mode 100644
index 00000000..53d80b23
--- /dev/null
+++ b/adapters/infoawarebidder.go
@@ -0,0 +1,160 @@
+package adapters
+
+import (
+	"fmt"
+
+	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// InfoAwareBidder wraps a Bidder to ensure all requests abide by the capabilities and
+// media types defined in the static/bidder-info/{bidder}.yaml file.
+//
+// It adjusts incoming requests in the following ways:
+//   1. If App or Site traffic is not supported by the info file, then requests from
+//      those sources will be rejected before the delegate is called.
+//   2. If a given MediaType is not supported for the platform, then it will be set
+//      to nil before the request is forwarded to the delegate.
+//   3. Any Imps which have no MediaTypes left will be removed.
+//   4. If there are no valid Imps left, the delegate won't be called at all.
+type InfoAwareBidder struct {
+	Bidder
+	info parsedBidderInfo
+}
+
+// BuildInfoAwareBidder wraps a bidder to enforce site, app, and media type support.
+func BuildInfoAwareBidder(bidder Bidder, info config.BidderInfo) Bidder {
+	return &InfoAwareBidder{
+		Bidder: bidder,
+		info:   parseBidderInfo(info),
+	}
+}
+
+func (i *InfoAwareBidder) MakeRequests(request *openrtb.BidRequest, reqInfo *ExtraRequestInfo) ([]*RequestData, []error) {
+	var allowedMediaTypes parsedSupports
+
+	if request.Site != nil {
+		if !i.info.site.enabled {
+			return nil, []error{&errortypes.BadInput{Message: "this bidder does not support site requests"}}
+		}
+		allowedMediaTypes = i.info.site
+	}
+	if request.App != nil {
+		if !i.info.app.enabled {
+			return nil, []error{&errortypes.BadInput{Message: "this bidder does not support app requests"}}
+		}
+		allowedMediaTypes = i.info.app
+	}
+
+	// Filtering imps is quite expensive (array filter with large, non-pointer elements)... but should be rare,
+	// because it only happens if the publisher makes a really bad request.
+	//
+	// To avoid allocating new arrays and copying in the normal case, we'll make one pass to
+	// see if any imps need to be removed, and another to do the removing if necessary.
+	numToFilter, errs := pruneImps(request.Imp, allowedMediaTypes)
+
+	// If all imps in bid request come with unsupported media types, exit
+	if numToFilter == len(request.Imp) {
+		return nil, append(errs, &errortypes.BadInput{Message: "Bid request didn't contain media types supported by the bidder"})
+	}
+
+	if numToFilter != 0 {
+		// Filter out imps with unsupported media types
+		filteredImps, newErrs := filterImps(request.Imp, numToFilter)
+		request.Imp = filteredImps
+		errs = append(errs, newErrs...)
+	}
+	reqs, delegateErrs := i.Bidder.MakeRequests(request, reqInfo)
+	return reqs, append(errs, delegateErrs...)
+}
+
+// pruneImps trims invalid media types from each imp, and returns true if any of the
+// Imps have _no_ valid Media Types left.
+func pruneImps(imps []openrtb.Imp, allowedTypes parsedSupports) (int, []error) {
+	numToFilter := 0
+	var errs []error
+	for i := 0; i < len(imps); i++ {
+		if !allowedTypes.banner && imps[i].Banner != nil {
+			imps[i].Banner = nil
+			errs = append(errs, &errortypes.BadInput{Message: fmt.Sprintf("request.imp[%d] uses banner, but this bidder doesn't support it", i)})
+		}
+		if !allowedTypes.video && imps[i].Video != nil {
+			imps[i].Video = nil
+			errs = append(errs, &errortypes.BadInput{Message: fmt.Sprintf("request.imp[%d] uses video, but this bidder doesn't support it", i)})
+		}
+		if !allowedTypes.audio && imps[i].Audio != nil {
+			imps[i].Audio = nil
+			errs = append(errs, &errortypes.BadInput{Message: fmt.Sprintf("request.imp[%d] uses audio, but this bidder doesn't support it", i)})
+		}
+		if !allowedTypes.native && imps[i].Native != nil {
+			imps[i].Native = nil
+			errs = append(errs, &errortypes.BadInput{Message: fmt.Sprintf("request.imp[%d] uses native, but this bidder doesn't support it", i)})
+		}
+		if !hasAnyTypes(&imps[i]) {
+			numToFilter = numToFilter + 1
+		}
+	}
+	return numToFilter, errs
+}
+
+func parseAllowedTypes(allowedTypes []openrtb_ext.BidType) (allowBanner bool, allowVideo bool, allowAudio bool, allowNative bool) {
+	for _, allowedType := range allowedTypes {
+		switch allowedType {
+		case openrtb_ext.BidTypeBanner:
+			allowBanner = true
+		case openrtb_ext.BidTypeVideo:
+			allowVideo = true
+		case openrtb_ext.BidTypeAudio:
+			allowAudio = true
+		case openrtb_ext.BidTypeNative:
+			allowNative = true
+		}
+	}
+	return
+}
+
+func hasAnyTypes(imp *openrtb.Imp) bool {
+	return imp.Banner != nil || imp.Video != nil || imp.Audio != nil || imp.Native != nil
+}
+
+func filterImps(imps []openrtb.Imp, numToFilter int) ([]openrtb.Imp, []error) {
+	newImps := make([]openrtb.Imp, 0, len(imps)-numToFilter)
+	errs := make([]error, 0, numToFilter)
+	for i := 0; i < len(imps); i++ {
+		if hasAnyTypes(&imps[i]) {
+			newImps = append(newImps, imps[i])
+		} else {
+			errs = append(errs, &errortypes.BadInput{Message: fmt.Sprintf("request.imp[%d] has no supported MediaTypes. It will be ignored", i)})
+		}
+	}
+	return newImps, errs
+}
+
+// Structs to handle parsed bidder info, so we aren't reparsing every request
+type parsedBidderInfo struct {
+	app  parsedSupports
+	site parsedSupports
+}
+
+type parsedSupports struct {
+	enabled bool
+	banner  bool
+	video   bool
+	audio   bool
+	native  bool
+}
+
+func parseBidderInfo(info config.BidderInfo) parsedBidderInfo {
+	var parsedInfo parsedBidderInfo
+	if info.Capabilities != nil && info.Capabilities.App != nil {
+		parsedInfo.app.enabled = true
+		parsedInfo.app.banner, parsedInfo.app.video, parsedInfo.app.audio, parsedInfo.app.native = parseAllowedTypes(info.Capabilities.App.MediaTypes)
+	}
+	if info.Capabilities != nil && info.Capabilities.Site != nil {
+		parsedInfo.site.enabled = true
+		parsedInfo.site.banner, parsedInfo.site.video, parsedInfo.site.audio, parsedInfo.site.native = parseAllowedTypes(info.Capabilities.Site.MediaTypes)
+	}
+	return parsedInfo
+}
diff --git a/adapters/nanointeractive/nanointeractive.go b/adapters/nanointeractive/nanointeractive.go
index 82574d8b..7189543e 100644
--- a/adapters/nanointeractive/nanointeractive.go
+++ b/adapters/nanointeractive/nanointeractive.go
@@ -95,7 +95,9 @@ func (a *NanoInteractiveAdapter) MakeBids(
 	if response.StatusCode == http.StatusNoContent {
 		return nil, nil
 	} else if response.StatusCode == http.StatusBadRequest {
-		return nil, []error{adapters.BadInput("Invalid request.")}
+		return nil, []error{&errortypes.BadInput{
+			Message: "Invalid request.",
+		}}
 	} else if response.StatusCode != http.StatusOK {
 		return nil, []error{&errortypes.BadServerResponse{
 			Message: fmt.Sprintf("unexpected HTTP status %d.", response.StatusCode),
diff --git a/adapters/smartrtb/smartrtb.go b/adapters/smartrtb/smartrtb.go
index 364c52a7..4950f1ce 100644
--- a/adapters/smartrtb/smartrtb.go
+++ b/adapters/smartrtb/smartrtb.go
@@ -62,12 +62,16 @@ func (adapter *SmartRTBAdapter) buildEndpointURL(pubID string) (string, error) {
 func parseExtImp(dst *bidRequestExt, imp *openrtb.Imp) error {
 	var ext adapters.ExtImpBidder
 	if err := json.Unmarshal(imp.Ext, &ext); err != nil {
-		return adapters.BadInput(err.Error())
+		return &errortypes.BadInput{
+			Message: err.Error(),
+		}
 	}
 
 	var src openrtb_ext.ExtImpSmartRTB
 	if err := json.Unmarshal(ext.Bidder, &src); err != nil {
-		return adapters.BadInput(err.Error())
+		return &errortypes.BadInput{
+			Message: err.Error(),
+		}
 	}
 
 	if dst.PubID == "" {
@@ -107,7 +111,7 @@ func (s *SmartRTBAdapter) MakeRequests(brq *openrtb.BidRequest, reqInfo *adapter
 	}
 
 	if ext.PubID == "" {
-		return nil, append(errs, adapters.BadInput("Cannot infer publisher ID from bid ext"))
+		return nil, append(errs, &errortypes.BadInput{Message: "Cannot infer publisher ID from bid ext"})
 	}
 
 	brq.Ext, err = json.Marshal(ext)
@@ -146,7 +150,7 @@ func (s *SmartRTBAdapter) MakeBids(
 	if rs.StatusCode == http.StatusNoContent {
 		return nil, nil
 	} else if rs.StatusCode == http.StatusBadRequest {
-		return nil, []error{adapters.BadInput("Invalid request.")}
+		return nil, []error{&errortypes.BadInput{Message: "Invalid request."}}
 	} else if rs.StatusCode != http.StatusOK {
 		return nil, []error{&errortypes.BadServerResponse{
 			Message: fmt.Sprintf("Unexpected HTTP status %d.", rs.StatusCode),
diff --git a/config/bidderinfo.go b/config/bidderinfo.go
new file mode 100644
index 00000000..4af8a97c
--- /dev/null
+++ b/config/bidderinfo.go
@@ -0,0 +1,88 @@
+package config
+
+import (
+	"fmt"
+	"io/ioutil"
+	"strings"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"gopkg.in/yaml.v2"
+)
+
+// BidderInfos contains a mapping of bidder name to bidder info.
+type BidderInfos map[string]BidderInfo
+
+// BidderInfo is the maintainer information, supported auction types, and feature opts-in for a bidder.
+type BidderInfo struct {
+	Enabled                 bool              // copied from adapter config for convenience. to be refactored.
+	Maintainer              *MaintainerInfo   `yaml:"maintainer"`
+	Capabilities            *CapabilitiesInfo `yaml:"capabilities"`
+	ModifyingVastXmlAllowed bool              `yaml:"modifyingVastXmlAllowed"`
+	Debug                   *DebugInfo        `yaml:"debug,omitempty"`
+}
+
+// MaintainerInfo is the support email address for a bidder.
+type MaintainerInfo struct {
+	Email string `yaml:"email"`
+}
+
+// CapabilitiesInfo is the supported platforms for a bidder.
+type CapabilitiesInfo struct {
+	App  *PlatformInfo `yaml:"app"`
+	Site *PlatformInfo `yaml:"site"`
+}
+
+// PlatformInfo is the supported media types for a bidder.
+type PlatformInfo struct {
+	MediaTypes []openrtb_ext.BidType `yaml:"mediaTypes"`
+}
+
+// DebugInfo is the supported debug options for a bidder.
+type DebugInfo struct {
+	Allow bool `yaml:"allow"`
+}
+
+// LoadBidderInfoFromDisk parses all static/bidder-info/{bidder}.yaml files from the file system.
+func LoadBidderInfoFromDisk(path string, adapterConfigs map[string]Adapter, bidders []string) (map[string]BidderInfo, error) {
+	reader := infoReaderFromDisk{path}
+	return loadBidderInfo(reader, adapterConfigs, bidders)
+}
+
+func loadBidderInfo(r infoReader, adapterConfigs map[string]Adapter, bidders []string) (map[string]BidderInfo, error) {
+	infos := map[string]BidderInfo{}
+
+	for _, bidder := range bidders {
+		data, err := r.Read(bidder)
+		if err != nil {
+			return nil, err
+		}
+
+		info := BidderInfo{}
+		if err := yaml.Unmarshal(data, &info); err != nil {
+			return nil, fmt.Errorf("error parsing yaml for bidder %s: %v", bidder, err)
+		}
+
+		info.Enabled = isEnabledByConfig(adapterConfigs, bidder)
+		infos[bidder] = info
+	}
+
+	return infos, nil
+}
+
+func isEnabledByConfig(adapterConfigs map[string]Adapter, bidderName string) bool {
+	a, ok := adapterConfigs[strings.ToLower(bidderName)]
+	return ok && !a.Disabled
+}
+
+type infoReader interface {
+	Read(bidder string) ([]byte, error)
+}
+
+type infoReaderFromDisk struct {
+	path string
+}
+
+func (r infoReaderFromDisk) Read(bidder string) ([]byte, error) {
+	path := fmt.Sprintf("%v/%v.yaml", r.path, bidder)
+	return ioutil.ReadFile(path)
+}
diff --git a/config/config.go b/config/config.go
index 3a7ad509..30e5eb7e 100644
--- a/config/config.go
+++ b/config/config.go
@@ -522,7 +522,7 @@ func New(v *viper.Viper) (*Configuration, error) {
 	glog.Info("Logging the resolved configuration:")
 	logGeneral(reflect.ValueOf(c), "  \t")
 	if errs := c.validate(); len(errs) > 0 {
-		return &c, errortypes.NewAggregateErrors("validation errors", errs)
+		return &c, errortypes.NewAggregateError("validation errors", errs)
 	}
 
 	return &c, nil
@@ -596,6 +596,7 @@ func (cfg *Configuration) setDerivedDefaults() {
 	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")
+	// openrtb_ext.BidderEpom doesn't have a good default.
 	// openrtb_ext.BidderFacebook doesn't have a good default.
 	// openrtb_ext.BidderGamma doesn't have a good default.
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGamoshi, "https://rtb.gamoshi.io/user_sync_prebid?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dgamoshi%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bgusr%5D")
@@ -836,6 +837,8 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com")
 	v.SetDefault("adapters.engagebdr.endpoint", "http://dsp.bnmla.com/hb")
 	v.SetDefault("adapters.eplanning.endpoint", "http://rtb.e-planning.net/pbs/1")
+	v.SetDefault("adapters.epom.endpoint", "https://an.epom.com/ortb")
+	v.SetDefault("adapters.epom.disabled", true)
 	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", "https://grid.bidswitch.net/sp_bid?sp=prebid")
diff --git a/endpoints/events/vtrack.go b/endpoints/events/vtrack.go
index 9a4d9c08..da845162 100644
--- a/endpoints/events/vtrack.go
+++ b/endpoints/events/vtrack.go
@@ -13,7 +13,6 @@ import (
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
 	accountService "github.com/prebid/prebid-server/account"
-	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/analytics"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/errortypes"
@@ -30,7 +29,7 @@ const (
 type vtrackEndpoint struct {
 	Cfg         *config.Configuration
 	Accounts    stored_requests.AccountFetcher
-	BidderInfos adapters.BidderInfos
+	BidderInfos config.BidderInfos
 	Cache       prebid_cache_client.Client
 }
 
@@ -46,7 +45,7 @@ type CacheObject struct {
 	UUID string `json:"uuid"`
 }
 
-func NewVTrackEndpoint(cfg *config.Configuration, accounts stored_requests.AccountFetcher, cache prebid_cache_client.Client, bidderInfos adapters.BidderInfos) httprouter.Handle {
+func NewVTrackEndpoint(cfg *config.Configuration, accounts stored_requests.AccountFetcher, cache prebid_cache_client.Client, bidderInfos config.BidderInfos) httprouter.Handle {
 	vte := &vtrackEndpoint{
 		Cfg:         cfg,
 		Accounts:    accounts,
@@ -240,7 +239,7 @@ func (v *vtrackEndpoint) cachePutObjects(ctx context.Context, req *BidCacheReque
 }
 
 // getBiddersAllowingVastUpdate returns a list of bidders that allow VAST XML modification
-func getBiddersAllowingVastUpdate(req *BidCacheRequest, bidderInfos *adapters.BidderInfos, allowUnknownBidder bool) map[string]struct{} {
+func getBiddersAllowingVastUpdate(req *BidCacheRequest, bidderInfos *config.BidderInfos, allowUnknownBidder bool) map[string]struct{} {
 	bl := map[string]struct{}{}
 
 	for _, bcr := range req.Puts {
@@ -253,12 +252,12 @@ func getBiddersAllowingVastUpdate(req *BidCacheRequest, bidderInfos *adapters.Bi
 }
 
 // isAllowVastForBidder checks if a bidder is active and allowed to modify vast xml data
-func isAllowVastForBidder(bidder string, bidderInfos *adapters.BidderInfos, allowUnknownBidder bool) bool {
+func isAllowVastForBidder(bidder string, bidderInfos *config.BidderInfos, allowUnknownBidder bool) bool {
 	//if bidder is active and isModifyingVastXmlAllowed is true
 	// check if bidder is configured
 	if b, ok := (*bidderInfos)[bidder]; bidderInfos != nil && ok {
 		// check if bidder is enabled
-		return b.Status == adapters.StatusActive && b.ModifyingVastXmlAllowed
+		return b.Enabled && b.ModifyingVastXmlAllowed
 	}
 
 	return allowUnknownBidder
diff --git a/endpoints/info/bidders.go b/endpoints/info/bidders.go
index 44482106..2bd925ce 100644
--- a/endpoints/info/bidders.go
+++ b/endpoints/info/bidders.go
@@ -3,128 +3,40 @@ package info
 import (
 	"encoding/json"
 	"net/http"
+	"sort"
 
-	"github.com/buger/jsonparser"
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
-	"github.com/prebid/prebid-server/adapters"
-	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/config"
 )
 
-// NewBiddersEndpoint implements /info/bidders
-func NewBiddersEndpoint(aliases map[string]string) httprouter.Handle {
-	bidderNames := make([]string, 0)
-
-	for _, bidderName := range openrtb_ext.CoreBidderNames() {
-		bidderNames = append(bidderNames, string(bidderName))
-	}
-
-	for aliasName := range aliases {
-		bidderNames = append(bidderNames, aliasName)
-	}
-
-	biddersJson, err := json.Marshal(bidderNames)
+// NewBiddersEndpoint builds a handler for the /info/bidders endpoint.
+func NewBiddersEndpoint(bidders config.BidderInfos, aliases map[string]string) httprouter.Handle {
+	response, err := prepareBiddersResponse(bidders, aliases)
 	if err != nil {
 		glog.Fatalf("error creating /info/bidders endpoint response: %v", err)
 	}
 
 	return func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
 		w.Header().Set("Content-Type", "application/json")
-		if _, err := w.Write(biddersJson); err != nil {
+		if _, err := w.Write(response); err != nil {
 			glog.Errorf("error writing response to /info/bidders: %v", err)
 		}
 	}
 }
 
-// NewBidderDetailsEndpoint implements /info/bidders/*
-func NewBidderDetailsEndpoint(infos adapters.BidderInfos, aliases map[string]string) httprouter.Handle {
-	// Validate if there exist and alias with name "all". If it does error out because
-	// that will break the /info/bidders/all endpoint.
-	if _, ok := aliases["all"]; ok {
-		glog.Fatal("Default aliases shouldn't contain an alias with name \"all\". This will break the /info/bidders/all endpoint")
-	}
-
-	// Build a new map that's basically a copy of "infos" but will also contain
-	// alias bidder infos
-	var allBidderInfo = make(map[string]adapters.BidderInfo, len(infos)+len(aliases))
-
-	// Build all the responses up front, since there are a finite number and it won't use much memory.
-	responses := make(map[string]json.RawMessage, len(infos)+len(aliases))
-	for bidderName, bidderInfo := range infos {
-		// Copy bidderInfo into "allBidderInfo" map
-		allBidderInfo[bidderName] = bidderInfo
+func prepareBiddersResponse(bidders config.BidderInfos, aliases map[string]string) ([]byte, error) {
+	bidderNames := make([]string, 0, len(bidders)+len(aliases))
 
-		// JSON encode bidder info and add it to the "responses" map
-		jsonData, err := json.Marshal(bidderInfo)
-		if err != nil {
-			glog.Fatalf("Failed to JSON-marshal bidder-info/%s.yaml data.", bidderName)
-		}
-		responses[bidderName] = jsonData
+	for name := range bidders {
+		bidderNames = append(bidderNames, name)
 	}
 
-	// Add in any default aliases
-	for aliasName, bidderName := range aliases {
-		// Add the alias bidder info into "allBidderInfo" map
-		aliasInfo := infos[bidderName]
-		aliasInfo.AliasOf = bidderName
-		allBidderInfo[aliasName] = aliasInfo
-
-		// JSON encode core bidder info for the alias and add it to the "responses" map
-		responses[aliasName] = createAliasInfo(responses, aliasName, bidderName)
+	for name := range aliases {
+		bidderNames = append(bidderNames, name)
 	}
 
-	allBidderResponse, err := json.Marshal(allBidderInfo)
-	if err != nil {
-		glog.Fatal("Failed to JSON-marshal all bidder info data.")
-	}
-	// Add the json response containing all bidders info for the /info/bidders/all endpoint
-	responses["all"] = allBidderResponse
-
-	// Return an endpoint which writes the responses from memory.
-	return func(w http.ResponseWriter, _ *http.Request, ps httprouter.Params) {
-		forBidder := ps.ByName("bidderName")
-
-		// If the requested path was /info/bidders/{bidderName} then return the info about that bidder
-		if response, ok := responses[forBidder]; ok {
-			w.Header().Set("Content-Type", "application/json")
-			if _, err := w.Write(response); err != nil {
-				glog.Errorf("error writing response to /info/bidders/%s: %v", forBidder, err)
-			}
-		} else {
-			w.WriteHeader(http.StatusNotFound)
-		}
-	}
-}
-
-func createAliasInfo(responses map[string]json.RawMessage, alias string, core string) json.RawMessage {
-	coreJSON, ok := responses[core]
-	if !ok {
-		glog.Fatalf("Unknown core bidder %s for default alias %s", core, alias)
-	}
-	jsonData := make(json.RawMessage, len(coreJSON))
-	copy(jsonData, coreJSON)
-
-	jsonInfo, err := jsonparser.Set(jsonData, []byte(`"`+core+`"`), "aliasOf")
-	if err != nil {
-		glog.Fatalf("Failed to generate bidder info for %s, an alias of %s", alias, core)
-	}
-	return jsonInfo
-}
-
-type infoFile struct {
-	Maintainer   *maintainerInfo   `yaml:"maintainer" json:"maintainer"`
-	Capabilities *capabilitiesInfo `yaml:"capabilities" json:"capabilities"`
-}
-
-type maintainerInfo struct {
-	Email string `yaml:"email" json:"email"`
-}
-
-type capabilitiesInfo struct {
-	App  *platformInfo `yaml:"app" json:"app"`
-	Site *platformInfo `yaml:"site" json:"site"`
-}
+	sort.Strings(bidderNames)
 
-type platformInfo struct {
-	MediaTypes []string `yaml:"mediaTypes" json:"mediaTypes"`
+	return json.Marshal(bidderNames)
 }
diff --git a/endpoints/info/bidders_detail.go b/endpoints/info/bidders_detail.go
new file mode 100644
index 00000000..04bc9b04
--- /dev/null
+++ b/endpoints/info/bidders_detail.go
@@ -0,0 +1,185 @@
+package info
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	"github.com/golang/glog"
+	"github.com/julienschmidt/httprouter"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+const (
+	statusActive   string = "ACTIVE"
+	statusDisabled string = "DISABLED"
+)
+
+// NewBiddersDetailEndpoint builds a handler for the /info/bidders/<bidder> endpoint.
+func NewBiddersDetailEndpoint(bidders config.BidderInfos, biddersConfig map[string]config.Adapter, aliases map[string]string) httprouter.Handle {
+	responses, err := prepareBiddersDetailResponse(bidders, biddersConfig, aliases)
+	if err != nil {
+		glog.Fatalf("error creating /info/bidders/<bidder> endpoint response: %v", err)
+	}
+
+	return func(w http.ResponseWriter, _ *http.Request, ps httprouter.Params) {
+		bidder := ps.ByName("bidderName")
+
+		if response, ok := responses[bidder]; ok {
+			w.Header().Set("Content-Type", "application/json")
+			if _, err := w.Write(response); err != nil {
+				glog.Errorf("error writing response to /info/bidders/%s: %v", bidder, err)
+			}
+		} else {
+			w.WriteHeader(http.StatusNotFound)
+		}
+	}
+}
+
+func prepareBiddersDetailResponse(bidders config.BidderInfos, biddersConfig map[string]config.Adapter, aliases map[string]string) (map[string][]byte, error) {
+	details, err := mapDetails(bidders, biddersConfig, aliases)
+	if err != nil {
+		return nil, err
+	}
+
+	responses, err := marshalDetailsResponse(details)
+	if err != nil {
+		return nil, err
+	}
+
+	all, err := marshalAllResponse(responses)
+	if err != nil {
+		return nil, err
+	}
+	responses["all"] = all
+
+	return responses, nil
+}
+
+func mapDetails(bidders config.BidderInfos, biddersConfig map[string]config.Adapter, aliases map[string]string) (map[string]bidderDetail, error) {
+	details := map[string]bidderDetail{}
+
+	for bidderName, bidderInfo := range bidders {
+		endpoint := resolveEndpoint(bidderName, biddersConfig)
+		details[bidderName] = mapDetailFromConfig(bidderInfo, endpoint)
+	}
+
+	for aliasName, bidderName := range aliases {
+		aliasBaseInfo, aliasBaseInfoFound := details[bidderName]
+		if !aliasBaseInfoFound {
+			return nil, fmt.Errorf("base adapter %s for alias %s not found", bidderName, aliasName)
+		}
+
+		aliasInfo := aliasBaseInfo
+		aliasInfo.AliasOf = bidderName
+		details[aliasName] = aliasInfo
+	}
+
+	return details, nil
+}
+
+func resolveEndpoint(bidder string, biddersConfig map[string]config.Adapter) string {
+	if c, found := biddersConfig[bidder]; found {
+		return c.Endpoint
+	}
+
+	return ""
+}
+
+func marshalDetailsResponse(details map[string]bidderDetail) (map[string][]byte, error) {
+	responses := map[string][]byte{}
+
+	for bidder, detail := range details {
+		json, err := json.Marshal(detail)
+		if err != nil {
+			return nil, fmt.Errorf("unable to marshal info for bidder %s: %v", bidder, err)
+		}
+		responses[bidder] = json
+	}
+
+	return responses, nil
+}
+
+func marshalAllResponse(responses map[string][]byte) ([]byte, error) {
+	responsesJSON := make(map[string]json.RawMessage, len(responses))
+
+	for k, v := range responses {
+		responsesJSON[k] = json.RawMessage(v)
+	}
+
+	json, err := json.Marshal(responsesJSON)
+	if err != nil {
+		return nil, fmt.Errorf("unable to marshal info for bidder all: %v", err)
+	}
+	return json, nil
+}
+
+type bidderDetail struct {
+	Status       string        `json:"status"`
+	UsesHTTPS    *bool         `json:"usesHttps,omitempty"`
+	Maintainer   *maintainer   `json:"maintainer,omitempty"`
+	Capabilities *capabilities `json:"capabilities,omitempty"`
+	AliasOf      string        `json:"aliasOf,omitempty"`
+}
+
+type maintainer struct {
+	Email string `json:"email"`
+}
+
+type capabilities struct {
+	App  *platform `json:"app,omitempty"`
+	Site *platform `json:"site,omitempty"`
+}
+
+type platform struct {
+	MediaTypes []string `json:"mediaTypes"`
+}
+
+func mapDetailFromConfig(c config.BidderInfo, endpoint string) bidderDetail {
+	var bidderDetail bidderDetail
+
+	if c.Maintainer != nil {
+		bidderDetail.Maintainer = &maintainer{
+			Email: c.Maintainer.Email,
+		}
+	}
+
+	if c.Enabled {
+		bidderDetail.Status = statusActive
+
+		usesHTTPS := strings.HasPrefix(strings.ToLower(endpoint), "https://")
+		bidderDetail.UsesHTTPS = &usesHTTPS
+
+		if c.Capabilities != nil {
+			bidderDetail.Capabilities = &capabilities{}
+
+			if c.Capabilities.App != nil {
+				bidderDetail.Capabilities.App = &platform{
+					MediaTypes: mapMediaTypes(c.Capabilities.App.MediaTypes),
+				}
+			}
+
+			if c.Capabilities.Site != nil {
+				bidderDetail.Capabilities.Site = &platform{
+					MediaTypes: mapMediaTypes(c.Capabilities.Site.MediaTypes),
+				}
+			}
+		}
+	} else {
+		bidderDetail.Status = statusDisabled
+	}
+
+	return bidderDetail
+}
+
+func mapMediaTypes(m []openrtb_ext.BidType) []string {
+	mediaTypes := make([]string, len(m))
+
+	for i, v := range m {
+		mediaTypes[i] = string(v)
+	}
+
+	return mediaTypes
+}
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index dc66163a..8cc8e9bd 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -245,7 +245,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 			Code:    errortypes.ReadCode(v),
 			Message: v.Error(),
 		}
-		warnings[openrtb_ext.BidderNameGeneral] = append(warnings[openrtb_ext.BidderNameGeneral], bidderErr)
+		warnings[openrtb_ext.BidderReservedGeneral] = append(warnings[openrtb_ext.BidderReservedGeneral], bidderErr)
 	}
 
 	// Now JSONify the targets for the AMP response.
diff --git a/errortypes/aggregate.go b/errortypes/aggregate.go
index d58bcea7..272e255c 100644
--- a/errortypes/aggregate.go
+++ b/errortypes/aggregate.go
@@ -5,22 +5,22 @@ import (
 	"strconv"
 )
 
-// AggregateErrors represents one or more errors.
-type AggregateErrors struct {
+// AggregateError represents one or more errors.
+type AggregateError struct {
 	Message string
 	Errors  []error
 }
 
-// NewAggregateErrors builds a AggregateErrors struct.
-func NewAggregateErrors(msg string, errs []error) AggregateErrors {
-	return AggregateErrors{
+// NewAggregateError builds a AggregateError struct.
+func NewAggregateError(msg string, errs []error) AggregateError {
+	return AggregateError{
 		Message: msg,
 		Errors:  errs,
 	}
 }
 
 // Error implements the standard error interface.
-func (e AggregateErrors) Error() string {
+func (e AggregateError) Error() string {
 	if len(e.Errors) == 0 {
 		return ""
 	}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 1a4bd158..b43aad8e 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -41,6 +41,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/emx_digital"
 	"github.com/prebid/prebid-server/adapters/engagebdr"
 	"github.com/prebid/prebid-server/adapters/eplanning"
+	"github.com/prebid/prebid-server/adapters/epom"
 	"github.com/prebid/prebid-server/adapters/gamma"
 	"github.com/prebid/prebid-server/adapters/gamoshi"
 	"github.com/prebid/prebid-server/adapters/grid"
@@ -144,6 +145,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderEmxDigital:       emx_digital.Builder,
 		openrtb_ext.BidderEngageBDR:        engagebdr.Builder,
 		openrtb_ext.BidderEPlanning:        eplanning.Builder,
+		openrtb_ext.BidderEpom:             epom.Builder,
 		openrtb_ext.BidderGamma:            gamma.Builder,
 		openrtb_ext.BidderGamoshi:          gamoshi.Builder,
 		openrtb_ext.BidderGrid:             grid.Builder,
diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go
index 7e271376..8af6d11a 100644
--- a/exchange/adapter_util.go
+++ b/exchange/adapter_util.go
@@ -12,7 +12,7 @@ import (
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
-func BuildAdapters(client *http.Client, cfg *config.Configuration, infos adapters.BidderInfos, me metrics.MetricsEngine) (map[openrtb_ext.BidderName]adaptedBidder, []error) {
+func BuildAdapters(client *http.Client, cfg *config.Configuration, infos config.BidderInfos, me metrics.MetricsEngine) (map[openrtb_ext.BidderName]adaptedBidder, []error) {
 	exchangeBidders := buildExchangeBiddersLegacy(cfg.Adapters, infos)
 
 	exchangeBiddersModern, errs := buildExchangeBidders(cfg, infos, client, me)
@@ -30,7 +30,7 @@ func BuildAdapters(client *http.Client, cfg *config.Configuration, infos adapter
 	return exchangeBidders, nil
 }
 
-func buildExchangeBidders(cfg *config.Configuration, infos adapters.BidderInfos, client *http.Client, me metrics.MetricsEngine) (map[openrtb_ext.BidderName]adaptedBidder, []error) {
+func buildExchangeBidders(cfg *config.Configuration, infos config.BidderInfos, client *http.Client, me metrics.MetricsEngine) (map[openrtb_ext.BidderName]adaptedBidder, []error) {
 	bidders, errs := buildBidders(cfg.Adapters, infos, newAdapterBuilders())
 	if len(errs) > 0 {
 		return nil, errs
@@ -50,7 +50,7 @@ func buildExchangeBidders(cfg *config.Configuration, infos adapters.BidderInfos,
 
 }
 
-func buildBidders(adapterConfig map[string]config.Adapter, infos adapters.BidderInfos, builders map[openrtb_ext.BidderName]adapters.Builder) (map[openrtb_ext.BidderName]adapters.Bidder, []error) {
+func buildBidders(adapterConfig map[string]config.Adapter, infos config.BidderInfos, builders map[openrtb_ext.BidderName]adapters.Builder) (map[openrtb_ext.BidderName]adapters.Bidder, []error) {
 	bidders := make(map[openrtb_ext.BidderName]adapters.Bidder)
 	var errs []error
 
@@ -78,14 +78,14 @@ func buildBidders(adapterConfig map[string]config.Adapter, infos adapters.Bidder
 			continue
 		}
 
-		if info.Status == adapters.StatusActive {
+		if info.Enabled {
 			bidderInstance, builderErr := builder(bidderName, cfg)
 			if builderErr != nil {
 				errs = append(errs, fmt.Errorf("%v: %v", bidder, builderErr))
 				continue
 			}
 
-			bidderWithInfoEnforcement := adapters.EnforceBidderInfo(bidderInstance, info)
+			bidderWithInfoEnforcement := adapters.BuildInfoAwareBidder(bidderInstance, info)
 
 			bidders[bidderName] = bidderWithInfoEnforcement
 		}
@@ -94,11 +94,11 @@ func buildBidders(adapterConfig map[string]config.Adapter, infos adapters.Bidder
 	return bidders, errs
 }
 
-func buildExchangeBiddersLegacy(adapterConfig map[string]config.Adapter, infos adapters.BidderInfos) map[openrtb_ext.BidderName]adaptedBidder {
+func buildExchangeBiddersLegacy(adapterConfig map[string]config.Adapter, infos config.BidderInfos) map[openrtb_ext.BidderName]adaptedBidder {
 	bidders := make(map[openrtb_ext.BidderName]adaptedBidder, 2)
 
 	// Lifestreet
-	if infos[string(openrtb_ext.BidderLifestreet)].Status == adapters.StatusActive {
+	if infos[string(openrtb_ext.BidderLifestreet)].Enabled {
 		adapter := lifestreet.NewLifestreetLegacyAdapter(adapters.DefaultHTTPAdapterConfig, adapterConfig[string(openrtb_ext.BidderLifestreet)].Endpoint)
 		bidders[openrtb_ext.BidderLifestreet] = adaptLegacyAdapter(adapter)
 	}
@@ -113,11 +113,11 @@ func wrapWithMiddleware(bidders map[openrtb_ext.BidderName]adaptedBidder) {
 }
 
 // GetActiveBidders returns a map of all active bidder names.
-func GetActiveBidders(infos adapters.BidderInfos) map[string]openrtb_ext.BidderName {
+func GetActiveBidders(infos config.BidderInfos) map[string]openrtb_ext.BidderName {
 	activeBidders := make(map[string]openrtb_ext.BidderName)
 
 	for name, info := range infos {
-		if info.Status != adapters.StatusDisabled {
+		if info.Enabled {
 			activeBidders[name] = openrtb_ext.BidderName(name)
 		}
 	}
@@ -126,13 +126,13 @@ func GetActiveBidders(infos adapters.BidderInfos) map[string]openrtb_ext.BidderN
 }
 
 // GetDisabledBiddersErrorMessages returns a map of error messages for disabled bidders.
-func GetDisabledBiddersErrorMessages(infos adapters.BidderInfos) map[string]string {
+func GetDisabledBiddersErrorMessages(infos config.BidderInfos) map[string]string {
 	disabledBidders := make(map[string]string)
 
-	for bidderName, bidderInfo := range infos {
-		if bidderInfo.Status == adapters.StatusDisabled {
-			msg := fmt.Sprintf(`Bidder "%s" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`, bidderName)
-			disabledBidders[bidderName] = msg
+	for name, info := range infos {
+		if !info.Enabled {
+			msg := fmt.Sprintf(`Bidder "%s" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`, name)
+			disabledBidders[name] = msg
 		}
 	}
 
diff --git a/exchange/bidder.go b/exchange/bidder.go
index 5b5852bd..f916e205 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -93,7 +93,7 @@ type pbsOrtbSeatBid struct {
 //
 // The name refers to the "Adapter" architecture pattern, and should not be confused with a Prebid "Adapter"
 // (which is being phased out and replaced by Bidder for OpenRTB auctions)
-func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Configuration, me metrics.MetricsEngine, name openrtb_ext.BidderName, debugInfo *adapters.DebugInfo) adaptedBidder {
+func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Configuration, me metrics.MetricsEngine, name openrtb_ext.BidderName, debugInfo *config.DebugInfo) adaptedBidder {
 	return &bidderAdapter{
 		Bidder:     bidder,
 		BidderName: name,
@@ -102,12 +102,12 @@ func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Config
 		config: bidderAdapterConfig{
 			Debug:              cfg.Debug,
 			DisableConnMetrics: cfg.Metrics.Disabled.AdapterConnectionMetrics,
-			DebugInfo:          adapters.DebugInfo{Allow: parseDebugInfo(debugInfo)},
+			DebugInfo:          config.DebugInfo{Allow: parseDebugInfo(debugInfo)},
 		},
 	}
 }
 
-func parseDebugInfo(info *adapters.DebugInfo) bool {
+func parseDebugInfo(info *config.DebugInfo) bool {
 	if info == nil {
 		return true
 	}
@@ -125,7 +125,7 @@ type bidderAdapter struct {
 type bidderAdapterConfig struct {
 	Debug              config.Debug
 	DisableConnMetrics bool
-	DebugInfo          adapters.DebugInfo
+	DebugInfo          config.DebugInfo
 }
 
 func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
diff --git a/exchange/events.go b/exchange/events.go
index 3a05e098..f15579c6 100644
--- a/exchange/events.go
+++ b/exchange/events.go
@@ -5,7 +5,6 @@ import (
 	"time"
 
 	jsonpatch "github.com/evanphx/json-patch"
-	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/analytics"
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/endpoints/events"
@@ -20,12 +19,12 @@ type eventTracking struct {
 	enabledForRequest  bool
 	auctionTimestampMs int64
 	integration        metrics.DemandSource // web app amp
-	bidderInfos        adapters.BidderInfos
+	bidderInfos        config.BidderInfos
 	externalURL        string
 }
 
 // getEventTracking creates an eventTracking object from the different configuration sources
-func getEventTracking(requestExtPrebid *openrtb_ext.ExtRequestPrebid, ts time.Time, account *config.Account, bidderInfos adapters.BidderInfos, externalURL string) *eventTracking {
+func getEventTracking(requestExtPrebid *openrtb_ext.ExtRequestPrebid, ts time.Time, account *config.Account, bidderInfos config.BidderInfos, externalURL string) *eventTracking {
 	return &eventTracking{
 		accountID:          account.ID,
 		enabledForAccount:  account.EventsEnabled,
diff --git a/exchange/exchange.go b/exchange/exchange.go
index cfb9c6d3..b4fa3a61 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -51,7 +51,7 @@ type IdFetcher interface {
 
 type exchange struct {
 	adapterMap          map[openrtb_ext.BidderName]adaptedBidder
-	bidderInfo          adapters.BidderInfos
+	bidderInfo          config.BidderInfos
 	me                  metrics.MetricsEngine
 	cache               prebid_cache_client.Client
 	cacheTime           time.Duration
@@ -78,7 +78,7 @@ type bidResponseWrapper struct {
 	bidder       openrtb_ext.BidderName
 }
 
-func NewExchange(adapters map[openrtb_ext.BidderName]adaptedBidder, cache prebid_cache_client.Client, cfg *config.Configuration, metricsEngine metrics.MetricsEngine, infos adapters.BidderInfos, gDPR gdpr.Permissions, currencyConverter *currency.RateConverter, categoriesFetcher stored_requests.CategoryFetcher) Exchange {
+func NewExchange(adapters map[openrtb_ext.BidderName]adaptedBidder, cache prebid_cache_client.Client, cfg *config.Configuration, metricsEngine metrics.MetricsEngine, infos config.BidderInfos, gDPR gdpr.Permissions, currencyConverter *currency.RateConverter, categoriesFetcher stored_requests.CategoryFetcher) Exchange {
 	return &exchange{
 		adapterMap:          adapters,
 		bidderInfo:          infos,
diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go
index f24fd6c5..36dac06c 100644
--- a/gdpr/gdpr.go
+++ b/gdpr/gdpr.go
@@ -44,8 +44,8 @@ func NewPermissions(ctx context.Context, cfg config.GDPR, vendorIDs map[openrtb_
 		cfg:       cfg,
 		vendorIDs: vendorIDs,
 		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
-			tcf1SpecVersion: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker, tcf1SpecVersion),
-			tcf2SpecVersion: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker, tcf2SpecVersion)},
+			tcf1SpecVersion: newVendorListFetcherTCF1(cfg),
+			tcf2SpecVersion: newVendorListFetcherTCF2(ctx, cfg, client, vendorListURLMaker)},
 	}
 
 	if cfg.HostVendorID == 0 {
diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go
index 95a17109..bc7eab40 100644
--- a/gdpr/vendorlist-fetching.go
+++ b/gdpr/vendorlist-fetching.go
@@ -26,30 +26,40 @@ type saveVendors func(uint16, api.VendorList)
 //
 // Nothing in this file is exported. Public APIs can be found in gdpr.go
 
-func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http.Client, urlMaker func(uint16, uint8) string, tcfSpecVersion uint8) func(ctx context.Context, id uint16) (vendorlist.VendorList, error) {
-	var fallback api.VendorList
-
-	if tcfSpecVersion == tcf1SpecVersion && len(cfg.TCF1.FallbackGVLPath) == 0 {
-		return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) {
+func newVendorListFetcherTCF1(cfg config.GDPR) func(ctx context.Context, id uint16) (vendorlist.VendorList, error) {
+	if len(cfg.TCF1.FallbackGVLPath) == 0 {
+		return func(_ context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) {
 			return nil, makeVendorListNotFoundError(vendorListVersion)
 		}
 	}
 
-	if tcfSpecVersion == tcf1SpecVersion {
-		fallback = loadFallbackGVL(cfg.TCF1.FallbackGVLPath)
+	fallback := loadFallbackGVLForTCF1(cfg.TCF1.FallbackGVLPath)
+	return func(_ context.Context, _ uint16) (vendorlist.VendorList, error) {
+		return fallback, nil
+	}
+}
 
-		return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) {
-			return fallback, nil
-		}
+func loadFallbackGVLForTCF1(fallbackGVLPath string) vendorlist.VendorList {
+	fallbackContents, err := ioutil.ReadFile(fallbackGVLPath)
+	if err != nil {
+		glog.Fatalf("Error reading from file %s: %v", fallbackGVLPath, err)
+	}
+
+	fallback, err := vendorlist.ParseEagerly(fallbackContents)
+	if err != nil {
+		glog.Fatalf("Error processing default GVL from %s: %v", fallbackGVLPath, err)
 	}
+	return fallback
+}
 
-	cacheSave, cacheLoad := newVendorListCache(fallback)
+func newVendorListFetcherTCF2(initCtx context.Context, cfg config.GDPR, client *http.Client, urlMaker func(uint16) string) func(ctx context.Context, id uint16) (vendorlist.VendorList, error) {
+	cacheSave, cacheLoad := newVendorListCache()
 
 	preloadContext, cancel := context.WithTimeout(initCtx, cfg.Timeouts.InitTimeout())
 	defer cancel()
-	preloadCache(preloadContext, client, urlMaker, cacheSave, tcfSpecVersion)
+	preloadCache(preloadContext, client, urlMaker, cacheSave)
 
-	saveOneRateLimited := newOccasionalSaver(cfg.Timeouts.ActiveTimeout(), tcfSpecVersion)
+	saveOneRateLimited := newOccasionalSaver(cfg.Timeouts.ActiveTimeout())
 	return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) {
 		// Attempt To Load From Cache
 		if list := cacheLoad(vendorListVersion); list != nil {
@@ -58,7 +68,7 @@ func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http
 
 		// Attempt To Download
 		// - May not add to cache immediately.
-		saveOneRateLimited(ctx, client, urlMaker(vendorListVersion, tcfSpecVersion), cacheSave)
+		saveOneRateLimited(ctx, client, urlMaker(vendorListVersion), cacheSave)
 
 		// Attempt To Load From Cache Again
 		// - May have been added by the call to saveOneRateLimited.
@@ -66,11 +76,6 @@ func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http
 			return list, nil
 		}
 
-		// Attempt To Use Hardcoded Fallback
-		if fallback != nil {
-			return fallback, nil
-		}
-
 		// Give Up
 		return nil, makeVendorListNotFoundError(vendorListVersion)
 	}
@@ -81,27 +86,24 @@ func makeVendorListNotFoundError(vendorListVersion uint16) error {
 }
 
 // preloadCache saves all the known versions of the vendor list for future use.
-func preloadCache(ctx context.Context, client *http.Client, urlMaker func(uint16, uint8) string, saver saveVendors, tcfSpecVersion uint8) {
-	latestVersion := saveOne(ctx, client, urlMaker(0, tcfSpecVersion), saver, tcfSpecVersion)
+func preloadCache(ctx context.Context, client *http.Client, urlMaker func(uint16) string, saver saveVendors) {
+	latestVersion := saveOne(ctx, client, urlMaker(0), saver)
 
-	for i := uint16(1); i < latestVersion; i++ {
-		saveOne(ctx, client, urlMaker(i, tcfSpecVersion), saver, tcfSpecVersion)
+	// The GVL for TCF2 has no vendors defined in its first version. It's very unlikely to be used, so don't preload it.
+	firstVersionToLoad := uint16(2)
+
+	for i := firstVersionToLoad; i < latestVersion; i++ {
+		saveOne(ctx, client, urlMaker(i), saver)
 	}
 }
 
 // Make a URL which can be used to fetch a given version of the Global Vendor List. If the version is 0,
 // this will fetch the latest version.
-func vendorListURLMaker(vendorListVersion uint16, tcfSpecVersion uint8) string {
-	if tcfSpecVersion == tcf2SpecVersion {
-		if vendorListVersion == 0 {
-			return "https://vendor-list.consensu.org/v2/vendor-list.json"
-		}
-		return "https://vendor-list.consensu.org/v2/archives/vendor-list-v" + strconv.Itoa(int(vendorListVersion)) + ".json"
-	}
+func vendorListURLMaker(vendorListVersion uint16) string {
 	if vendorListVersion == 0 {
-		return "https://vendor-list.consensu.org/vendorlist.json"
+		return "https://vendor-list.consensu.org/v2/vendor-list.json"
 	}
-	return "https://vendor-list.consensu.org/v-" + strconv.Itoa(int(vendorListVersion)) + "/vendorlist.json"
+	return "https://vendor-list.consensu.org/v2/archives/vendor-list-v" + strconv.Itoa(int(vendorListVersion)) + ".json"
 }
 
 // newOccasionalSaver returns a wrapped version of saveOne() which only activates every few minutes.
@@ -109,7 +111,7 @@ func vendorListURLMaker(vendorListVersion uint16, tcfSpecVersion uint8) string {
 // The goal here is to update quickly when new versions of the VendorList are released, but not wreck
 // server performance if a bad CMP starts sending us malformed consent strings that advertize a version
 // that doesn't exist yet.
-func newOccasionalSaver(timeout time.Duration, tcfSpecVersion uint8) func(ctx context.Context, client *http.Client, url string, saver saveVendors) {
+func newOccasionalSaver(timeout time.Duration) func(ctx context.Context, client *http.Client, url string, saver saveVendors) {
 	lastSaved := &atomic.Value{}
 	lastSaved.Store(time.Time{})
 
@@ -120,13 +122,13 @@ func newOccasionalSaver(timeout time.Duration, tcfSpecVersion uint8) func(ctx co
 		if timeSinceLastSave.Minutes() > 10 {
 			withTimeout, cancel := context.WithTimeout(ctx, timeout)
 			defer cancel()
-			saveOne(withTimeout, client, url, saver, tcfSpecVersion)
+			saveOne(withTimeout, client, url, saver)
 			lastSaved.Store(now)
 		}
 	}
 }
 
-func saveOne(ctx context.Context, client *http.Client, url string, saver saveVendors, tcfSpecVersion uint8) uint16 {
+func saveOne(ctx context.Context, client *http.Client, url string, saver saveVendors) uint16 {
 	req, err := http.NewRequest("GET", url, nil)
 	if err != nil {
 		glog.Errorf("Failed to build GET %s request. Cookie syncs may be affected: %v", url, err)
@@ -150,11 +152,7 @@ func saveOne(ctx context.Context, client *http.Client, url string, saver saveVen
 		return 0
 	}
 	var newList api.VendorList
-	if tcfSpecVersion == tcf2SpecVersion {
-		newList, err = vendorlist2.ParseEagerly(respBody)
-	} else {
-		newList, err = vendorlist.ParseEagerly(respBody)
-	}
+	newList, err = vendorlist2.ParseEagerly(respBody)
 	if err != nil {
 		glog.Errorf("GET %s returned malformed JSON. Cookie syncs may be affected. Error was %v. Body was %s", url, err, string(respBody))
 		return 0
@@ -164,7 +162,7 @@ func saveOne(ctx context.Context, client *http.Client, url string, saver saveVen
 	return newList.Version()
 }
 
-func newVendorListCache(fallbackVL api.VendorList) (save func(vendorListVersion uint16, list api.VendorList), load func(vendorListVersion uint16) api.VendorList) {
+func newVendorListCache() (save func(vendorListVersion uint16, list api.VendorList), load func(vendorListVersion uint16) api.VendorList) {
 	cache := &sync.Map{}
 
 	save = func(vendorListVersion uint16, list api.VendorList) {
@@ -180,16 +178,3 @@ func newVendorListCache(fallbackVL api.VendorList) (save func(vendorListVersion
 	}
 	return
 }
-
-func loadFallbackGVL(fallbackGVLPath string) vendorlist.VendorList {
-	fallbackContents, err := ioutil.ReadFile(fallbackGVLPath)
-	if err != nil {
-		glog.Fatalf("Error reading from file %s: %v", fallbackGVLPath, err)
-	}
-
-	fallback, err := vendorlist.ParseEagerly(fallbackContents)
-	if err != nil {
-		glog.Fatalf("Error processing default GVL from %s: %v", fallbackGVLPath, err)
-	}
-	return fallback
-}
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 83cf0c96..04d0eb22 100755
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -17,12 +17,6 @@ const schemaDirectory = "static/bidder-params"
 // BidderName refers to a core bidder id or an alias id.
 type BidderName string
 
-// BidderNameGeneral is reserved for non-bidder specific messages when using a map keyed on the bidder name.
-const BidderNameGeneral = BidderName("general")
-
-// BidderNameContext is reserved for first party data.
-const BidderNameContext = BidderName("context")
-
 func (name BidderName) MarshalJSON() ([]byte, error) {
 	return []byte(name), nil
 }
@@ -34,6 +28,45 @@ func (name *BidderName) String() string {
 	return string(*name)
 }
 
+// Names of reserved bidders. These names may not be used by a core bidder or alias.
+const (
+	BidderReservedAll     BidderName = "all"     // Reserved for the /info/bidders/all endpoint.
+	BidderReservedContext BidderName = "context" // Reserved for first party data.
+	BidderReservedData    BidderName = "data"    // Reserved for first party data.
+	BidderReservedGeneral BidderName = "general" // Reserved for non-bidder specific messages when using a map keyed on the bidder name.
+	BidderReservedPrebid  BidderName = "prebid"  // Reserved for Prebid Server configuration.
+	BidderReservedSKAdN   BidderName = "skadn"   // Reserved for SKAdNetwork OpenRTB extension.
+)
+
+// IsBidderNameReserved returns true if the specified name is a case insensitive match for a reserved bidder name.
+func IsBidderNameReserved(name string) bool {
+	if strings.EqualFold(name, string(BidderReservedAll)) {
+		return true
+	}
+
+	if strings.EqualFold(name, string(BidderReservedContext)) {
+		return true
+	}
+
+	if strings.EqualFold(name, string(BidderReservedData)) {
+		return true
+	}
+
+	if strings.EqualFold(name, string(BidderReservedGeneral)) {
+		return true
+	}
+
+	if strings.EqualFold(name, string(BidderReservedSKAdN)) {
+		return true
+	}
+
+	if strings.EqualFold(name, string(BidderReservedPrebid)) {
+		return true
+	}
+
+	return false
+}
+
 // Names of core bidders. These names *must* match the bidder code in Prebid.js if an adapter also exists in that
 // project. You may *not* use the name 'general' as that is reserved for general error messages nor 'context' as
 // that is reserved for first party data.
@@ -79,6 +112,7 @@ const (
 	BidderEmxDigital       BidderName = "emx_digital"
 	BidderEngageBDR        BidderName = "engagebdr"
 	BidderEPlanning        BidderName = "eplanning"
+	BidderEpom             BidderName = "epom"
 	BidderGamma            BidderName = "gamma"
 	BidderGamoshi          BidderName = "gamoshi"
 	BidderGrid             BidderName = "grid"
@@ -181,6 +215,7 @@ func CoreBidderNames() []BidderName {
 		BidderEmxDigital,
 		BidderEngageBDR,
 		BidderEPlanning,
+		BidderEpom,
 		BidderGamma,
 		BidderGamoshi,
 		BidderGrid,
@@ -252,6 +287,16 @@ func BuildBidderMap() map[string]BidderName {
 	return lookup
 }
 
+// BuildBidderStringSlice builds a slioce of strings for each BidderName.
+func BuildBidderStringSlice() []string {
+	coreBidders := CoreBidderNames()
+	slice := make([]string, len(coreBidders))
+	for i, name := range CoreBidderNames() {
+		slice[i] = string(name)
+	}
+	return slice
+}
+
 func BuildBidderNameHashSet() map[string]struct{} {
 	hashSet := make(map[string]struct{})
 	for _, name := range CoreBidderNames() {
diff --git a/openrtb_ext/imp_epom.go b/openrtb_ext/imp_epom.go
new file mode 100644
index 00000000..a99f60ef
--- /dev/null
+++ b/openrtb_ext/imp_epom.go
@@ -0,0 +1,4 @@
+package openrtb_ext
+
+type ImpExtEpom struct {
+}
diff --git a/router/router.go b/router/router.go
index 48580f85..7a088bbf 100644
--- a/router/router.go
+++ b/router/router.go
@@ -232,12 +232,18 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	}
 
 	p, _ := filepath.Abs(infoDirectory)
-	bidderInfos := adapters.ParseBidderInfos(cfg.Adapters, p, openrtb_ext.CoreBidderNames())
+	bidderInfos, err := config.LoadBidderInfoFromDisk(p, cfg.Adapters, openrtb_ext.BuildBidderStringSlice())
+	if err != nil {
+		glog.Fatal(err)
+	}
 
 	activeBidders := exchange.GetActiveBidders(bidderInfos)
 	disabledBidders := exchange.GetDisabledBiddersErrorMessages(bidderInfos)
 
 	defaultAliases, defReqJSON := readDefaultRequest(cfg.DefReqConfig)
+	if err := validateDefaultAliases(defaultAliases); err != nil {
+		glog.Fatal(err)
+	}
 
 	syncers := usersyncers.NewSyncerMap(cfg)
 	gdprPerms := gdpr.NewPermissions(context.Background(), cfg.GDPR, adapters.GDPRAwareSyncerIDs(syncers), generalHttpClient)
@@ -247,7 +253,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 
 	adapters, adaptersErrs := exchange.BuildAdapters(generalHttpClient, cfg, bidderInfos, r.MetricsEngine)
 	if len(adaptersErrs) > 0 {
-		errs := errortypes.NewAggregateErrors("Failed to initialize adapters", adaptersErrs)
+		errs := errortypes.NewAggregateError("Failed to initialize adapters", adaptersErrs)
 		glog.Fatalf("%v", errs)
 	}
 
@@ -259,7 +265,6 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	}
 
 	ampEndpoint, err := openrtb2.NewAmpEndpoint(theExchange, paramsValidator, ampFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders)
-
 	if err != nil {
 		glog.Fatalf("Failed to create the amp endpoint handler. %v", err)
 	}
@@ -278,8 +283,8 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
 	r.POST("/openrtb2/auction", openrtbEndpoint)
 	r.POST("/openrtb2/video", videoEndpoint)
 	r.GET("/openrtb2/amp", ampEndpoint)
-	r.GET("/info/bidders", infoEndpoints.NewBiddersEndpoint(defaultAliases))
-	r.GET("/info/bidders/:bidderName", infoEndpoints.NewBidderDetailsEndpoint(bidderInfos, defaultAliases))
+	r.GET("/info/bidders", infoEndpoints.NewBiddersEndpoint(bidderInfos, defaultAliases))
+	r.GET("/info/bidders/:bidderName", infoEndpoints.NewBiddersDetailEndpoint(bidderInfos, cfg.Adapters, defaultAliases))
 	r.GET("/bidders/params", NewJsonDirectoryServer(schemaDirectory, paramsValidator, defaultAliases))
 	r.POST("/cookie_sync", endpoints.NewCookieSyncEndpoint(syncers, cfg, gdprPerms, r.MetricsEngine, pbsAnalytics, activeBidders))
 	r.GET("/status", endpoints.NewStatusEndpoint(cfg.StatusResponse))
@@ -374,3 +379,19 @@ func readDefaultRequest(defReqConfig config.DefReqConfig) (map[string]string, []
 	}
 	return aliases, []byte{}
 }
+
+func validateDefaultAliases(aliases map[string]string) error {
+	var errs []error
+
+	for alias := range aliases {
+		if openrtb_ext.IsBidderNameReserved(alias) {
+			errs = append(errs, fmt.Errorf("alias %s is a reserved bidder name and cannot be used", alias))
+		}
+	}
+
+	if len(errs) > 0 {
+		return errortypes.NewAggregateError("default request alias errors", errs)
+	}
+
+	return nil
+}
diff --git a/static/bidder-info/epom.yaml b/static/bidder-info/epom.yaml
new file mode 100644
index 00000000..32afa346
--- /dev/null
+++ b/static/bidder-info/epom.yaml
@@ -0,0 +1,14 @@
+maintainer:
+  email: "support@epom.com"
+modifyingVastXmlAllowed: true
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
diff --git a/static/bidder-params/adform.json b/static/bidder-params/adform.json
index f0b8c7a6..6c8a6773 100644
--- a/static/bidder-params/adform.json
+++ b/static/bidder-params/adform.json
@@ -25,7 +25,7 @@
     },
     "cdims": {
       "type": "string",
-      "description": "Comma-separated creative dimentions.",
+      "description": "Comma-separated creative dimensions.",
       "pattern": "(^\\d+x\\d+)(,\\d+x\\d+)*$"
     },
     "minp": {
diff --git a/static/bidder-params/connectad.json b/static/bidder-params/connectad.json
index 961b3b71..15f4ab66 100644
--- a/static/bidder-params/connectad.json
+++ b/static/bidder-params/connectad.json
@@ -16,9 +16,9 @@
       },
       "bidfloor": {
         "type": "number",
-        "description": "Requestes Floorprice"
+        "description": "Requests Floorprice"
       }
     },
     "required": ["networkId", "siteId"]
   }
-  
\ No newline at end of file
+  
diff --git a/static/bidder-params/epom.json b/static/bidder-params/epom.json
new file mode 100644
index 00000000..ee8c14e4
--- /dev/null
+++ b/static/bidder-params/epom.json
@@ -0,0 +1,8 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Epom Adapter Params",
+  "description": "A schema which validates params accepted by the Epom adapter",
+  "type": "object",
+
+  "properties": {}
+}
diff --git a/static/bidder-params/invibes.json b/static/bidder-params/invibes.json
index 11d276f8..5545b174 100644
--- a/static/bidder-params/invibes.json
+++ b/static/bidder-params/invibes.json
@@ -23,7 +23,7 @@
           "type": "boolean"
         }
       },
-      "description": "Parameters used for debugging porposes"
+      "description": "Parameters used for debugging purposes"
     }
   },
   "required": ["placementId"]
diff --git a/static/bidder-params/nanointeractive.json b/static/bidder-params/nanointeractive.json
index 707dff2f..aacd3154 100644
--- a/static/bidder-params/nanointeractive.json
+++ b/static/bidder-params/nanointeractive.json
@@ -6,7 +6,7 @@
   "properties": {
     "pid": {
       "type": "string",
-      "description": "Placement idd"
+      "description": "Placement id"
     },
     "nq": {
       "type": "array",
@@ -29,4 +29,4 @@
     }
   },
   "required": ["pid"]
-}
\ No newline at end of file
+}
diff --git a/static/bidder-params/nobid.json b/static/bidder-params/nobid.json
index 576dbfec..6293c40c 100644
--- a/static/bidder-params/nobid.json
+++ b/static/bidder-params/nobid.json
@@ -7,12 +7,12 @@
     "properties": {
       "siteId": {
         "type": "integer",
-        "description": "A Required ID which identifies the NoBid site. The siteId paramerter is provided by your NoBid account manager."
+        "description": "A Required ID which identifies the NoBid site. The siteId parameter is provided by your NoBid account manager."
       }, "placementId": { 
     	  "type": "integer",
-    	  "description": "An oprional ID which identifies an adunit in a site. The placementId paramerter is provided by your NoBid account manager."
+    	  "description": "An optional ID which identifies an adunit in a site. The placementId parameter is provided by your NoBid account manager."
       }
     },
     "required": ["siteId"]
   }
-  
\ No newline at end of file
+  


Test patch:
diff --git a/adapters/epom/epom_test.go b/adapters/epom/epom_test.go
new file mode 100644
index 00000000..7991f4ea
--- /dev/null
+++ b/adapters/epom/epom_test.go
@@ -0,0 +1,20 @@
+package epom
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderEpom, config.Adapter{
+		Endpoint: "https://an.epom.com/ortb"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "epomtest", bidder)
+}
diff --git a/adapters/epom/epomtest/exemplary/simple-app-banner.json b/adapters/epom/epomtest/exemplary/simple-app-banner.json
new file mode 100644
index 00000000..f24b5b6e
--- /dev/null
+++ b/adapters/epom/epomtest/exemplary/simple-app-banner.json
@@ -0,0 +1,105 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app": {
+      "bundle": "com.prebid"
+    },
+    "device": {
+      "ip":"238.89.104.156"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 50
+            }
+          ]
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://an.epom.com/ortb",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "app": {
+            "bundle": "com.prebid"
+          },
+          "device": {
+            "ip":"238.89.104.156"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 50
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "epom",
+              "bid": [
+                {
+                  "id": "1",
+                  "impid": "test-imp-id",
+                  "price": 0.500000,
+                  "adm": "test-adm",
+                  "crid": "test-crid",
+                  "h": 50,
+                  "w": 300
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "1",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "test-adm",
+            "crid": "test-crid",
+            "w": 300,
+            "h": 50
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/exemplary/simple-app-native.json b/adapters/epom/epomtest/exemplary/simple-app-native.json
new file mode 100644
index 00000000..1504870b
--- /dev/null
+++ b/adapters/epom/epomtest/exemplary/simple-app-native.json
@@ -0,0 +1,91 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app": {
+      "bundle": "com.prebid"
+    },
+    "device": {
+      "ip":"238.89.104.156"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "native": {
+          "ver": "1.1",
+          "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":60,\"hmin\":60,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":75}},{\"id\":4,\"required\":0,\"data\":{\"type\":6,\"len\":1000}},{\"id\":5,\"required\":0,\"data\":{\"type\":7,\"len\":1000}},{\"id\":6,\"required\":0,\"data\":{\"type\":11,\"len\":1000}}]}"
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://an.epom.com/ortb",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "app": {
+            "bundle": "com.prebid"
+          },
+          "device": {
+            "ip":"238.89.104.156"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "native": {
+                "ver": "1.1",
+                "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":60,\"hmin\":60,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":75}},{\"id\":4,\"required\":0,\"data\":{\"type\":6,\"len\":1000}},{\"id\":5,\"required\":0,\"data\":{\"type\":7,\"len\":1000}},{\"id\":6,\"required\":0,\"data\":{\"type\":11,\"len\":1000}}]}"
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "epom",
+              "bid": [{
+                "id": "1",
+                "impid": "test-imp-id",
+                "price": 0.500000,
+                "adm": "some-test-adm",
+                "crid": "test-crid"
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "1",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-adm",
+            "crid": "test-crid"
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/exemplary/simple-app-video.json b/adapters/epom/epomtest/exemplary/simple-app-video.json
new file mode 100644
index 00000000..af210467
--- /dev/null
+++ b/adapters/epom/epomtest/exemplary/simple-app-video.json
@@ -0,0 +1,115 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app": {
+      "bundle": "com.prebid"
+    },
+    "device": {
+      "ip":"238.89.104.156"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "protocols": [
+            2,
+            3,
+            5,
+            6
+          ],
+          "w": 1024,
+          "h": 576
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://an.epom.com/ortb",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "app": {
+            "bundle": "com.prebid"
+          },
+          "device": {
+            "ip":"238.89.104.156"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "protocols": [
+                  2,
+                  3,
+                  5,
+                  6
+                ],
+                "w": 1024,
+                "h": 576
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "cur": "USD",
+          "seatbid": [
+            {
+              "seat": "epom",
+              "bid": [
+                {
+                  "id": "1",
+                  "impid": "test-imp-id",
+                  "price": 0.500000,
+                  "adm": "some-test-adm",
+                  "crid": "test-crid",
+                  "w": 1024,
+                  "h": 576
+                }
+              ]
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "1",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-adm",
+            "crid": "test-crid",
+            "w": 1024,
+            "h": 576
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/exemplary/simple-site-banner.json b/adapters/epom/epomtest/exemplary/simple-site-banner.json
new file mode 100644
index 00000000..65d05977
--- /dev/null
+++ b/adapters/epom/epomtest/exemplary/simple-site-banner.json
@@ -0,0 +1,105 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "prebid.org"
+    },
+    "device": {
+      "ip":"238.89.104.156"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 50
+            }
+          ]
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://an.epom.com/ortb",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "site": {
+            "page": "prebid.org"
+          },
+          "device": {
+            "ip":"238.89.104.156"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 50
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "epom",
+              "bid": [
+                {
+                  "id": "1",
+                  "impid": "test-imp-id",
+                  "price": 0.500000,
+                  "adm": "some-test-adm",
+                  "crid": "test-crid",
+                  "h": 50,
+                  "w": 300
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "1",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-adm",
+            "crid": "test-crid",
+            "w": 300,
+            "h": 50
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/exemplary/simple-site-native.json b/adapters/epom/epomtest/exemplary/simple-site-native.json
new file mode 100644
index 00000000..0ae393f5
--- /dev/null
+++ b/adapters/epom/epomtest/exemplary/simple-site-native.json
@@ -0,0 +1,92 @@
+
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "prebid.org"
+    },
+    "device": {
+      "ip":"238.89.104.156"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "native": {
+          "ver": "1.1",
+          "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":60,\"hmin\":60,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":75}},{\"id\":4,\"required\":0,\"data\":{\"type\":6,\"len\":1000}},{\"id\":5,\"required\":0,\"data\":{\"type\":7,\"len\":1000}},{\"id\":6,\"required\":0,\"data\":{\"type\":11,\"len\":1000}}]}"
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://an.epom.com/ortb",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "site": {
+            "page": "prebid.org"
+          },
+          "device": {
+            "ip":"238.89.104.156"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "native": {
+                "ver": "1.1",
+                "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":60,\"hmin\":60,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":75}},{\"id\":4,\"required\":0,\"data\":{\"type\":6,\"len\":1000}},{\"id\":5,\"required\":0,\"data\":{\"type\":7,\"len\":1000}},{\"id\":6,\"required\":0,\"data\":{\"type\":11,\"len\":1000}}]}"
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "epom",
+              "bid": [{
+                "id": "1",
+                "impid": "test-imp-id",
+                "price": 0.500000,
+                "adm": "some-test-adm",
+                "crid": "test-crid"
+              }]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "1",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-adm",
+            "crid": "test-crid"
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/exemplary/simple-site-video.json b/adapters/epom/epomtest/exemplary/simple-site-video.json
new file mode 100644
index 00000000..fdf89011
--- /dev/null
+++ b/adapters/epom/epomtest/exemplary/simple-site-video.json
@@ -0,0 +1,115 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "prebid.org"
+    },
+    "device": {
+      "ip":"238.89.104.156"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "protocols": [
+            2,
+            3,
+            5,
+            6
+          ],
+          "w": 1024,
+          "h": 576
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://an.epom.com/ortb",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "site": {
+            "page": "prebid.org"
+          },
+          "device": {
+            "ip":"238.89.104.156"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "protocols": [
+                  2,
+                  3,
+                  5,
+                  6
+                ],
+                "w": 1024,
+                "h": 576
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "cur": "USD",
+          "seatbid": [
+            {
+              "seat": "epom",
+              "bid": [
+                {
+                  "id": "1",
+                  "impid": "test-imp-id",
+                  "price": 0.500000,
+                  "adm": "some-test-adm",
+                  "crid": "test-crid",
+                  "w": 1024,
+                  "h": 576
+                }
+              ]
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "1",
+            "impid": "test-imp-id",
+            "price": 0.5,
+            "adm": "some-test-adm",
+            "crid": "test-crid",
+            "w": 1024,
+            "h": 576
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/params/race/banner.json b/adapters/epom/epomtest/params/race/banner.json
new file mode 100644
index 00000000..2c63c085
--- /dev/null
+++ b/adapters/epom/epomtest/params/race/banner.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/adapters/epom/epomtest/params/race/native.json b/adapters/epom/epomtest/params/race/native.json
new file mode 100644
index 00000000..7a73a41b
--- /dev/null
+++ b/adapters/epom/epomtest/params/race/native.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/adapters/epom/epomtest/params/race/video.json b/adapters/epom/epomtest/params/race/video.json
new file mode 100644
index 00000000..2c63c085
--- /dev/null
+++ b/adapters/epom/epomtest/params/race/video.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/adapters/epom/epomtest/supplemental/bad-request-no-device.json b/adapters/epom/epomtest/supplemental/bad-request-no-device.json
new file mode 100644
index 00000000..87267fca
--- /dev/null
+++ b/adapters/epom/epomtest/supplemental/bad-request-no-device.json
@@ -0,0 +1,27 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "prebid.org"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 50
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "ipv4 address is required field",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/supplemental/bad-request-no-ip.json b/adapters/epom/epomtest/supplemental/bad-request-no-ip.json
new file mode 100644
index 00000000..42db8fc4
--- /dev/null
+++ b/adapters/epom/epomtest/supplemental/bad-request-no-ip.json
@@ -0,0 +1,29 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "prebid.org"
+    },
+    "device": {
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 50
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "ipv4 address is required field",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/supplemental/bad-response-bad-request-error.json b/adapters/epom/epomtest/supplemental/bad-response-bad-request-error.json
new file mode 100644
index 00000000..b278bc9c
--- /dev/null
+++ b/adapters/epom/epomtest/supplemental/bad-response-bad-request-error.json
@@ -0,0 +1,72 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app": {
+      "bundle": "com.prebid"
+    },
+    "device": {
+      "ip":"238.89.104.156"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 50
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://an.epom.com/ortb",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "app": {
+            "bundle": "com.prebid"
+          },
+          "device": {
+            "ip":"238.89.104.156"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 50
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 400,
+        "body": {
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Bad request to dsp",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/supplemental/bad-response-no-bid-obj-error.json b/adapters/epom/epomtest/supplemental/bad-response-no-bid-obj-error.json
new file mode 100644
index 00000000..47eea3a4
--- /dev/null
+++ b/adapters/epom/epomtest/supplemental/bad-response-no-bid-obj-error.json
@@ -0,0 +1,75 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "prebid.org"
+    },
+    "device": {
+      "ip":"238.89.104.156"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 50
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://an.epom.com/ortb",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "site": {
+            "page": "prebid.org"
+          },
+          "device": {
+            "ip":"238.89.104.156"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 50
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "cur": "USD",
+          "seatbid": []
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "No bids in response",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/supplemental/bad-response-no-seatbid-error.json b/adapters/epom/epomtest/supplemental/bad-response-no-seatbid-error.json
new file mode 100644
index 00000000..ef8c7698
--- /dev/null
+++ b/adapters/epom/epomtest/supplemental/bad-response-no-seatbid-error.json
@@ -0,0 +1,74 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "prebid.org"
+    },
+    "device": {
+      "ip":"238.89.104.156"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 50
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://an.epom.com/ortb",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "site": {
+            "page": "prebid.org"
+          },
+          "device": {
+            "ip":"238.89.104.156"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 50
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "No bids in response",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/supplemental/bad-response-server-internal-error.json b/adapters/epom/epomtest/supplemental/bad-response-server-internal-error.json
new file mode 100644
index 00000000..402990eb
--- /dev/null
+++ b/adapters/epom/epomtest/supplemental/bad-response-server-internal-error.json
@@ -0,0 +1,72 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app": {
+      "bundle": "com.prebid"
+    },
+    "device": {
+      "ip":"238.89.104.156"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 50
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://an.epom.com/ortb",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "app": {
+            "bundle": "com.prebid"
+          },
+          "device": {
+            "ip":"238.89.104.156"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 50
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 500,
+        "body": {
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 500. Dsp server internal error",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/epom/epomtest/supplemental/bad-response-unexpected-error.json b/adapters/epom/epomtest/supplemental/bad-response-unexpected-error.json
new file mode 100644
index 00000000..23e5e974
--- /dev/null
+++ b/adapters/epom/epomtest/supplemental/bad-response-unexpected-error.json
@@ -0,0 +1,72 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": {
+      "page": "prebid.org"
+    },
+    "device": {
+      "ip":"238.89.104.156"
+    },
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 50
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://an.epom.com/ortb",
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ]
+        },
+        "body": {
+          "id": "test-request-id",
+          "site": {
+            "page": "prebid.org"
+          },
+          "device": {
+            "ip":"238.89.104.156"
+          },
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 50
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 301,
+        "body": {
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 301",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/info_test.go b/adapters/infoawarebidder_test.go
similarity index 74%
rename from adapters/info_test.go
rename to adapters/infoawarebidder_test.go
index ce0f8869..6053f18a 100644
--- a/adapters/info_test.go
+++ b/adapters/infoawarebidder_test.go
@@ -2,7 +2,6 @@ package adapters_test
 
 import (
 	"errors"
-	"strings"
 	"testing"
 
 	"github.com/mxmCherry/openrtb"
@@ -15,14 +14,14 @@ import (
 
 func TestAppNotSupported(t *testing.T) {
 	bidder := &mockBidder{}
-	info := adapters.BidderInfo{
-		Capabilities: &adapters.CapabilitiesInfo{
-			Site: &adapters.PlatformInfo{
+	info := config.BidderInfo{
+		Capabilities: &config.CapabilitiesInfo{
+			Site: &config.PlatformInfo{
 				MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner},
 			},
 		},
 	}
-	constrained := adapters.EnforceBidderInfo(bidder, info)
+	constrained := adapters.BuildInfoAwareBidder(bidder, info)
 	bids, errs := constrained.MakeRequests(&openrtb.BidRequest{
 		Imp: []openrtb.Imp{{ID: "imp-1", Banner: &openrtb.Banner{}}},
 		App: &openrtb.App{},
@@ -37,14 +36,14 @@ func TestAppNotSupported(t *testing.T) {
 
 func TestSiteNotSupported(t *testing.T) {
 	bidder := &mockBidder{}
-	info := adapters.BidderInfo{
-		Capabilities: &adapters.CapabilitiesInfo{
-			App: &adapters.PlatformInfo{
+	info := config.BidderInfo{
+		Capabilities: &config.CapabilitiesInfo{
+			App: &config.PlatformInfo{
 				MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner},
 			},
 		},
 	}
-	constrained := adapters.EnforceBidderInfo(bidder, info)
+	constrained := adapters.BuildInfoAwareBidder(bidder, info)
 	bids, errs := constrained.MakeRequests(&openrtb.BidRequest{
 		Imp:  []openrtb.Imp{{ID: "imp-1", Banner: &openrtb.Banner{}}},
 		Site: &openrtb.Site{},
@@ -59,18 +58,18 @@ func TestSiteNotSupported(t *testing.T) {
 
 func TestImpFiltering(t *testing.T) {
 	bidder := &mockBidder{}
-	info := adapters.BidderInfo{
-		Capabilities: &adapters.CapabilitiesInfo{
-			Site: &adapters.PlatformInfo{
+	info := config.BidderInfo{
+		Capabilities: &config.CapabilitiesInfo{
+			Site: &config.PlatformInfo{
 				MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeVideo},
 			},
-			App: &adapters.PlatformInfo{
+			App: &config.PlatformInfo{
 				MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner},
 			},
 		},
 	}
 
-	constrained := adapters.EnforceBidderInfo(bidder, info)
+	constrained := adapters.BuildInfoAwareBidder(bidder, info)
 
 	testCases := []struct {
 		description    string
@@ -195,33 +194,3 @@ func (m *mockBidder) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters
 func (m *mockBidder) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
 	return nil, []error{errors.New("mock MakeBids error")}
 }
-
-func blankAdapterConfig(bidderName openrtb_ext.BidderName) map[string]config.Adapter {
-	adapters := make(map[string]config.Adapter)
-	adapters[strings.ToLower(string(bidderName))] = config.Adapter{}
-
-	return adapters
-}
-
-func TestParsing(t *testing.T) {
-	mockBidderName := openrtb_ext.BidderName("someBidder")
-	infos := adapters.ParseBidderInfos(blankAdapterConfig(mockBidderName), "./adapterstest/bidder-info", []openrtb_ext.BidderName{mockBidderName})
-	if infos[string(mockBidderName)].Maintainer.Email != "some-email@domain.com" {
-		t.Errorf("Bad maintainer email. Got %s", infos[string(mockBidderName)].Maintainer.Email)
-	}
-
-	assert.Equal(t, true, infos.IsActive(mockBidderName))
-
-	assert.Equal(t, true, infos.HasAppSupport(mockBidderName))
-	assert.Equal(t, true, infos.HasSiteSupport(mockBidderName))
-
-	assert.Equal(t, true, infos.SupportsAppMediaType(mockBidderName, openrtb_ext.BidTypeBanner))
-	assert.Equal(t, false, infos.SupportsAppMediaType(mockBidderName, openrtb_ext.BidTypeVideo))
-	assert.Equal(t, false, infos.SupportsAppMediaType(mockBidderName, openrtb_ext.BidTypeAudio))
-	assert.Equal(t, true, infos.SupportsAppMediaType(mockBidderName, openrtb_ext.BidTypeNative))
-
-	assert.Equal(t, true, infos.SupportsWebMediaType(mockBidderName, openrtb_ext.BidTypeBanner))
-	assert.Equal(t, true, infos.SupportsWebMediaType(mockBidderName, openrtb_ext.BidTypeVideo))
-	assert.Equal(t, false, infos.SupportsWebMediaType(mockBidderName, openrtb_ext.BidTypeAudio))
-	assert.Equal(t, true, infos.SupportsWebMediaType(mockBidderName, openrtb_ext.BidTypeNative))
-}
diff --git a/config/bidderinfo_test.go b/config/bidderinfo_test.go
new file mode 100644
index 00000000..2d9d53ec
--- /dev/null
+++ b/config/bidderinfo_test.go
@@ -0,0 +1,188 @@
+package config
+
+import (
+	"errors"
+	"strings"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+)
+
+const testInfoFilesPath = "./test/bidder-info"
+const testYAML = `
+maintainer:
+  email: "some-email@domain.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
+`
+
+func TestLoadBidderInfoFromDisk(t *testing.T) {
+	bidder := "someBidder"
+
+	adapterConfigs := make(map[string]Adapter)
+	adapterConfigs[strings.ToLower(bidder)] = Adapter{}
+
+	infos, err := LoadBidderInfoFromDisk(testInfoFilesPath, adapterConfigs, []string{bidder})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expected := map[string]BidderInfo{
+		bidder: {
+			Enabled: true,
+			Maintainer: &MaintainerInfo{
+				Email: "some-email@domain.com",
+			},
+			Capabilities: &CapabilitiesInfo{
+				App: &PlatformInfo{
+					MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeNative},
+				},
+				Site: &PlatformInfo{
+					MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+				},
+			},
+		},
+	}
+	assert.Equal(t, expected, infos)
+}
+
+func TestLoadBidderInfo(t *testing.T) {
+	bidder := "someBidder" // important to be mixed case for tests
+
+	testCases := []struct {
+		description   string
+		givenConfigs  map[string]Adapter
+		givenContent  string
+		givenError    error
+		expectedInfo  map[string]BidderInfo
+		expectedError string
+	}{
+		{
+			description:  "Enabled",
+			givenConfigs: map[string]Adapter{strings.ToLower(bidder): {}},
+			givenContent: testYAML,
+			expectedInfo: map[string]BidderInfo{
+				bidder: {
+					Enabled: true,
+					Maintainer: &MaintainerInfo{
+						Email: "some-email@domain.com",
+					},
+					Capabilities: &CapabilitiesInfo{
+						App: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeNative},
+						},
+						Site: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+					},
+				},
+			},
+		},
+		{
+			description:  "Disabled - Bidder Not Configured",
+			givenConfigs: map[string]Adapter{},
+			givenContent: testYAML,
+			expectedInfo: map[string]BidderInfo{
+				bidder: {
+					Enabled: false,
+					Maintainer: &MaintainerInfo{
+						Email: "some-email@domain.com",
+					},
+					Capabilities: &CapabilitiesInfo{
+						App: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeNative},
+						},
+						Site: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+					},
+				},
+			},
+		},
+		{
+			description:  "Disabled - Bidder Wrong Case",
+			givenConfigs: map[string]Adapter{bidder: {}},
+			givenContent: testYAML,
+			expectedInfo: map[string]BidderInfo{
+				bidder: {
+					Enabled: false,
+					Maintainer: &MaintainerInfo{
+						Email: "some-email@domain.com",
+					},
+					Capabilities: &CapabilitiesInfo{
+						App: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeNative},
+						},
+						Site: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+					},
+				},
+			},
+		},
+		{
+			description:  "Disabled - Explicitly Configured",
+			givenConfigs: map[string]Adapter{strings.ToLower(bidder): {Disabled: false}},
+			givenContent: testYAML,
+			expectedInfo: map[string]BidderInfo{
+				bidder: {
+					Enabled: true,
+					Maintainer: &MaintainerInfo{
+						Email: "some-email@domain.com",
+					},
+					Capabilities: &CapabilitiesInfo{
+						App: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeNative},
+						},
+						Site: &PlatformInfo{
+							MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative},
+						},
+					},
+				},
+			},
+		},
+		{
+			description:   "Read Error",
+			givenConfigs:  map[string]Adapter{strings.ToLower(bidder): {}},
+			givenError:    errors.New("any read error"),
+			expectedError: "any read error",
+		},
+		{
+			description:   "Unmarshal Error",
+			givenConfigs:  map[string]Adapter{strings.ToLower(bidder): {}},
+			givenContent:  "invalid yaml",
+			expectedError: "error parsing yaml for bidder someBidder: yaml: unmarshal errors:\n  line 1: cannot unmarshal !!str `invalid...` into config.BidderInfo",
+		},
+	}
+
+	for _, test := range testCases {
+		r := fakeInfoReader{test.givenContent, test.givenError}
+		info, err := loadBidderInfo(r, test.givenConfigs, []string{bidder})
+
+		if test.expectedError == "" {
+			assert.NoError(t, err, test.description)
+		} else {
+			assert.EqualError(t, err, test.expectedError, test.description)
+		}
+
+		assert.Equal(t, test.expectedInfo, info, test.description)
+	}
+}
+
+type fakeInfoReader struct {
+	content string
+	err     error
+}
+
+func (r fakeInfoReader) Read(bidder string) ([]byte, error) {
+	return []byte(r.content), r.err
+}
diff --git a/config/bidderinfo_validate_test.go b/config/bidderinfo_validate_test.go
new file mode 100644
index 00000000..23d6e3df
--- /dev/null
+++ b/config/bidderinfo_validate_test.go
@@ -0,0 +1,115 @@
+package config
+
+import (
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+	"gopkg.in/yaml.v2"
+)
+
+const bidderInfoRelativePath = "../static/bidder-info"
+
+// TestBidderInfoFiles ensures each bidder has a valid static/bidder-info/bidder.yaml file. Validation is performed directly
+// against the file system with separate yaml unmarshalling from the LoadBidderInfoFromDisk func.
+func TestBidderInfoFiles(t *testing.T) {
+	fileInfos, err := ioutil.ReadDir(bidderInfoRelativePath)
+	if err != nil {
+		assert.FailNow(t, "Error reading the static/bidder-info directory: %v", err)
+	}
+
+	// Ensure YAML Files Are For A Known Core Bidder
+	for _, fileInfo := range fileInfos {
+		bidder := strings.TrimSuffix(fileInfo.Name(), ".yaml")
+
+		_, isKnown := openrtb_ext.NormalizeBidderName(bidder)
+		assert.True(t, isKnown, "unexpected bidder info yaml file %s", fileInfo.Name())
+	}
+
+	// Ensure YAML Files Are Defined For Each Core Bidder
+	expectedFileInfosLength := len(openrtb_ext.CoreBidderNames())
+	assert.Len(t, fileInfos, expectedFileInfosLength, "static/bidder-info contains %d files, but there are %d known bidders. Did you forget to add a YAML file for your bidder?", len(fileInfos), expectedFileInfosLength)
+
+	// Validate Contents
+	for _, fileInfo := range fileInfos {
+		path := fmt.Sprintf(bidderInfoRelativePath + "/" + fileInfo.Name())
+
+		infoFileData, err := os.Open(path)
+		assert.NoError(t, err, "Unexpected error: %v", err)
+
+		content, err := ioutil.ReadAll(infoFileData)
+		assert.NoError(t, err, "Failed to read static/bidder-info/%s: %v", fileInfo.Name(), err)
+
+		var fileInfoContent BidderInfo
+		err = yaml.Unmarshal(content, &fileInfoContent)
+		assert.NoError(t, err, "Error interpreting content from static/bidder-info/%s: %v", fileInfo.Name(), err)
+
+		err = validateInfo(&fileInfoContent)
+		assert.NoError(t, err, "Invalid content in static/bidder-info/%s: %v", fileInfo.Name(), err)
+	}
+}
+
+func validateInfo(info *BidderInfo) error {
+	if err := validateMaintainer(info.Maintainer); err != nil {
+		return err
+	}
+
+	if err := validateCapabilities(info.Capabilities); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func validateMaintainer(info *MaintainerInfo) error {
+	if info == nil || info.Email == "" {
+		return errors.New("missing required field: maintainer.email")
+	}
+	return nil
+}
+
+func validateCapabilities(info *CapabilitiesInfo) error {
+	if info == nil {
+		return errors.New("missing required field: capabilities")
+	}
+
+	if info.App == nil && info.Site == nil {
+		return errors.New("at least one of capabilities.site or capabilities.app must exist")
+	}
+
+	if info.App != nil {
+		if err := validatePlatformInfo(info.App); err != nil {
+			return fmt.Errorf("capabilities.app failed validation: %v", err)
+		}
+	}
+
+	if info.Site != nil {
+		if err := validatePlatformInfo(info.Site); err != nil {
+			return fmt.Errorf("capabilities.site failed validation: %v", err)
+		}
+	}
+	return nil
+}
+
+func validatePlatformInfo(info *PlatformInfo) error {
+	if info == nil {
+		return errors.New("object cannot be empty")
+	}
+
+	if len(info.MediaTypes) == 0 {
+		return errors.New("mediaTypes should be an array with at least one string element")
+	}
+
+	for index, mediaType := range info.MediaTypes {
+		if mediaType != "banner" && mediaType != "video" && mediaType != "native" && mediaType != "audio" {
+			return fmt.Errorf("unrecognized media type at index %d: %s", index, mediaType)
+		}
+	}
+
+	return nil
+}
diff --git a/adapters/adapterstest/bidder-info/someBidder.yaml b/config/test/bidder-info/someBidder.yaml
similarity index 100%
rename from adapters/adapterstest/bidder-info/someBidder.yaml
rename to config/test/bidder-info/someBidder.yaml
diff --git a/endpoints/events/account_test.go b/endpoints/events/account_test.go
index ff0dd51b..b61f29dc 100644
--- a/endpoints/events/account_test.go
+++ b/endpoints/events/account_test.go
@@ -3,16 +3,16 @@ package events
 import (
 	"errors"
 	"fmt"
-	"github.com/julienschmidt/httprouter"
-	"github.com/prebid/prebid-server/adapters"
-	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/stored_requests"
-	"github.com/stretchr/testify/assert"
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
 	"strings"
 	"testing"
+
+	"github.com/julienschmidt/httprouter"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/stored_requests"
+	"github.com/stretchr/testify/assert"
 )
 
 func TestHandleAccountServiceErrors(t *testing.T) {
@@ -155,7 +155,7 @@ func vast(t *testing.T, cfg *config.Configuration, fetcher stored_requests.Accou
 		r    *http.Request
 	}{
 		name: "vast",
-		h:    NewVTrackEndpoint(cfg, fetcher, &vtrackMockCacheClient{}, adapters.BidderInfos{}),
+		h:    NewVTrackEndpoint(cfg, fetcher, &vtrackMockCacheClient{}, config.BidderInfos{}),
 		r:    httptest.NewRequest("POST", "/vtrack?a=testacc", strings.NewReader(vtrackBody)),
 	}
 }
diff --git a/endpoints/events/vtrack_test.go b/endpoints/events/vtrack_test.go
index 0ec04663..1766f2e2 100644
--- a/endpoints/events/vtrack_test.go
+++ b/endpoints/events/vtrack_test.go
@@ -5,15 +5,15 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"github.com/prebid/prebid-server/adapters"
-	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/prebid_cache_client"
-	"github.com/prebid/prebid-server/stored_requests"
-	"github.com/stretchr/testify/assert"
 	"io/ioutil"
 	"net/http/httptest"
 	"strings"
 	"testing"
+
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/prebid_cache_client"
+	"github.com/prebid/prebid-server/stored_requests"
+	"github.com/stretchr/testify/assert"
 )
 
 const (
@@ -374,13 +374,13 @@ func TestShouldSendToCacheExpectedPutsAndUpdatableBiddersWhenBidderVastNotAllowe
 	cfg.MarshalAccountDefaults()
 
 	// bidder info
-	bidderInfos := make(adapters.BidderInfos)
-	bidderInfos["bidder"] = adapters.BidderInfo{
-		Status:                  adapters.StatusActive,
+	bidderInfos := make(config.BidderInfos)
+	bidderInfos["bidder"] = config.BidderInfo{
+		Enabled:                 true,
 		ModifyingVastXmlAllowed: false,
 	}
-	bidderInfos["updatable_bidder"] = adapters.BidderInfo{
-		Status:                  adapters.StatusActive,
+	bidderInfos["updatable_bidder"] = config.BidderInfo{
+		Enabled:                 true,
 		ModifyingVastXmlAllowed: true,
 	}
 
@@ -437,13 +437,13 @@ func TestShouldSendToCacheExpectedPutsAndUpdatableBiddersWhenBidderVastAllowed(t
 	cfg.MarshalAccountDefaults()
 
 	// bidder info
-	bidderInfos := make(adapters.BidderInfos)
-	bidderInfos["bidder"] = adapters.BidderInfo{
-		Status:                  adapters.StatusActive,
+	bidderInfos := make(config.BidderInfos)
+	bidderInfos["bidder"] = config.BidderInfo{
+		Enabled:                 true,
 		ModifyingVastXmlAllowed: true,
 	}
-	bidderInfos["updatable_bidder"] = adapters.BidderInfo{
-		Status:                  adapters.StatusActive,
+	bidderInfos["updatable_bidder"] = config.BidderInfo{
+		Enabled:                 true,
 		ModifyingVastXmlAllowed: true,
 	}
 
@@ -500,7 +500,7 @@ func TestShouldSendToCacheExpectedPutsAndUpdatableUnknownBiddersWhenUnknownBidde
 	cfg.MarshalAccountDefaults()
 
 	// bidder info
-	bidderInfos := make(adapters.BidderInfos)
+	bidderInfos := make(config.BidderInfos)
 
 	// prepare
 	data, err := getValidVTrackRequestBody(true, false)
@@ -556,7 +556,7 @@ func TestShouldReturnBadRequestWhenRequestExceedsMaxRequestSize(t *testing.T) {
 	cfg.MarshalAccountDefaults()
 
 	// bidder info
-	bidderInfos := make(adapters.BidderInfos)
+	bidderInfos := make(config.BidderInfos)
 
 	// prepare
 	data, err := getValidVTrackRequestBody(true, false)
diff --git a/endpoints/info/bidders_detail_test.go b/endpoints/info/bidders_detail_test.go
new file mode 100644
index 00000000..d8b6f2bf
--- /dev/null
+++ b/endpoints/info/bidders_detail_test.go
@@ -0,0 +1,497 @@
+package info
+
+import (
+	"bytes"
+	"io/ioutil"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/julienschmidt/httprouter"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestPrepareBiddersDetailResponse(t *testing.T) {
+	bidderAInfo := config.BidderInfo{Enabled: true, Maintainer: &config.MaintainerInfo{Email: "bidderA"}}
+	bidderAConfig := config.Adapter{Endpoint: "https://secureEndpoint.com"}
+	bidderAResponse := []byte(`{"status":"ACTIVE","usesHttps":true,"maintainer":{"email":"bidderA"}}`)
+
+	bidderBInfo := config.BidderInfo{Enabled: true, Maintainer: &config.MaintainerInfo{Email: "bidderB"}}
+	bidderBConfig := config.Adapter{Endpoint: "http://unsecureEndpoint.com"}
+	bidderBResponse := []byte(`{"status":"ACTIVE","usesHttps":false,"maintainer":{"email":"bidderB"}}`)
+
+	allResponseBidderA := bytes.Buffer{}
+	allResponseBidderA.WriteString(`{"a":`)
+	allResponseBidderA.Write(bidderAResponse)
+	allResponseBidderA.WriteString(`}`)
+
+	allResponseBidderAB := bytes.Buffer{}
+	allResponseBidderAB.WriteString(`{"a":`)
+	allResponseBidderAB.Write(bidderAResponse)
+	allResponseBidderAB.WriteString(`,"b":`)
+	allResponseBidderAB.Write(bidderBResponse)
+	allResponseBidderAB.WriteString(`}`)
+
+	var testCases = []struct {
+		description        string
+		givenBidders       config.BidderInfos
+		givenBiddersConfig map[string]config.Adapter
+		givenAliases       map[string]string
+		expectedResponses  map[string][]byte
+		expectedError      string
+	}{
+		{
+			description:        "None",
+			givenBidders:       config.BidderInfos{},
+			givenBiddersConfig: map[string]config.Adapter{},
+			givenAliases:       map[string]string{},
+			expectedResponses:  map[string][]byte{"all": []byte(`{}`)},
+		},
+		{
+			description:        "One",
+			givenBidders:       config.BidderInfos{"a": bidderAInfo},
+			givenBiddersConfig: map[string]config.Adapter{"a": bidderAConfig},
+			givenAliases:       map[string]string{},
+			expectedResponses:  map[string][]byte{"a": bidderAResponse, "all": allResponseBidderA.Bytes()},
+		},
+		{
+			description:        "Many",
+			givenBidders:       config.BidderInfos{"a": bidderAInfo, "b": bidderBInfo},
+			givenBiddersConfig: map[string]config.Adapter{"a": bidderAConfig, "b": bidderBConfig},
+			givenAliases:       map[string]string{},
+			expectedResponses:  map[string][]byte{"a": bidderAResponse, "b": bidderBResponse, "all": allResponseBidderAB.Bytes()},
+		},
+		{
+			description:        "Error - Map Details", // Returns error due to invalid alias.
+			givenBidders:       config.BidderInfos{"a": bidderAInfo},
+			givenBiddersConfig: map[string]config.Adapter{"a": bidderAConfig},
+			givenAliases:       map[string]string{"zAlias": "z"},
+			expectedError:      "base adapter z for alias zAlias not found",
+		},
+	}
+
+	for _, test := range testCases {
+		responses, err := prepareBiddersDetailResponse(test.givenBidders, test.givenBiddersConfig, test.givenAliases)
+
+		if test.expectedError == "" {
+			assert.Equal(t, test.expectedResponses, responses, test.description+":responses")
+			assert.NoError(t, err, test.expectedError, test.description+":err")
+		} else {
+			assert.Empty(t, responses, test.description+":responses")
+			assert.EqualError(t, err, test.expectedError, test.description+":err")
+		}
+	}
+}
+
+func TestMapDetails(t *testing.T) {
+	trueValue := true
+	falseValue := false
+
+	bidderAInfo := config.BidderInfo{Enabled: true, Maintainer: &config.MaintainerInfo{Email: "bidderA"}}
+	bidderAConfig := config.Adapter{Endpoint: "https://secureEndpoint.com"}
+	bidderADetail := bidderDetail{Status: "ACTIVE", UsesHTTPS: &trueValue, Maintainer: &maintainer{Email: "bidderA"}}
+	aliasADetail := bidderDetail{Status: "ACTIVE", UsesHTTPS: &trueValue, Maintainer: &maintainer{Email: "bidderA"}, AliasOf: "a"}
+
+	bidderBInfo := config.BidderInfo{Enabled: true, Maintainer: &config.MaintainerInfo{Email: "bidderB"}}
+	bidderBConfig := config.Adapter{Endpoint: "http://unsecureEndpoint.com"}
+	bidderBDetail := bidderDetail{Status: "ACTIVE", UsesHTTPS: &falseValue, Maintainer: &maintainer{Email: "bidderB"}}
+	aliasBDetail := bidderDetail{Status: "ACTIVE", UsesHTTPS: &falseValue, Maintainer: &maintainer{Email: "bidderB"}, AliasOf: "b"}
+
+	var testCases = []struct {
+		description        string
+		givenBidders       config.BidderInfos
+		givenBiddersConfig map[string]config.Adapter
+		givenAliases       map[string]string
+		expectedDetails    map[string]bidderDetail
+		expectedError      string
+	}{
+		{
+			description:        "None",
+			givenBidders:       config.BidderInfos{},
+			givenBiddersConfig: map[string]config.Adapter{},
+			givenAliases:       map[string]string{},
+			expectedDetails:    map[string]bidderDetail{},
+		},
+		{
+			description:        "One Core Bidder",
+			givenBidders:       config.BidderInfos{"a": bidderAInfo},
+			givenBiddersConfig: map[string]config.Adapter{"a": bidderAConfig},
+			givenAliases:       map[string]string{},
+			expectedDetails:    map[string]bidderDetail{"a": bidderADetail},
+		},
+		{
+			description:        "Many Core Bidders",
+			givenBidders:       config.BidderInfos{"a": bidderAInfo, "b": bidderBInfo},
+			givenBiddersConfig: map[string]config.Adapter{"a": bidderAConfig, "b": bidderBConfig},
+			givenAliases:       map[string]string{},
+			expectedDetails:    map[string]bidderDetail{"a": bidderADetail, "b": bidderBDetail},
+		},
+		{
+			description:        "One Alias",
+			givenBidders:       config.BidderInfos{"a": bidderAInfo},
+			givenBiddersConfig: map[string]config.Adapter{"a": bidderAConfig},
+			givenAliases:       map[string]string{"aAlias": "a"},
+			expectedDetails:    map[string]bidderDetail{"a": bidderADetail, "aAlias": aliasADetail},
+		},
+		{
+			description:        "Many Aliases - Same Core Bidder",
+			givenBidders:       config.BidderInfos{"a": bidderAInfo},
+			givenBiddersConfig: map[string]config.Adapter{"a": bidderAConfig},
+			givenAliases:       map[string]string{"aAlias1": "a", "aAlias2": "a"},
+			expectedDetails:    map[string]bidderDetail{"a": bidderADetail, "aAlias1": aliasADetail, "aAlias2": aliasADetail},
+		},
+		{
+			description:        "Many Aliases - Different Core Bidders",
+			givenBidders:       config.BidderInfos{"a": bidderAInfo, "b": bidderBInfo},
+			givenBiddersConfig: map[string]config.Adapter{"a": bidderAConfig, "b": bidderBConfig},
+			givenAliases:       map[string]string{"aAlias": "a", "bAlias": "b"},
+			expectedDetails:    map[string]bidderDetail{"a": bidderADetail, "b": bidderBDetail, "aAlias": aliasADetail, "bAlias": aliasBDetail},
+		},
+		{
+			description:        "Error - Alias Without Core Bidder",
+			givenBidders:       config.BidderInfos{"a": bidderAInfo},
+			givenBiddersConfig: map[string]config.Adapter{"a": bidderAConfig},
+			givenAliases:       map[string]string{"zAlias": "z"},
+			expectedError:      "base adapter z for alias zAlias not found",
+		},
+	}
+
+	for _, test := range testCases {
+		details, err := mapDetails(test.givenBidders, test.givenBiddersConfig, test.givenAliases)
+
+		if test.expectedError == "" {
+			assert.Equal(t, test.expectedDetails, details, test.description+":details")
+			assert.NoError(t, err, test.expectedError, test.description+":err")
+		} else {
+			assert.Empty(t, details, test.description+":details")
+			assert.EqualError(t, err, test.expectedError, test.description+":err")
+		}
+	}
+}
+
+func TestResolveEndpoint(t *testing.T) {
+	var testCases = []struct {
+		description        string
+		givenBidder        string
+		givenBiddersConfig map[string]config.Adapter
+		expectedEndpoint   string
+	}{
+		{
+			description:        "Bidder Found - Uses Config Value",
+			givenBidder:        "a",
+			givenBiddersConfig: map[string]config.Adapter{"a": {Endpoint: "anyEndpoint"}},
+			expectedEndpoint:   "anyEndpoint",
+		},
+		{
+			description:        "Bidder Not Found - Returns Empty",
+			givenBidder:        "hasNoConfig",
+			givenBiddersConfig: map[string]config.Adapter{"a": {Endpoint: "anyEndpoint"}},
+			expectedEndpoint:   "",
+		},
+	}
+
+	for _, test := range testCases {
+		result := resolveEndpoint(test.givenBidder, test.givenBiddersConfig)
+		assert.Equal(t, test.expectedEndpoint, result, test.description)
+	}
+}
+
+func TestMarshalDetailsResponse(t *testing.T) {
+	// Verifies omitempty is working correctly for bidderDetail, maintainer, capabilities, and aliasOf.
+	bidderDetailA := bidderDetail{Status: "ACTIVE", Maintainer: &maintainer{Email: "bidderA"}}
+	bidderDetailAResponse := []byte(`{"status":"ACTIVE","maintainer":{"email":"bidderA"}}`)
+
+	// Verifies omitempty is working correctly for capabilities.app / capabilities.site.
+	bidderDetailB := bidderDetail{Status: "ACTIVE", Maintainer: &maintainer{Email: "bidderB"}, Capabilities: &capabilities{App: &platform{MediaTypes: []string{"banner"}}}}
+	bidderDetailBResponse := []byte(`{"status":"ACTIVE","maintainer":{"email":"bidderB"},"capabilities":{"app":{"mediaTypes":["banner"]}}}`)
+
+	var testCases = []struct {
+		description      string
+		givenDetails     map[string]bidderDetail
+		expectedResponse map[string][]byte
+	}{
+		{
+			description:      "None",
+			givenDetails:     map[string]bidderDetail{},
+			expectedResponse: map[string][]byte{},
+		},
+		{
+			description:      "One",
+			givenDetails:     map[string]bidderDetail{"a": bidderDetailA},
+			expectedResponse: map[string][]byte{"a": bidderDetailAResponse},
+		},
+		{
+			description:      "Many",
+			givenDetails:     map[string]bidderDetail{"a": bidderDetailA, "b": bidderDetailB},
+			expectedResponse: map[string][]byte{"a": bidderDetailAResponse, "b": bidderDetailBResponse},
+		},
+	}
+
+	for _, test := range testCases {
+		response, err := marshalDetailsResponse(test.givenDetails)
+
+		assert.NoError(t, err, test.description+":err")
+		assert.Equal(t, test.expectedResponse, response, test.description+":response")
+	}
+}
+
+func TestMarshalAllResponse(t *testing.T) {
+	responses := map[string][]byte{
+		"a": []byte(`{"Status":"ACTIVE"}`),
+		"b": []byte(`{"Status":"DISABLED"}`),
+	}
+
+	result, err := marshalAllResponse(responses)
+
+	assert.NoError(t, err)
+	assert.Equal(t, []byte(`{"a":{"Status":"ACTIVE"},"b":{"Status":"DISABLED"}}`), result)
+}
+
+func TestMapDetailFromConfig(t *testing.T) {
+	trueValue := true
+	falseValue := false
+
+	var testCases = []struct {
+		description     string
+		givenBidderInfo config.BidderInfo
+		givenEndpoint   string
+		expected        bidderDetail
+	}{
+		{
+			description: "Enabled - All Values Present",
+			givenBidderInfo: config.BidderInfo{
+				Enabled: true,
+				Maintainer: &config.MaintainerInfo{
+					Email: "foo@bar.com",
+				},
+				Capabilities: &config.CapabilitiesInfo{
+					App:  &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}},
+					Site: &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeVideo}},
+				},
+			},
+			givenEndpoint: "http://amyEndpoint",
+			expected: bidderDetail{
+				Status:    "ACTIVE",
+				UsesHTTPS: &falseValue,
+				Maintainer: &maintainer{
+					Email: "foo@bar.com",
+				},
+				Capabilities: &capabilities{
+					App:  &platform{MediaTypes: []string{"banner"}},
+					Site: &platform{MediaTypes: []string{"video"}},
+				},
+				AliasOf: "",
+			},
+		},
+		{
+			description: "Disabled - All Values Present",
+			givenBidderInfo: config.BidderInfo{
+				Enabled: false,
+				Maintainer: &config.MaintainerInfo{
+					Email: "foo@bar.com",
+				},
+				Capabilities: &config.CapabilitiesInfo{
+					App:  &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}},
+					Site: &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeVideo}},
+				},
+			},
+			givenEndpoint: "http://amyEndpoint",
+			expected: bidderDetail{
+				Status:    "DISABLED",
+				UsesHTTPS: nil,
+				Maintainer: &maintainer{
+					Email: "foo@bar.com",
+				},
+				Capabilities: nil,
+				AliasOf:      "",
+			},
+		},
+		{
+			description: "Enabled - No Values Present",
+			givenBidderInfo: config.BidderInfo{
+				Enabled: true,
+			},
+			givenEndpoint: "http://amyEndpoint",
+			expected: bidderDetail{
+				Status:    "ACTIVE",
+				UsesHTTPS: &falseValue,
+			},
+		},
+		{
+			description: "Enabled - Protocol - HTTP",
+			givenBidderInfo: config.BidderInfo{
+				Enabled: true,
+			},
+			givenEndpoint: "http://amyEndpoint",
+			expected: bidderDetail{
+				Status:    "ACTIVE",
+				UsesHTTPS: &falseValue,
+			},
+		},
+		{
+			description: "Enabled - Protocol - HTTPS",
+			givenBidderInfo: config.BidderInfo{
+				Enabled: true,
+			},
+			givenEndpoint: "https://amyEndpoint",
+			expected: bidderDetail{
+				Status:    "ACTIVE",
+				UsesHTTPS: &trueValue,
+			},
+		},
+		{
+			description: "Enabled - Protocol - HTTPS - Case Insensitive",
+			givenBidderInfo: config.BidderInfo{
+				Enabled: true,
+			},
+			givenEndpoint: "https://amyEndpoint",
+			expected: bidderDetail{
+				Status:    "ACTIVE",
+				UsesHTTPS: &trueValue,
+			},
+		},
+		{
+			description: "Enabled - Protocol - Unknown",
+			givenBidderInfo: config.BidderInfo{
+				Enabled: true,
+			},
+			givenEndpoint: "endpointWithoutProtocol",
+			expected: bidderDetail{
+				Status:    "ACTIVE",
+				UsesHTTPS: &falseValue,
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		result := mapDetailFromConfig(test.givenBidderInfo, test.givenEndpoint)
+		assert.Equal(t, test.expected, result, test.description)
+	}
+}
+
+func TestMapMediaTypes(t *testing.T) {
+	var testCases = []struct {
+		description string
+		mediaTypes  []openrtb_ext.BidType
+		expected    []string
+	}{
+		{
+			description: "Nil",
+			mediaTypes:  nil,
+			expected:    nil,
+		},
+		{
+			description: "None",
+			mediaTypes:  []openrtb_ext.BidType{},
+			expected:    []string{},
+		},
+		{
+			description: "One",
+			mediaTypes:  []openrtb_ext.BidType{openrtb_ext.BidTypeBanner},
+			expected:    []string{"banner"},
+		},
+		{
+			description: "Many",
+			mediaTypes:  []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo},
+			expected:    []string{"banner", "video"},
+		},
+	}
+
+	for _, test := range testCases {
+		result := mapMediaTypes(test.mediaTypes)
+		assert.ElementsMatch(t, test.expected, result, test.description)
+	}
+}
+
+func TestBiddersDetailHandler(t *testing.T) {
+	bidderAInfo := config.BidderInfo{Enabled: true, Maintainer: &config.MaintainerInfo{Email: "bidderA"}}
+	bidderAConfig := config.Adapter{Endpoint: "https://secureEndpoint.com"}
+	bidderAResponse := []byte(`{"status":"ACTIVE","usesHttps":true,"maintainer":{"email":"bidderA"}}`)
+	aliasAResponse := []byte(`{"status":"ACTIVE","usesHttps":true,"maintainer":{"email":"bidderA"},"aliasOf":"a"}`)
+
+	bidderBInfo := config.BidderInfo{Enabled: true, Maintainer: &config.MaintainerInfo{Email: "bidderB"}}
+	bidderBConfig := config.Adapter{Endpoint: "http://unsecureEndpoint.com"}
+	bidderBResponse := []byte(`{"status":"ACTIVE","usesHttps":false,"maintainer":{"email":"bidderB"}}`)
+
+	allResponse := bytes.Buffer{}
+	allResponse.WriteString(`{"a":`)
+	allResponse.Write(bidderAResponse)
+	allResponse.WriteString(`,"aAlias":`)
+	allResponse.Write(aliasAResponse)
+	allResponse.WriteString(`,"b":`)
+	allResponse.Write(bidderBResponse)
+	allResponse.WriteString(`}`)
+
+	bidders := config.BidderInfos{"a": bidderAInfo, "b": bidderBInfo}
+	biddersConfig := map[string]config.Adapter{"a": bidderAConfig, "b": bidderBConfig}
+	aliases := map[string]string{"aAlias": "a"}
+
+	handler := NewBiddersDetailEndpoint(bidders, biddersConfig, aliases)
+
+	var testCases = []struct {
+		description      string
+		givenBidder      string
+		expectedStatus   int
+		expectedHeaders  http.Header
+		expectedResponse []byte
+	}{
+		{
+			description:      "Bidder A",
+			givenBidder:      "a",
+			expectedStatus:   http.StatusOK,
+			expectedHeaders:  http.Header{"Content-Type": []string{"application/json"}},
+			expectedResponse: bidderAResponse,
+		},
+		{
+			description:      "Bidder B",
+			givenBidder:      "b",
+			expectedStatus:   http.StatusOK,
+			expectedHeaders:  http.Header{"Content-Type": []string{"application/json"}},
+			expectedResponse: bidderBResponse,
+		},
+		{
+			description:      "Bidder A Alias",
+			givenBidder:      "aAlias",
+			expectedStatus:   http.StatusOK,
+			expectedHeaders:  http.Header{"Content-Type": []string{"application/json"}},
+			expectedResponse: aliasAResponse,
+		},
+		{
+			description:      "All Bidders",
+			givenBidder:      "all",
+			expectedStatus:   http.StatusOK,
+			expectedHeaders:  http.Header{"Content-Type": []string{"application/json"}},
+			expectedResponse: allResponse.Bytes(),
+		},
+		{
+			description:      "All Bidders - Wrong Case",
+			givenBidder:      "ALL",
+			expectedStatus:   http.StatusNotFound,
+			expectedHeaders:  http.Header{},
+			expectedResponse: []byte{},
+		},
+		{
+			description:      "Invalid Bidder",
+			givenBidder:      "doesntExist",
+			expectedStatus:   http.StatusNotFound,
+			expectedHeaders:  http.Header{},
+			expectedResponse: []byte{},
+		},
+	}
+
+	for _, test := range testCases {
+		responseRecorder := httptest.NewRecorder()
+		handler(responseRecorder, nil, httprouter.Params{{"bidderName", test.givenBidder}})
+
+		result := responseRecorder.Result()
+		assert.Equal(t, result.StatusCode, test.expectedStatus, test.description+":statuscode")
+
+		resultBody, _ := ioutil.ReadAll(result.Body)
+		assert.Equal(t, test.expectedResponse, resultBody, test.description+":body")
+
+		resultHeaders := result.Header
+		assert.Equal(t, test.expectedHeaders, resultHeaders, test.description+":headers")
+	}
+}
diff --git a/endpoints/info/bidders_test.go b/endpoints/info/bidders_test.go
index 8229472b..e48dd3d0 100644
--- a/endpoints/info/bidders_test.go
+++ b/endpoints/info/bidders_test.go
@@ -1,321 +1,89 @@
-package info_test
+package info
 
 import (
-	"encoding/json"
-	"errors"
-	"fmt"
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
-	"os"
-	"strings"
 	"testing"
 
-	"github.com/julienschmidt/httprouter"
-	"github.com/stretchr/testify/assert"
-
-	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/config"
-	"github.com/prebid/prebid-server/endpoints/info"
-	"github.com/prebid/prebid-server/openrtb_ext"
-	yaml "gopkg.in/yaml.v2"
+	"github.com/stretchr/testify/assert"
 )
 
-func TestGetBiddersNoAliases(t *testing.T) {
-	testGetBidders(t, map[string]string{})
-}
-
-func TestGetBiddersWithAliases(t *testing.T) {
-	aliases := map[string]string{
-		"test1": "appnexus",
-		"test2": "rubicon",
-		"test3": "openx",
-	}
-	testGetBidders(t, aliases)
-}
-
-func testGetBidders(t *testing.T, aliases map[string]string) {
-	endpoint := info.NewBiddersEndpoint(aliases)
-
-	req, err := http.NewRequest("GET", "http://prebid-server.com/info/bidders", strings.NewReader(""))
-	if err != nil {
-		assert.FailNow(t, "Failed to create a GET /info/bidders request: %v", err)
-	}
-
-	r := httptest.NewRecorder()
-	endpoint(r, req, nil)
-
-	assert.Equal(t, http.StatusOK, r.Code, "GET /info/bidders returned bad status: %d", r.Code)
-	assert.Equal(t, "application/json", r.Header().Get("Content-Type"), "Bad /info/bidders content type. Expected application/json. Got %s", r.Header().Get("Content-Type"))
-
-	bidderMap := openrtb_ext.BuildBidderNameHashSet()
-
-	bodyBytes := r.Body.Bytes()
-	bidderSlice := make([]string, 0)
-	err = json.Unmarshal(bodyBytes, &bidderSlice)
-	assert.NoError(t, err, "Failed to unmarshal /info/bidders response: %v", err)
-
-	for _, bidderName := range bidderSlice {
-		if _, ok := bidderMap[bidderName]; !ok {
-			assert.Contains(t, aliases, bidderName, "Response from /info/bidders contained unexpected BidderName: %s", bidderName)
-		}
-	}
-
-	expectedBidderSliceLength := len(bidderMap) + len(aliases)
-	assert.Len(t, bidderSlice, expectedBidderSliceLength, "Response from /info/bidders did not match BidderMap. Expected %d elements. Got %d", expectedBidderSliceLength)
-}
-
-// TestGetSpecificBidders validates all the GET /info/bidders/{bidderName} endpoints
-func TestGetSpecificBidders(t *testing.T) {
-	// Setup:
+func TestPrepareBiddersResponse(t *testing.T) {
 	testCases := []struct {
-		status      adapters.BidderStatus
-		description string
+		description  string
+		givenBidders config.BidderInfos
+		givenAliases map[string]string
+		expected     string
 	}{
 		{
-			status:      adapters.StatusActive,
-			description: "case 1 - bidder status is active",
+			description:  "None",
+			givenBidders: config.BidderInfos{},
+			givenAliases: nil,
+			expected:     `[]`,
 		},
 		{
-			status:      adapters.StatusDisabled,
-			description: "case 2 - bidder status is disabled",
+			description:  "Core Bidders Only - One",
+			givenBidders: config.BidderInfos{"a": {}},
+			givenAliases: nil,
+			expected:     `["a"]`,
+		},
+		{
+			description:  "Core Bidders Only - Many",
+			givenBidders: config.BidderInfos{"a": {}, "b": {}},
+			givenAliases: nil,
+			expected:     `["a","b"]`,
+		},
+		{
+			description:  "Core Bidders Only - Many Sorted",
+			givenBidders: config.BidderInfos{"z": {}, "a": {}},
+			givenAliases: nil,
+			expected:     `["a","z"]`,
+		},
+		{
+			description:  "With Aliases - One",
+			givenBidders: config.BidderInfos{"a": {}},
+			givenAliases: map[string]string{"b": "b"},
+			expected:     `["a","b"]`,
+		},
+		{
+			description:  "With Aliases - Many",
+			givenBidders: config.BidderInfos{"a": {}},
+			givenAliases: map[string]string{"b": "b", "c": "c"},
+			expected:     `["a","b","c"]`,
+		},
+		{
+			description:  "With Aliases - Sorted",
+			givenBidders: config.BidderInfos{"z": {}},
+			givenAliases: map[string]string{"a": "a"},
+			expected:     `["a","z"]`,
 		},
 	}
 
-	for _, tc := range testCases {
-		bidderDisabled := false
-		if tc.status == adapters.StatusDisabled {
-			bidderDisabled = true
-		}
-		cfg := blankAdapterConfigWithStatus(openrtb_ext.CoreBidderNames(), bidderDisabled)
-		bidderInfos := adapters.ParseBidderInfos(cfg, "../../static/bidder-info", openrtb_ext.CoreBidderNames())
-		endpoint := info.NewBidderDetailsEndpoint(bidderInfos, map[string]string{})
-
-		for _, bidderName := range openrtb_ext.CoreBidderNames() {
-			req, err := http.NewRequest("GET", "http://prebid-server.com/info/bidders/"+string(bidderName), strings.NewReader(""))
-			if err != nil {
-				t.Errorf("Failed to create a GET /info/bidders request: %v", err)
-				continue
-			}
-			params := []httprouter.Param{{
-				Key:   "bidderName",
-				Value: string(bidderName),
-			}}
-			r := httptest.NewRecorder()
-
-			// Execute:
-			endpoint(r, req, params)
-
-			// Verify:
-			assert.Equal(t, http.StatusOK, r.Code, "GET /info/bidders/"+string(bidderName)+" returned a %d. Expected 200", r.Code, tc.description)
-			assert.Equal(t, "application/json", r.HeaderMap.Get("Content-Type"), "GET /info/bidders/"+string(bidderName)+" returned Content-Type %s. Expected application/json", r.HeaderMap.Get("Content-Type"), tc.description)
-
-			var resBidderInfo adapters.BidderInfo
-			if err := json.Unmarshal(r.Body.Bytes(), &resBidderInfo); err != nil {
-				assert.FailNow(t, "Failed to unmarshal JSON from endpoints/info/bidders/%s: %v", bidderName, err, tc.description)
-			}
-
-			assert.Equal(t, tc.status, resBidderInfo.Status, tc.description)
-		}
-	}
-}
-
-func TestGetBidderAccuracyNoAliases(t *testing.T) {
-	testGetBidderAccuracy(t, "")
-}
-
-func TestGetBidderAccuracyAliases(t *testing.T) {
-	testGetBidderAccuracy(t, "aliasedBidder")
-}
-
-// TestGetBidderAccuracyAlias validates the output for an alias of a known file.
-func testGetBidderAccuracy(t *testing.T, alias string) {
-	cfg := blankAdapterConfig(openrtb_ext.CoreBidderNames())
-	bidderInfos := adapters.ParseBidderInfos(cfg, "../../adapters/adapterstest/bidder-info", []openrtb_ext.BidderName{openrtb_ext.BidderName("someBidder")})
-
-	aliases := map[string]string{}
-	bidder := "someBidder"
-	if len(alias) > 0 {
-		aliases[alias] = bidder
-		bidder = alias
-	}
-
-	endpoint := info.NewBidderDetailsEndpoint(bidderInfos, aliases)
-	req, err := http.NewRequest("GET", "http://prebid-server.com/info/bidders/"+bidder, strings.NewReader(""))
-	assert.NoError(t, err, "Failed to create a GET /info/bidders request: %v", err)
-	params := []httprouter.Param{{
-		Key:   "bidderName",
-		Value: bidder,
-	}}
-
-	r := httptest.NewRecorder()
-	endpoint(r, req, params)
-
-	var fileData adapters.BidderInfo
-	if err := json.Unmarshal(r.Body.Bytes(), &fileData); err != nil {
-		assert.FailNow(t, "Failed to unmarshal JSON from endpoints/info/sample/someBidder.yaml: %v", err)
-	}
-
-	assert.Equal(t, "some-email@domain.com", fileData.Maintainer.Email, "maintainer.email should be some-email@domain.com. Got %s", fileData.Maintainer.Email)
-	assert.Len(t, fileData.Capabilities.App.MediaTypes, 2, "Expected 2 supported mediaTypes on app. Got %d", len(fileData.Capabilities.App.MediaTypes))
-	assert.Equal(t, openrtb_ext.BidType("banner"), fileData.Capabilities.App.MediaTypes[0], "capabilities.app.mediaTypes[0] should be banner. Got %s", fileData.Capabilities.App.MediaTypes[0])
-	assert.Equal(t, openrtb_ext.BidType("native"), fileData.Capabilities.App.MediaTypes[1], "capabilities.app.mediaTypes[1] should be native. Got %s", fileData.Capabilities.App.MediaTypes[1])
-	assert.Len(t, fileData.Capabilities.Site.MediaTypes, 3, "Expected 3 supported mediaTypes on app. Got %d", len(fileData.Capabilities.Site.MediaTypes))
-	assert.Equal(t, openrtb_ext.BidType("banner"), fileData.Capabilities.Site.MediaTypes[0], "capabilities.app.mediaTypes[0] should be banner. Got %s", fileData.Capabilities.Site.MediaTypes[0])
-	assert.Equal(t, openrtb_ext.BidType("video"), fileData.Capabilities.Site.MediaTypes[1], "capabilities.app.mediaTypes[1] should be video. Got %s", fileData.Capabilities.Site.MediaTypes[1])
-	assert.Equal(t, openrtb_ext.BidType("native"), fileData.Capabilities.Site.MediaTypes[2], "capabilities.app.mediaTypes[2] should be native. Got %s", fileData.Capabilities.Site.MediaTypes[2])
-	if len(alias) > 0 {
-		assert.Equal(t, "someBidder", fileData.AliasOf, "aliasOf should be \"someBidder\". Got \"%s\"", fileData.AliasOf)
-	} else {
-		assert.Zero(t, len(fileData.AliasOf), "aliasOf should be empty. Got \"%s\"", fileData.AliasOf)
-	}
-}
-
-func TestGetUnknownBidder(t *testing.T) {
-	bidderInfos := adapters.BidderInfos(make(map[string]adapters.BidderInfo))
-	endpoint := info.NewBidderDetailsEndpoint(bidderInfos, map[string]string{})
-	req, err := http.NewRequest("GET", "http://prebid-server.com/info/bidders/someUnknownBidder", strings.NewReader(""))
-	if err != nil {
-		assert.FailNow(t, "Failed to create a GET /info/bidders/someUnknownBidder request: %v", err)
-	}
-
-	params := []httprouter.Param{{
-		Key:   "bidderName",
-		Value: "someUnknownBidder",
-	}}
-	r := httptest.NewRecorder()
-
-	endpoint(r, req, params)
-	assert.Equal(t, http.StatusNotFound, r.Code, "GET /info/bidders/* should return a 404 on unknown bidders. Got %d", r.Code)
-}
-func TestGetAllBidders(t *testing.T) {
-	cfg := blankAdapterConfig(openrtb_ext.CoreBidderNames())
-	bidderInfos := adapters.ParseBidderInfos(cfg, "../../static/bidder-info", openrtb_ext.CoreBidderNames())
-	endpoint := info.NewBidderDetailsEndpoint(bidderInfos, map[string]string{})
-	req, err := http.NewRequest("GET", "http://prebid-server.com/info/bidders/all", strings.NewReader(""))
-	if err != nil {
-		assert.FailNow(t, "Failed to create a GET /info/bidders/someUnknownBidder request: %v", err)
-	}
-	params := []httprouter.Param{{
-		Key:   "bidderName",
-		Value: "all",
-	}}
-	r := httptest.NewRecorder()
-
-	endpoint(r, req, params)
-	assert.Equal(t, http.StatusOK, r.Code, "GET /info/bidders/all returned a %d. Expected 200", r.Code)
-	assert.Equal(t, "application/json", r.HeaderMap.Get("Content-Type"), "GET /info/bidders/all returned Content-Type %s. Expected application/json", r.HeaderMap.Get("Content-Type"))
-
-	var resBidderInfos map[string]adapters.BidderInfo
-
-	if err := json.Unmarshal(r.Body.Bytes(), &resBidderInfos); err != nil {
-		assert.FailNow(t, "Failed to unmarshal JSON from endpoints/info/sample/someBidder.yaml: %v", err)
-	}
-
-	assert.Len(t, resBidderInfos, len(bidderInfos), "GET /info/bidders/all should respond with all bidders info")
-}
-
-// TestInfoFiles makes sure that static/bidder-info contains a .yaml file for every BidderName.
-func TestInfoFiles(t *testing.T) {
-	fileInfos, err := ioutil.ReadDir("../../static/bidder-info")
-	if err != nil {
-		assert.FailNow(t, "Error reading the static/bidder-info directory: %v", err)
-	}
-
-	// Make sure that files exist for each BidderName
-	for _, bidderName := range openrtb_ext.CoreBidderNames() {
-		_, err := os.Stat(fmt.Sprintf("../../static/bidder-info/%s.yaml", string(bidderName)))
-		assert.False(t, os.IsNotExist(err), "static/bidder-info/%s.yaml not found. Did you forget to create it?", bidderName)
-	}
-
-	expectedFileInfosLength := len(openrtb_ext.CoreBidderNames())
-	assert.Len(t, fileInfos, expectedFileInfosLength, "static/bidder-info contains %d files, but the BidderMap has %d entries. These two should be in sync.", len(fileInfos), expectedFileInfosLength)
-
-	// Make sure that all the files have valid content
-	for _, fileInfo := range fileInfos {
-		infoFileData, err := os.Open(fmt.Sprintf("../../static/bidder-info/%s", fileInfo.Name()))
-		assert.NoError(t, err, "Unexpected error: %v", err)
-
-		content, err := ioutil.ReadAll(infoFileData)
-		assert.NoError(t, err, "Failed to read static/bidder-info/%s: %v", fileInfo.Name(), err)
-
-		var fileInfoContent adapters.BidderInfo
-		err = yaml.Unmarshal(content, &fileInfoContent)
-		assert.NoError(t, err, "Error interpreting content from static/bidder-info/%s: %v", fileInfo.Name(), err)
-
-		err = validateInfo(&fileInfoContent)
-		assert.NoError(t, err, "Invalid content in static/bidder-info/%s: %v", fileInfo.Name(), err)
-
-	}
-}
-
-func validateInfo(info *adapters.BidderInfo) error {
-	if err := validateMaintainer(info.Maintainer); err != nil {
-		return err
-	}
-
-	if err := validateCapabilities(info.Capabilities); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func validateCapabilities(info *adapters.CapabilitiesInfo) error {
-	if info == nil {
-		return errors.New("missing required field: capabilities")
+	for _, test := range testCases {
+		result, err := prepareBiddersResponse(test.givenBidders, test.givenAliases)
 
+		assert.NoError(t, err, test.description)
+		assert.Equal(t, []byte(test.expected), result, test.description)
 	}
-	if info.App == nil && info.Site == nil {
-		return errors.New("at least one of capabilities.site or capabilities.app should exist")
-	}
-	if info.App != nil {
-		if err := validatePlatformInfo(info.App); err != nil {
-			return fmt.Errorf("capabilities.app failed validation: %v", err)
-		}
-	}
-	if info.Site != nil {
-		if err := validatePlatformInfo(info.Site); err != nil {
-			return fmt.Errorf("capabilities.site failed validation: %v", err)
-		}
-	}
-	return nil
 }
 
-func validatePlatformInfo(info *adapters.PlatformInfo) error {
-	if info == nil {
-		return errors.New("we can't validate a nil platformInfo")
-	}
-	if len(info.MediaTypes) == 0 {
-		return errors.New("mediaTypes should be an array with at least one string element")
-	}
+func TestBiddersHandler(t *testing.T) {
+	bidders := config.BidderInfos{"a": {}}
+	aliases := map[string]string{"b": "b"}
 
-	for index, mediaType := range info.MediaTypes {
-		if mediaType != "banner" && mediaType != "video" && mediaType != "native" && mediaType != "audio" {
-			return fmt.Errorf("unrecognized media type at index %d: %s", index, mediaType)
-		}
-	}
+	handler := NewBiddersEndpoint(bidders, aliases)
 
-	return nil
-}
+	responseRecorder := httptest.NewRecorder()
+	handler(responseRecorder, nil, nil)
 
-func validateMaintainer(info *adapters.MaintainerInfo) error {
-	if info == nil || info.Email == "" {
-		return errors.New("missing required field: maintainer.email")
-	}
-	return nil
-}
+	result := responseRecorder.Result()
+	assert.Equal(t, result.StatusCode, http.StatusOK)
 
-func blankAdapterConfig(bidderList []openrtb_ext.BidderName) map[string]config.Adapter {
-	return blankAdapterConfigWithStatus(bidderList, false)
-}
+	resultBody, _ := ioutil.ReadAll(result.Body)
+	assert.Equal(t, []byte(`["a","b"]`), resultBody)
 
-func blankAdapterConfigWithStatus(bidderList []openrtb_ext.BidderName, biddersAreDisabled bool) map[string]config.Adapter {
-	adapters := make(map[string]config.Adapter)
-	for _, b := range bidderList {
-		adapters[strings.ToLower(string(b))] = config.Adapter{
-			Disabled: biddersAreDisabled,
-		}
-	}
-	return adapters
+	resultHeaders := result.Header
+	assert.Equal(t, http.Header{"Content-Type": []string{"application/json"}}, resultHeaders)
 }
diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go
index 97e99c6b..08461d40 100644
--- a/endpoints/openrtb2/amp_auction_test.go
+++ b/endpoints/openrtb2/amp_auction_test.go
@@ -479,7 +479,7 @@ func TestInvalidConsent(t *testing.T) {
 
 	// Assert Result
 	expectedWarnings := map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError{
-		openrtb_ext.BidderNameGeneral: {
+		openrtb_ext.BidderReservedGeneral: {
 			{
 				Code:    10001,
 				Message: "Consent '" + invalidConsent + "' is not recognized as either CCPA or GDPR TCF.",
diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go
index b42e45b6..e55ffd11 100644
--- a/endpoints/openrtb2/auction_benchmark_test.go
+++ b/endpoints/openrtb2/auction_benchmark_test.go
@@ -7,7 +7,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/currency"
 
 	analyticsConf "github.com/prebid/prebid-server/analytics/config"
@@ -60,8 +59,8 @@ func BenchmarkOpenrtbEndpoint(b *testing.B) {
 	server := httptest.NewServer(http.HandlerFunc(dummyServer))
 	defer server.Close()
 
-	var infos adapters.BidderInfos
-	infos["appnexus"] = adapters.BidderInfo{Capabilities: &adapters.CapabilitiesInfo{Site: &adapters.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}}}}
+	var infos config.BidderInfos
+	infos["appnexus"] = config.BidderInfo{Capabilities: &config.CapabilitiesInfo{Site: &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}}}}
 	paramValidator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
 	if err != nil {
 		return
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index 413d7995..4411b4e3 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -17,7 +17,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/stored_requests"
 
 	"github.com/buger/jsonparser"
@@ -2520,8 +2519,8 @@ func (m *mockExchange) HoldAuction(ctx context.Context, r exchange.AuctionReques
 	}, nil
 }
 
-func getBidderInfos(cfg map[string]config.Adapter, biddersNames []openrtb_ext.BidderName) adapters.BidderInfos {
-	biddersInfos := make(adapters.BidderInfos)
+func getBidderInfos(cfg map[string]config.Adapter, biddersNames []openrtb_ext.BidderName) config.BidderInfos {
+	biddersInfos := make(config.BidderInfos)
 	for _, name := range biddersNames {
 		adapterConfig, ok := cfg[string(name)]
 		if !ok {
@@ -2532,14 +2531,9 @@ func getBidderInfos(cfg map[string]config.Adapter, biddersNames []openrtb_ext.Bi
 	return biddersInfos
 }
 
-func newBidderInfo(cfg config.Adapter) adapters.BidderInfo {
-	status := adapters.StatusActive
-	if cfg.Disabled == true {
-		status = adapters.StatusDisabled
-	}
-
-	return adapters.BidderInfo{
-		Status: status,
+func newBidderInfo(cfg config.Adapter) config.BidderInfo {
+	return config.BidderInfo{
+		Enabled: !cfg.Disabled,
 	}
 }
 
diff --git a/errortypes/aggregate_test.go b/errortypes/aggregate_test.go
index 82af9b89..2d4ce21b 100644
--- a/errortypes/aggregate_test.go
+++ b/errortypes/aggregate_test.go
@@ -7,7 +7,7 @@ import (
 	"github.com/influxdata/influxdb/pkg/testing/assert"
 )
 
-func TestAggregateErrors(t *testing.T) {
+func TestAggregateError(t *testing.T) {
 	var testCases = []struct {
 		description string
 		message     string
@@ -35,7 +35,7 @@ func TestAggregateErrors(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		err := NewAggregateErrors(test.message, test.errors)
+		err := NewAggregateError(test.message, test.errors)
 		assert.Equal(t, test.expected, err.Error(), test.description)
 	}
 }
diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go
index 92eb0129..7420779f 100644
--- a/exchange/adapter_util_test.go
+++ b/exchange/adapter_util_test.go
@@ -19,9 +19,8 @@ import (
 )
 
 var (
-	infoActive   = adapters.BidderInfo{Status: adapters.StatusActive}
-	infoDisabled = adapters.BidderInfo{Status: adapters.StatusDisabled}
-	infoUnknown  = adapters.BidderInfo{Status: adapters.StatusUnknown}
+	infoEnabled  = config.BidderInfo{Enabled: true}
+	infoDisabled = config.BidderInfo{Enabled: false}
 )
 
 func TestBuildAdaptersSuccess(t *testing.T) {
@@ -30,16 +29,16 @@ func TestBuildAdaptersSuccess(t *testing.T) {
 		"appnexus":   {},
 		"lifestreet": {Endpoint: "anyEndpoint"},
 	}}
-	infos := map[string]adapters.BidderInfo{
-		"appnexus":   infoActive,
-		"lifestreet": infoActive,
+	infos := map[string]config.BidderInfo{
+		"appnexus":   infoEnabled,
+		"lifestreet": infoEnabled,
 	}
 	metricEngine := &metrics.DummyMetricsEngine{}
 
 	bidders, errs := BuildAdapters(client, cfg, infos, metricEngine)
 
 	appnexusBidder, _ := appnexus.Builder(openrtb_ext.BidderAppnexus, config.Adapter{})
-	appnexusBidderWithInfo := adapters.EnforceBidderInfo(appnexusBidder, infoActive)
+	appnexusBidderWithInfo := adapters.BuildInfoAwareBidder(appnexusBidder, infoEnabled)
 	appnexusBidderAdapted := adaptBidder(appnexusBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderAppnexus, nil)
 	appnexusBidderValidated := addValidatedBidderMiddleware(appnexusBidderAdapted)
 
@@ -58,7 +57,7 @@ func TestBuildAdaptersSuccess(t *testing.T) {
 func TestBuildAdaptersErrors(t *testing.T) {
 	client := &http.Client{}
 	cfg := &config.Configuration{Adapters: map[string]config.Adapter{"unknown": {}}}
-	infos := map[string]adapters.BidderInfo{}
+	infos := map[string]config.BidderInfo{}
 	metricEngine := &metrics.DummyMetricsEngine{}
 
 	bidders, errs := BuildAdapters(client, cfg, infos, metricEngine)
@@ -76,24 +75,24 @@ func TestBuildExchangeBidders(t *testing.T) {
 	metricEngine := &metrics.DummyMetricsEngine{}
 
 	appnexusBidder, _ := appnexus.Builder(openrtb_ext.BidderAppnexus, config.Adapter{})
-	appnexusBidderWithInfo := adapters.EnforceBidderInfo(appnexusBidder, infoActive)
+	appnexusBidderWithInfo := adapters.BuildInfoAwareBidder(appnexusBidder, infoEnabled)
 	appnexusBidderAdapted := adaptBidder(appnexusBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderAppnexus, nil)
 
 	rubiconBidder, _ := rubicon.Builder(openrtb_ext.BidderRubicon, config.Adapter{})
-	rubiconBidderWithInfo := adapters.EnforceBidderInfo(rubiconBidder, infoActive)
+	rubiconBidderWithInfo := adapters.BuildInfoAwareBidder(rubiconBidder, infoEnabled)
 	rubiconBidderAdapted := adaptBidder(rubiconBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderRubicon, nil)
 
 	testCases := []struct {
 		description     string
 		adapterConfig   map[string]config.Adapter
-		bidderInfos     map[string]adapters.BidderInfo
+		bidderInfos     map[string]config.BidderInfo
 		expectedBidders map[openrtb_ext.BidderName]adaptedBidder
 		expectedErrors  []error
 	}{
 		{
 			description:   "Invalid - Builder Errors",
 			adapterConfig: map[string]config.Adapter{"appnexus": {}, "unknown": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{},
+			bidderInfos:   map[string]config.BidderInfo{},
 			expectedErrors: []error{
 				errors.New("appnexus: bidder info not found"),
 				errors.New("unknown: unknown bidder"),
@@ -102,13 +101,13 @@ func TestBuildExchangeBidders(t *testing.T) {
 		{
 			description:     "Success - None",
 			adapterConfig:   map[string]config.Adapter{},
-			bidderInfos:     map[string]adapters.BidderInfo{},
+			bidderInfos:     map[string]config.BidderInfo{},
 			expectedBidders: map[openrtb_ext.BidderName]adaptedBidder{},
 		},
 		{
 			description:   "Success - One",
 			adapterConfig: map[string]config.Adapter{"appnexus": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive},
+			bidderInfos:   map[string]config.BidderInfo{"appnexus": infoEnabled},
 			expectedBidders: map[openrtb_ext.BidderName]adaptedBidder{
 				openrtb_ext.BidderAppnexus: appnexusBidderAdapted,
 			},
@@ -116,7 +115,7 @@ func TestBuildExchangeBidders(t *testing.T) {
 		{
 			description:   "Success - Many",
 			adapterConfig: map[string]config.Adapter{"appnexus": {}, "rubicon": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive, "rubicon": infoActive},
+			bidderInfos:   map[string]config.BidderInfo{"appnexus": infoEnabled, "rubicon": infoEnabled},
 			expectedBidders: map[openrtb_ext.BidderName]adaptedBidder{
 				openrtb_ext.BidderAppnexus: appnexusBidderAdapted,
 				openrtb_ext.BidderRubicon:  rubiconBidderAdapted,
@@ -145,7 +144,7 @@ func TestBuildBidders(t *testing.T) {
 	testCases := []struct {
 		description     string
 		adapterConfig   map[string]config.Adapter
-		bidderInfos     map[string]adapters.BidderInfo
+		bidderInfos     map[string]config.BidderInfo
 		builders        map[openrtb_ext.BidderName]adapters.Builder
 		expectedBidders map[openrtb_ext.BidderName]adapters.Bidder
 		expectedErrors  []error
@@ -153,7 +152,7 @@ func TestBuildBidders(t *testing.T) {
 		{
 			description:   "Invalid - Unknown Bidder",
 			adapterConfig: map[string]config.Adapter{"unknown": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"unknown": infoActive},
+			bidderInfos:   map[string]config.BidderInfo{"unknown": infoEnabled},
 			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder},
 			expectedErrors: []error{
 				errors.New("unknown: unknown bidder"),
@@ -162,7 +161,7 @@ func TestBuildBidders(t *testing.T) {
 		{
 			description:   "Invalid - No Bidder Info",
 			adapterConfig: map[string]config.Adapter{"appnexus": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{},
+			bidderInfos:   map[string]config.BidderInfo{},
 			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder},
 			expectedErrors: []error{
 				errors.New("appnexus: bidder info not found"),
@@ -171,7 +170,7 @@ func TestBuildBidders(t *testing.T) {
 		{
 			description:   "Invalid - No Builder",
 			adapterConfig: map[string]config.Adapter{"appnexus": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive},
+			bidderInfos:   map[string]config.BidderInfo{"appnexus": infoEnabled},
 			builders:      map[openrtb_ext.BidderName]adapters.Builder{},
 			expectedErrors: []error{
 				errors.New("appnexus: builder not registered"),
@@ -180,7 +179,7 @@ func TestBuildBidders(t *testing.T) {
 		{
 			description:   "Success - Builder Error",
 			adapterConfig: map[string]config.Adapter{"appnexus": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive},
+			bidderInfos:   map[string]config.BidderInfo{"appnexus": infoEnabled},
 			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilderWithError},
 			expectedErrors: []error{
 				errors.New("appnexus: anyError"),
@@ -189,62 +188,53 @@ func TestBuildBidders(t *testing.T) {
 		{
 			description:   "Success - None",
 			adapterConfig: map[string]config.Adapter{},
-			bidderInfos:   map[string]adapters.BidderInfo{},
+			bidderInfos:   map[string]config.BidderInfo{},
 			builders:      map[openrtb_ext.BidderName]adapters.Builder{},
 		},
 		{
 			description:   "Success - One",
 			adapterConfig: map[string]config.Adapter{"appnexus": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive},
+			bidderInfos:   map[string]config.BidderInfo{"appnexus": infoEnabled},
 			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder},
 			expectedBidders: map[openrtb_ext.BidderName]adapters.Bidder{
-				openrtb_ext.BidderAppnexus: adapters.EnforceBidderInfo(appnexusBidder, infoActive),
+				openrtb_ext.BidderAppnexus: adapters.BuildInfoAwareBidder(appnexusBidder, infoEnabled),
 			},
 		},
 		{
 			description:   "Success - Many",
 			adapterConfig: map[string]config.Adapter{"appnexus": {}, "rubicon": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive, "rubicon": infoActive},
+			bidderInfos:   map[string]config.BidderInfo{"appnexus": infoEnabled, "rubicon": infoEnabled},
 			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder, openrtb_ext.BidderRubicon: rubiconBuilder},
 			expectedBidders: map[openrtb_ext.BidderName]adapters.Bidder{
-				openrtb_ext.BidderAppnexus: adapters.EnforceBidderInfo(appnexusBidder, infoActive),
-				openrtb_ext.BidderRubicon:  adapters.EnforceBidderInfo(rubiconBidder, infoActive),
+				openrtb_ext.BidderAppnexus: adapters.BuildInfoAwareBidder(appnexusBidder, infoEnabled),
+				openrtb_ext.BidderRubicon:  adapters.BuildInfoAwareBidder(rubiconBidder, infoEnabled),
 			},
 		},
 		{
 			description:   "Success - Ignores Legacy",
 			adapterConfig: map[string]config.Adapter{"appnexus": {}, "lifestreet": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive, "lifestreet": infoActive},
+			bidderInfos:   map[string]config.BidderInfo{"appnexus": infoEnabled, "lifestreet": infoEnabled},
 			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder, openrtb_ext.BidderLifestreet: inconsequentialBuilder},
 			expectedBidders: map[openrtb_ext.BidderName]adapters.Bidder{
-				openrtb_ext.BidderAppnexus: adapters.EnforceBidderInfo(appnexusBidder, infoActive),
+				openrtb_ext.BidderAppnexus: adapters.BuildInfoAwareBidder(appnexusBidder, infoEnabled),
 			},
 		},
 		{
 			description:   "Success - Ignores Disabled",
 			adapterConfig: map[string]config.Adapter{"appnexus": {}, "rubicon": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoDisabled, "rubicon": infoActive},
+			bidderInfos:   map[string]config.BidderInfo{"appnexus": infoDisabled, "rubicon": infoEnabled},
 			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder, openrtb_ext.BidderRubicon: rubiconBuilder},
 			expectedBidders: map[openrtb_ext.BidderName]adapters.Bidder{
-				openrtb_ext.BidderRubicon: adapters.EnforceBidderInfo(rubiconBidder, infoActive),
-			},
-		},
-		{
-			description:   "Success - Ignores Unknown State",
-			adapterConfig: map[string]config.Adapter{"appnexus": {}, "rubicon": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoUnknown, "rubicon": infoActive},
-			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder, openrtb_ext.BidderRubicon: rubiconBuilder},
-			expectedBidders: map[openrtb_ext.BidderName]adapters.Bidder{
-				openrtb_ext.BidderRubicon: adapters.EnforceBidderInfo(rubiconBidder, infoActive),
+				openrtb_ext.BidderRubicon: adapters.BuildInfoAwareBidder(rubiconBidder, infoEnabled),
 			},
 		},
 		{
 			description:   "Success - Ignores Adapter Config Case",
 			adapterConfig: map[string]config.Adapter{"AppNexus": {}},
-			bidderInfos:   map[string]adapters.BidderInfo{"appnexus": infoActive},
+			bidderInfos:   map[string]config.BidderInfo{"appnexus": infoEnabled},
 			builders:      map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder},
 			expectedBidders: map[openrtb_ext.BidderName]adapters.Bidder{
-				openrtb_ext.BidderAppnexus: adapters.EnforceBidderInfo(appnexusBidder, infoActive),
+				openrtb_ext.BidderAppnexus: adapters.BuildInfoAwareBidder(appnexusBidder, infoEnabled),
 			},
 		},
 	}
@@ -270,25 +260,19 @@ func TestBuildExchangeBiddersLegacy(t *testing.T) {
 	testCases := []struct {
 		description   string
 		adapterConfig map[string]config.Adapter
-		bidderInfos   map[string]adapters.BidderInfo
+		bidderInfos   map[string]config.BidderInfo
 		expected      map[openrtb_ext.BidderName]adaptedBidder
 	}{
 		{
 			description:   "Active",
 			adapterConfig: map[string]config.Adapter{"lifestreet": cfg},
-			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoActive},
+			bidderInfos:   map[string]config.BidderInfo{"lifestreet": infoEnabled},
 			expected:      map[openrtb_ext.BidderName]adaptedBidder{"lifestreet": expectedLifestreet},
 		},
 		{
 			description:   "Disabled",
 			adapterConfig: map[string]config.Adapter{"lifestreet": cfg},
-			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoDisabled},
-			expected:      map[openrtb_ext.BidderName]adaptedBidder{},
-		},
-		{
-			description:   "Unknown",
-			adapterConfig: map[string]config.Adapter{"lifestreet": cfg},
-			bidderInfos:   map[string]adapters.BidderInfo{"lifestreet": infoUnknown},
+			bidderInfos:   map[string]config.BidderInfo{"lifestreet": infoDisabled},
 			expected:      map[openrtb_ext.BidderName]adaptedBidder{},
 		},
 	}
@@ -318,33 +302,28 @@ func TestWrapWithMiddleware(t *testing.T) {
 func TestGetActiveBidders(t *testing.T) {
 	testCases := []struct {
 		description string
-		bidderInfos map[string]adapters.BidderInfo
+		bidderInfos map[string]config.BidderInfo
 		expected    map[string]openrtb_ext.BidderName
 	}{
 		{
 			description: "None",
-			bidderInfos: map[string]adapters.BidderInfo{},
+			bidderInfos: map[string]config.BidderInfo{},
 			expected:    map[string]openrtb_ext.BidderName{},
 		},
 		{
-			description: "Active",
-			bidderInfos: map[string]adapters.BidderInfo{"appnexus": infoActive},
+			description: "Enabled",
+			bidderInfos: map[string]config.BidderInfo{"appnexus": infoEnabled},
 			expected:    map[string]openrtb_ext.BidderName{"appnexus": openrtb_ext.BidderAppnexus},
 		},
 		{
 			description: "Disabled",
-			bidderInfos: map[string]adapters.BidderInfo{"appnexus": infoDisabled},
+			bidderInfos: map[string]config.BidderInfo{"appnexus": infoDisabled},
 			expected:    map[string]openrtb_ext.BidderName{},
 		},
-		{
-			description: "Unknown",
-			bidderInfos: map[string]adapters.BidderInfo{"appnexus": infoUnknown},
-			expected:    map[string]openrtb_ext.BidderName{"appnexus": openrtb_ext.BidderAppnexus},
-		},
 		{
 			description: "Mixed",
-			bidderInfos: map[string]adapters.BidderInfo{"appnexus": infoDisabled, "openx": infoActive, "rubicon": infoUnknown},
-			expected:    map[string]openrtb_ext.BidderName{"openx": openrtb_ext.BidderOpenx, "rubicon": openrtb_ext.BidderRubicon},
+			bidderInfos: map[string]config.BidderInfo{"appnexus": infoDisabled, "openx": infoEnabled},
+			expected:    map[string]openrtb_ext.BidderName{"openx": openrtb_ext.BidderOpenx},
 		},
 	}
 
@@ -357,34 +336,29 @@ func TestGetActiveBidders(t *testing.T) {
 func TestGetDisabledBiddersErrorMessages(t *testing.T) {
 	testCases := []struct {
 		description string
-		bidderInfos map[string]adapters.BidderInfo
+		bidderInfos map[string]config.BidderInfo
 		expected    map[string]string
 	}{
 		{
 			description: "None",
-			bidderInfos: map[string]adapters.BidderInfo{},
+			bidderInfos: map[string]config.BidderInfo{},
 			expected:    map[string]string{},
 		},
 		{
-			description: "Active",
-			bidderInfos: map[string]adapters.BidderInfo{"appnexus": infoActive},
+			description: "Enabled",
+			bidderInfos: map[string]config.BidderInfo{"appnexus": infoEnabled},
 			expected:    map[string]string{},
 		},
 		{
 			description: "Disabled",
-			bidderInfos: map[string]adapters.BidderInfo{"appnexus": infoDisabled},
+			bidderInfos: map[string]config.BidderInfo{"appnexus": infoDisabled},
 			expected: map[string]string{
 				"appnexus": `Bidder "appnexus" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`,
 			},
 		},
-		{
-			description: "Unknown",
-			bidderInfos: map[string]adapters.BidderInfo{"appnexus": infoUnknown},
-			expected:    map[string]string{},
-		},
 		{
 			description: "Mixed",
-			bidderInfos: map[string]adapters.BidderInfo{"appnexus": infoDisabled, "openx": infoActive, "rubicon": infoUnknown},
+			bidderInfos: map[string]config.BidderInfo{"appnexus": infoDisabled, "openx": infoEnabled},
 			expected:    map[string]string{"appnexus": `Bidder "appnexus" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`},
 		},
 	}
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index b809c7e0..b829ddd7 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -36,13 +36,13 @@ import (
 // 2. The returned values are correct for a non-test bid.
 func TestSingleBidder(t *testing.T) {
 	type aTest struct {
-		debugInfo    *adapters.DebugInfo
+		debugInfo    *config.DebugInfo
 		httpCallsLen int
 	}
 
 	testCases := []*aTest{
-		{&adapters.DebugInfo{Allow: false}, 0},
-		{&adapters.DebugInfo{Allow: true}, 1},
+		{&config.DebugInfo{Allow: false}, 0},
+		{&config.DebugInfo{Allow: true}, 1},
 	}
 
 	respStatus := 200
@@ -1437,13 +1437,13 @@ func TestTimeoutNotificationOn(t *testing.T) {
 }
 
 func TestParseDebugInfoTrue(t *testing.T) {
-	debugInfo := &adapters.DebugInfo{Allow: true}
+	debugInfo := &config.DebugInfo{Allow: true}
 	resDebugInfo := parseDebugInfo(debugInfo)
 	assert.True(t, resDebugInfo, "Debug Allow value should be true")
 }
 
 func TestParseDebugInfoFalse(t *testing.T) {
-	debugInfo := &adapters.DebugInfo{Allow: false}
+	debugInfo := &config.DebugInfo{Allow: false}
 	resDebugInfo := parseDebugInfo(debugInfo)
 	assert.False(t, resDebugInfo, "Debug Allow value should be false")
 }
@@ -1454,15 +1454,15 @@ func TestParseDebugInfoIsNil(t *testing.T) {
 }
 
 func wrapWithBidderInfo(bidder adapters.Bidder) adapters.Bidder {
-	bidderInfo := adapters.BidderInfo{
-		Status: adapters.StatusActive,
-		Capabilities: &adapters.CapabilitiesInfo{
-			App: &adapters.PlatformInfo{
+	bidderInfo := config.BidderInfo{
+		Enabled: true,
+		Capabilities: &config.CapabilitiesInfo{
+			App: &config.PlatformInfo{
 				MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner},
 			},
 		},
 	}
-	return adapters.EnforceBidderInfo(bidder, bidderInfo)
+	return adapters.BuildInfoAwareBidder(bidder, bidderInfo)
 }
 
 type goodSingleBidder struct {
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 8bdf6c45..7507ecef 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -52,7 +52,11 @@ func TestNewExchange(t *testing.T) {
 		},
 	}
 
-	biddersInfo := adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", knownAdapters)
+	biddersInfo, err := config.LoadBidderInfoFromDisk("../static/bidder-info", cfg.Adapters, openrtb_ext.BuildBidderStringSlice())
+	if err != nil {
+		t.Fatal(err)
+	}
+
 	adapters, adaptersErr := BuildAdapters(server.Client(), cfg, biddersInfo, &metricsConf.DummyMetricsEngine{})
 	if adaptersErr != nil {
 		t.Fatalf("Error intializing adapters: %v", adaptersErr)
@@ -96,7 +100,11 @@ func TestCharacterEscape(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(handlerNoBidServer))
 	defer server.Close()
 
-	biddersInfo := adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.CoreBidderNames())
+	biddersInfo, err := config.LoadBidderInfoFromDisk("../static/bidder-info", cfg.Adapters, openrtb_ext.BuildBidderStringSlice())
+	if err != nil {
+		t.Fatal(err)
+	}
+
 	adapters, adaptersErr := BuildAdapters(server.Client(), cfg, biddersInfo, &metricsConf.DummyMetricsEngine{})
 	if adaptersErr != nil {
 		t.Fatalf("Error intializing adapters: %v", adaptersErr)
@@ -277,7 +285,7 @@ func TestDebugBehaviour(t *testing.T) {
 	for _, test := range testCases {
 
 		e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{
-			openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &adapters.DebugInfo{Allow: test.debugData.bidderLevelDebugAllowed}),
+			openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &config.DebugInfo{Allow: test.debugData.bidderLevelDebugAllowed}),
 		}
 
 		//request level debug key
@@ -411,8 +419,8 @@ func TestTwoBiddersDebugDisabledAndEnabled(t *testing.T) {
 		}
 
 		e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{
-			openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &adapters.DebugInfo{Allow: testCase.bidder1DebugEnabled}),
-			openrtb_ext.BidderTelaria:  adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &adapters.DebugInfo{Allow: testCase.bidder2DebugEnabled}),
+			openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &config.DebugInfo{Allow: testCase.bidder1DebugEnabled}),
+			openrtb_ext.BidderTelaria:  adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &config.DebugInfo{Allow: testCase.bidder2DebugEnabled}),
 		}
 		// Run test
 		outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, &debugLog)
@@ -680,7 +688,11 @@ func TestGetBidCacheInfoEndToEnd(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(handlerNoBidServer))
 	defer server.Close()
 
-	biddersInfo := adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.CoreBidderNames())
+	biddersInfo, err := config.LoadBidderInfoFromDisk("../static/bidder-info", cfg.Adapters, openrtb_ext.BuildBidderStringSlice())
+	if err != nil {
+		t.Fatal(err)
+	}
+
 	adapters, adaptersErr := BuildAdapters(server.Client(), cfg, biddersInfo, &metricsConf.DummyMetricsEngine{})
 	if adaptersErr != nil {
 		t.Fatalf("Error intializing adapters: %v", adaptersErr)
@@ -1029,7 +1041,11 @@ func TestBidResponseCurrency(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(handlerNoBidServer))
 	defer server.Close()
 
-	biddersInfo := adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.CoreBidderNames())
+	biddersInfo, err := config.LoadBidderInfoFromDisk("../static/bidder-info", cfg.Adapters, openrtb_ext.BuildBidderStringSlice())
+	if err != nil {
+		t.Fatal(err)
+	}
+
 	adapters, adaptersErr := BuildAdapters(server.Client(), cfg, biddersInfo, &metricsConf.DummyMetricsEngine{})
 	if adaptersErr != nil {
 		t.Fatalf("Error intializing adapters: %v", adaptersErr)
@@ -1203,7 +1219,11 @@ func TestRaceIntegration(t *testing.T) {
 		ExtraAdapterInfo: "{\"video_endpoint\":\"" + server.URL + "\"}",
 	}
 
-	biddersInfo := adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.CoreBidderNames())
+	biddersInfo, err := config.LoadBidderInfoFromDisk("../static/bidder-info", cfg.Adapters, openrtb_ext.BuildBidderStringSlice())
+	if err != nil {
+		t.Fatal(err)
+	}
+
 	adapters, adaptersErr := BuildAdapters(server.Client(), cfg, biddersInfo, &metricsConf.DummyMetricsEngine{})
 	if adaptersErr != nil {
 		t.Fatalf("Error intializing adapters: %v", adaptersErr)
@@ -1219,7 +1239,7 @@ func TestRaceIntegration(t *testing.T) {
 
 	debugLog := DebugLog{}
 	ex := NewExchange(adapters, &wellBehavedCache{}, cfg, &metricsConf.DummyMetricsEngine{}, biddersInfo, gdpr.AlwaysAllow{}, currencyConverter, &nilCategoryFetcher{}).(*exchange)
-	_, err := ex.HoldAuction(context.Background(), auctionRequest, &debugLog)
+	_, err = ex.HoldAuction(context.Background(), auctionRequest, &debugLog)
 	if err != nil {
 		t.Errorf("HoldAuction returned unexpected error: %v", err)
 	}
@@ -1301,7 +1321,11 @@ func TestPanicRecovery(t *testing.T) {
 		Adapters: blankAdapterConfig(openrtb_ext.CoreBidderNames()),
 	}
 
-	biddersInfo := adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.CoreBidderNames())
+	biddersInfo, err := config.LoadBidderInfoFromDisk("../static/bidder-info", cfg.Adapters, openrtb_ext.BuildBidderStringSlice())
+	if err != nil {
+		t.Fatal(err)
+	}
+
 	adapters, adaptersErr := BuildAdapters(&http.Client{}, cfg, biddersInfo, &metricsConf.DummyMetricsEngine{})
 	if adaptersErr != nil {
 		t.Fatalf("Error intializing adapters: %v", adaptersErr)
@@ -1387,7 +1411,11 @@ func TestPanicRecoveryHighLevel(t *testing.T) {
 	}
 	cfg.Adapters["audiencenetwork"] = config.Adapter{Disabled: true}
 
-	biddersInfo := adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.CoreBidderNames())
+	biddersInfo, err := config.LoadBidderInfoFromDisk("../static/bidder-info", cfg.Adapters, openrtb_ext.BuildBidderStringSlice())
+	if err != nil {
+		t.Fatal(err)
+	}
+
 	adapters, adaptersErr := BuildAdapters(server.Client(), cfg, biddersInfo, &metricsConf.DummyMetricsEngine{})
 	if adaptersErr != nil {
 		t.Fatalf("Error intializing adapters: %v", adaptersErr)
@@ -1439,7 +1467,7 @@ func TestPanicRecoveryHighLevel(t *testing.T) {
 		UserSyncs:  &emptyUsersync{},
 	}
 	debugLog := DebugLog{}
-	_, err := e.HoldAuction(context.Background(), auctionRequest, &debugLog)
+	_, err = e.HoldAuction(context.Background(), auctionRequest, &debugLog)
 	if err != nil {
 		t.Errorf("HoldAuction returned unexpected error: %v", err)
 	}
@@ -1608,7 +1636,6 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 	if spec.IncomingRequest.OrtbRequest.Test == 1 {
 		//compare debug info
 		diffJson(t, "Debug info modified", bid.Ext, spec.Response.Ext)
-
 	}
 }
 
@@ -1651,7 +1678,7 @@ func extractResponseTimes(t *testing.T, context string, bid *openrtb.BidResponse
 
 func newExchangeForTests(t *testing.T, filename string, expectations map[string]*bidderSpec, aliases map[string]string, privacyConfig config.Privacy) Exchange {
 	bidderAdapters := make(map[openrtb_ext.BidderName]adaptedBidder, len(expectations))
-	bidderInfos := make(adapters.BidderInfos, len(expectations))
+	bidderInfos := make(config.BidderInfos, len(expectations))
 	for _, bidderName := range openrtb_ext.CoreBidderNames() {
 		if spec, ok := expectations[string(bidderName)]; ok {
 			bidderAdapters[bidderName] = &validatingBidder{
@@ -1661,7 +1688,7 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 				expectations:  map[string]*bidderRequest{string(bidderName): spec.ExpectedRequest},
 				mockResponses: map[string]bidderResponse{string(bidderName): spec.MockResponse},
 			}
-			bidderInfos[string(bidderName)] = adapters.BidderInfo{ModifyingVastXmlAllowed: spec.ModifyingVastXmlAllowed}
+			bidderInfos[string(bidderName)] = config.BidderInfo{ModifyingVastXmlAllowed: spec.ModifyingVastXmlAllowed}
 		}
 	}
 
diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go
index 77f3f294..27f1bc3b 100644
--- a/gdpr/vendorlist-fetching_test.go
+++ b/gdpr/vendorlist-fetching_test.go
@@ -66,67 +66,13 @@ func TestTCF1FetcherInitialLoad(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		runTest(t, test, tcf1SpecVersion, server)
-	}
-}
-
-func TestTCF2FetcherInitialLoad(t *testing.T) {
-	// Loads two vendor lists during initialization by setting the latest vendor list version to 2.
-	// Ensures TCF1 fetch settings have no effect on TCF2.
-
-	server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{
-		vendorListLatestVersion: 2,
-		vendorLists: map[int]string{
-			1: tcf2VendorList1,
-			2: tcf2VendorList2,
-		},
-	})))
-	defer server.Close()
-
-	testCases := []test{
-		{
-			description: "Fallback - Vendor List 1",
-			setup: testSetup{
-				enableTCF1Fallback: true,
-				vendorListVersion:  1,
-			},
-			expected: vendorList1Expected,
-		},
-		{
-			description: "Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fallback: true,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
-		},
-		{
-			description: "No Fallback - Vendor List 1",
-			setup: testSetup{
-				enableTCF1Fallback: false,
-				vendorListVersion:  1,
-			},
-			expected: vendorList1Expected,
-		},
-		{
-			description: "No Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fallback: false,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
-		},
-	}
-
-	for _, test := range testCases {
-		runTest(t, test, tcf2SpecVersion, server)
+		runTestTCF1(t, test, server)
 	}
 }
 
 func TestTCF2FetcherDynamicLoadListExists(t *testing.T) {
 	// Loads the first vendor list during initialization by setting the latest vendor list version to 1.
 	// All other vendor lists will be dynamically loaded.
-	// Ensures TCF1 fetch settings have no effect on TCF2.
 
 	server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{
 		vendorListLatestVersion: 1,
@@ -137,34 +83,20 @@ func TestTCF2FetcherDynamicLoadListExists(t *testing.T) {
 	})))
 	defer server.Close()
 
-	testCases := []test{
-		{
-			description: "Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fallback: true,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
-		},
-		{
-			description: "No Fallback - Vendor List 2",
-			setup: testSetup{
-				enableTCF1Fallback: false,
-				vendorListVersion:  2,
-			},
-			expected: vendorList2Expected,
+	test := test{
+		description: "Dynamic Load - List Exists",
+		setup: testSetup{
+			vendorListVersion: 2,
 		},
+		expected: vendorList2Expected,
 	}
 
-	for _, test := range testCases {
-		runTest(t, test, tcf2SpecVersion, server)
-	}
+	runTestTCF2(t, test, server)
 }
 
 func TestTCF2FetcherDynamicLoadListDoesntExist(t *testing.T) {
 	// Loads the first vendor list during initialization by setting the latest vendor list version to 1.
 	// All other vendor list load attempts will be done dynamically.
-	// Ensures TCF1 fetch settings have no effect on TCF2.
 
 	server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{
 		vendorListLatestVersion: 1,
@@ -174,32 +106,17 @@ func TestTCF2FetcherDynamicLoadListDoesntExist(t *testing.T) {
 	})))
 	defer server.Close()
 
-	testCases := []test{
-		{
-			description: "Fallback - Vendor Doesn't Exist",
-			setup: testSetup{
-				enableTCF1Fallback: true,
-				vendorListVersion:  2,
-			},
-			expected: testExpected{
-				errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes",
-			},
+	test := test{
+		description: "No Fallback - Vendor Doesn't Exist",
+		setup: testSetup{
+			vendorListVersion: 2,
 		},
-		{
-			description: "No Fallback - Vendor Doesn't Exist",
-			setup: testSetup{
-				enableTCF1Fallback: false,
-				vendorListVersion:  2,
-			},
-			expected: testExpected{
-				errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes",
-			},
+		expected: testExpected{
+			errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes",
 		},
 	}
 
-	for _, test := range testCases {
-		runTest(t, test, tcf2SpecVersion, server)
-	}
+	runTestTCF2(t, test, server)
 }
 
 func TestTCF2FetcherThrottling(t *testing.T) {
@@ -222,7 +139,7 @@ func TestTCF2FetcherThrottling(t *testing.T) {
 	})))
 	defer server.Close()
 
-	fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf2SpecVersion)
+	fetcher := newVendorListFetcherTCF2(context.Background(), testConfig(), server.Client(), testURLMaker(server))
 
 	// Dynamically Load List 2 Successfully
 	_, errList1 := fetcher(context.Background(), 2)
@@ -243,7 +160,7 @@ func TestTCF2MalformedVendorlist(t *testing.T) {
 	})))
 	defer server.Close()
 
-	fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf2SpecVersion)
+	fetcher := newVendorListFetcherTCF2(context.Background(), testConfig(), server.Client(), testURLMaker(server))
 	_, err := fetcher(context.Background(), 1)
 
 	// Fetching should fail since vendor list could not be unmarshalled.
@@ -254,9 +171,9 @@ func TestTCF2ServerUrlInvalid(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
 	server.Close()
 
-	invalidURLGenerator := func(uint16, uint8) string { return " http://invalid-url-has-leading-whitespace" }
+	invalidURLGenerator := func(uint16) string { return " http://invalid-url-has-leading-whitespace" }
 
-	fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), invalidURLGenerator, tcf2SpecVersion)
+	fetcher := newVendorListFetcherTCF2(context.Background(), testConfig(), server.Client(), invalidURLGenerator)
 	_, err := fetcher(context.Background(), 1)
 
 	assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes")
@@ -266,7 +183,7 @@ func TestTCF2ServerUnavailable(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
 	server.Close()
 
-	fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf2SpecVersion)
+	fetcher := newVendorListFetcherTCF2(context.Background(), testConfig(), server.Client(), testURLMaker(server))
 	_, err := fetcher(context.Background(), 1)
 
 	assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes")
@@ -275,38 +192,23 @@ func TestTCF2ServerUnavailable(t *testing.T) {
 func TestVendorListURLMaker(t *testing.T) {
 	testCases := []struct {
 		description       string
-		tcfSpecVersion    uint8
 		vendorListVersion uint16
 		expectedURL       string
 	}{
 		{
-			description:       "TCF1 - Latest",
-			tcfSpecVersion:    1,
-			vendorListVersion: 0, // Forces latest version.
-			expectedURL:       "https://vendor-list.consensu.org/vendorlist.json",
-		},
-		{
-			description:       "TCF1 - Specific",
-			tcfSpecVersion:    1,
-			vendorListVersion: 42,
-			expectedURL:       "https://vendor-list.consensu.org/v-42/vendorlist.json",
-		},
-		{
-			description:       "TCF2 - Latest",
-			tcfSpecVersion:    2,
-			vendorListVersion: 0, // Forces latest version.
+			description:       "Latest",
+			vendorListVersion: 0,
 			expectedURL:       "https://vendor-list.consensu.org/v2/vendor-list.json",
 		},
 		{
-			description:       "TCF2 - Specific",
-			tcfSpecVersion:    2,
+			description:       "Specific",
 			vendorListVersion: 42,
 			expectedURL:       "https://vendor-list.consensu.org/v2/archives/vendor-list-v42.json",
 		},
 	}
 
 	for _, test := range testCases {
-		result := vendorListURLMaker(test.vendorListVersion, test.tcfSpecVersion)
+		result := vendorListURLMaker(test.vendorListVersion)
 		assert.Equal(t, test.expectedURL, result)
 	}
 }
@@ -321,12 +223,6 @@ var tcf2VendorList1 = tcf2MarshalVendorList(tcf2VendorList{
 	Vendors:           map[string]*tcf2Vendor{"12": {ID: 12, Purposes: []int{2}}},
 })
 
-var vendorList1Expected = testExpected{
-	vendorListVersion: 1,
-	vendorID:          12,
-	vendorPurposes:    map[int]bool{1: false, 2: true, 3: false},
-}
-
 var tcf1VendorList2 = tcf1MarshalVendorList(tcf1VendorList{
 	VendorListVersion: 2,
 	Vendors:           []tcf1Vendor{{ID: 12, Purposes: []int{2, 3}}},
@@ -438,13 +334,31 @@ type testExpected struct {
 	vendorPurposes    map[int]bool
 }
 
-func runTest(t *testing.T, test test, tcfSpecVersion uint8, server *httptest.Server) {
+func runTestTCF1(t *testing.T, test test, server *httptest.Server) {
 	config := testConfig()
 	if test.setup.enableTCF1Fallback {
 		config.TCF1.FallbackGVLPath = "../static/tcf1/fallback_gvl.json"
 	}
 
-	fetcher := newVendorListFetcher(context.Background(), config, server.Client(), testURLMaker(server), tcfSpecVersion)
+	fetcher := newVendorListFetcherTCF1(config)
+	vendorList, err := fetcher(context.Background(), test.setup.vendorListVersion)
+
+	if test.expected.errorMessage != "" {
+		assert.EqualError(t, err, test.expected.errorMessage, test.description+":error")
+	} else {
+		assert.NoError(t, err, test.description+":vendorlist")
+		assert.Equal(t, test.expected.vendorListVersion, vendorList.Version(), test.description+":vendorlistid")
+		vendor := vendorList.Vendor(test.expected.vendorID)
+		for id, expected := range test.expected.vendorPurposes {
+			result := vendor.Purpose(consentconstants.Purpose(id))
+			assert.Equalf(t, expected, result, "%s:vendor-%d:purpose-%d", test.description, vendorList.Version(), id)
+		}
+	}
+}
+
+func runTestTCF2(t *testing.T, test test, server *httptest.Server) {
+	config := testConfig()
+	fetcher := newVendorListFetcherTCF2(context.Background(), config, server.Client(), testURLMaker(server))
 	vendorList, err := fetcher(context.Background(), test.setup.vendorListVersion)
 
 	if test.expected.errorMessage != "" {
@@ -460,9 +374,9 @@ func runTest(t *testing.T, test test, tcfSpecVersion uint8, server *httptest.Ser
 	}
 }
 
-func testURLMaker(server *httptest.Server) func(uint16, uint8) string {
+func testURLMaker(server *httptest.Server) func(uint16) string {
 	url := server.URL
-	return func(vendorListVersion uint16, tcfSpecVersion uint8) string {
+	return func(vendorListVersion uint16) string {
 		return url + "?version=" + strconv.Itoa(int(vendorListVersion))
 	}
 }
diff --git a/openrtb_ext/bidders_test.go b/openrtb_ext/bidders_test.go
index 9c4889c9..26ebf7dc 100644
--- a/openrtb_ext/bidders_test.go
+++ b/openrtb_ext/bidders_test.go
@@ -2,114 +2,119 @@ package openrtb_ext
 
 import (
 	"encoding/json"
-	"os"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/xeipuuv/gojsonschema"
 )
 
-// TestMain does the expensive setup so we don't keep re-reading the files in static/bidder-params for each test.
-func TestMain(m *testing.M) {
-	bidderParams, err := NewBidderParamsValidator("../static/bidder-params")
-	if err != nil {
-		os.Exit(1)
+func TestBidderParamValidatorValidate(t *testing.T) {
+	testSchemaLoader := gojsonschema.NewStringLoader(`{
+		"$schema": "http://json-schema.org/draft-04/schema#",
+		"title": "Test Params",
+		"description": "Test Description",
+		"type": "object",
+		"properties": {
+		  "placementId": {
+			"type": "integer",
+			"description": "An ID which identifies this placement of the impression."
+		  },
+		  "optionalText": {
+			"type": "string",
+			"description": "Optional text for testing."
+		  }
+		},
+		"required": ["placementId"]
+	}`)
+	testSchema, err := gojsonschema.NewSchema(testSchemaLoader)
+	if !assert.NoError(t, err) {
+		t.FailNow()
 	}
-	validator = bidderParams
-	os.Exit(m.Run())
-}
-
-var validator BidderParamValidator
-
-// TestBidderParamSchemas makes sure that the validator.Schema() function
-// returns valid JSON for all known CoreBidderNames.
-func TestBidderParamSchemas(t *testing.T) {
-	for _, bidderName := range CoreBidderNames() {
-		schema := validator.Schema(bidderName)
-		if schema == "" {
-			t.Errorf("No schema exists for bidder %s. Does static/bidder-params/%s.json exist?", bidderName, bidderName)
-		}
-
-		if _, err := gojsonschema.NewBytesLoader([]byte(schema)).LoadJSON(); err != nil {
-			t.Errorf("static/bidder-params/%s.json does not have a valid json-schema. %v", bidderName, err)
-		}
+	testBidderName := BidderName("foo")
+	testValidator := bidderParamValidator{
+		parsedSchemas: map[BidderName]*gojsonschema.Schema{
+			testBidderName: testSchema,
+		},
 	}
-}
 
-// TestValidParams and TestInvalidParams overlap with adapters/appnexus/params_test... but those tests
-// from the other packages don't show up in code coverage.
-func TestValidParams(t *testing.T) {
-	if err := validator.Validate(BidderAppnexus, json.RawMessage(`{"placementId":123}`)); err != nil {
-		t.Errorf("These params should be valid. Error was: %v", err)
+	testCases := []struct {
+		description   string
+		ext           json.RawMessage
+		expectedError string
+	}{
+		{
+			description:   "Valid",
+			ext:           json.RawMessage(`{"placementId":123}`),
+			expectedError: "",
+		},
+		{
+			description:   "Invalid - Wrong Type",
+			ext:           json.RawMessage(`{"placementId":"stringInsteadOfInt"}`),
+			expectedError: "placementId: Invalid type. Expected: integer, given: string",
+		},
+		{
+			description:   "Invalid - Empty Object",
+			ext:           json.RawMessage(`{}`),
+			expectedError: "placementId: placementId is required",
+		},
+		{
+			description:   "Malformed",
+			ext:           json.RawMessage(`malformedJSON`),
+			expectedError: "invalid character 'm' looking for beginning of value",
+		},
 	}
-}
 
-func TestInvalidParams(t *testing.T) {
-	if err := validator.Validate(BidderAppnexus, json.RawMessage(`{}`)); err == nil {
-		t.Error("These params should be invalid.")
+	for _, test := range testCases {
+		err := testValidator.Validate(testBidderName, test.ext)
+		if test.expectedError == "" {
+			assert.NoError(t, err, test.description)
+		} else {
+			assert.EqualError(t, err, test.expectedError, test.description)
+		}
 	}
 }
 
-func TestBidderListDoesNotDefineGeneral(t *testing.T) {
-	assert.NotContains(t, CoreBidderNames(), BidderNameGeneral)
-}
-
-func TestBidderListDoesNotDefineContext(t *testing.T) {
-	assert.NotContains(t, CoreBidderNames(), BidderNameContext)
-}
-
-// TestBidderUniquenessGatekeeping acts as a gatekeeper of bidder name uniqueness. If this test fails
-// when you're building a new adapter, please consider choosing a different bidder name to maintain the
-// current uniqueness threshold, or else start a discussion in the PR.
-func TestBidderUniquenessGatekeeping(t *testing.T) {
-	// Get List Of Bidders
-	// - Exclude duplicates of adapters for the same bidder, as it's unlikely a publisher will use both.
-	var bidders []string
-	for _, bidder := range CoreBidderNames() {
-		if bidder != BidderTripleliftNative && bidder != BidderAdkernelAdn {
-			bidders = append(bidders, string(bidder))
-		}
+func TestBidderParamValidatorSchema(t *testing.T) {
+	testValidator := bidderParamValidator{
+		schemaContents: map[BidderName]string{
+			BidderName("foo"): "foo content",
+			BidderName("bar"): "bar content",
+		},
 	}
 
-	currentThreshold := 6
-	measuredThreshold := minUniquePrefixLength(bidders)
-
-	assert.NotZero(t, measuredThreshold, "BidderMap contains duplicate bidder name values.")
-	assert.LessOrEqual(t, measuredThreshold, currentThreshold)
-}
+	result := testValidator.Schema(BidderName("bar"))
 
-// minUniquePrefixLength measures the minimun amount of characters needed to uniquely identify
-// one of the strings, or returns 0 if there are duplicates.
-func minUniquePrefixLength(b []string) int {
-	targetingKeyMaxLength := 20
-	for prefixLength := 1; prefixLength <= targetingKeyMaxLength; prefixLength++ {
-		if uniqueForPrefixLength(b, prefixLength) {
-			return prefixLength
-		}
-	}
-	return 0
+	assert.Equal(t, "bar content", result)
 }
 
-func uniqueForPrefixLength(b []string, prefixLength int) bool {
-	m := make(map[string]struct{})
-
-	if prefixLength <= 0 {
-		return false
+func TestIsBidderNameReserved(t *testing.T) {
+	testCases := []struct {
+		bidder   string
+		expected bool
+	}{
+		{"all", true},
+		{"aLl", true},
+		{"ALL", true},
+		{"context", true},
+		{"CONTEXT", true},
+		{"conTExt", true},
+		{"data", true},
+		{"DATA", true},
+		{"DaTa", true},
+		{"general", true},
+		{"gEnErAl", true},
+		{"GENERAL", true},
+		{"skadn", true},
+		{"skADN", true},
+		{"SKADN", true},
+		{"prebid", true},
+		{"PREbid", true},
+		{"PREBID", true},
+		{"notreserved", false},
 	}
 
-	for i, n := range b {
-		ns := string(n)
-
-		if len(ns) > prefixLength {
-			ns = ns[0:prefixLength]
-		}
-
-		m[ns] = struct{}{}
-
-		if len(m) != i+1 {
-			return false
-		}
+	for _, test := range testCases {
+		result := IsBidderNameReserved(test.bidder)
+		assert.Equal(t, test.expected, result, test.bidder)
 	}
-
-	return true
 }
diff --git a/openrtb_ext/bidders_validate_test.go b/openrtb_ext/bidders_validate_test.go
new file mode 100644
index 00000000..2ee3dd7d
--- /dev/null
+++ b/openrtb_ext/bidders_validate_test.go
@@ -0,0 +1,99 @@
+package openrtb_ext
+
+import (
+	"os"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/xeipuuv/gojsonschema"
+)
+
+// TestMain does the expensive setup so we don't keep re-reading the files in static/bidder-params for each test.
+func TestMain(m *testing.M) {
+	bidderParams, err := NewBidderParamsValidator("../static/bidder-params")
+	if err != nil {
+		os.Exit(1)
+	}
+	validator = bidderParams
+	os.Exit(m.Run())
+}
+
+var validator BidderParamValidator
+
+// TestBidderParamSchemas makes sure that the validator.Schema() function
+// returns valid JSON for all known CoreBidderNames.
+func TestBidderParamSchemas(t *testing.T) {
+	for _, bidderName := range CoreBidderNames() {
+		schema := validator.Schema(bidderName)
+		if schema == "" {
+			t.Errorf("No schema exists for bidder %s. Does static/bidder-params/%s.json exist?", bidderName, bidderName)
+		}
+
+		if _, err := gojsonschema.NewBytesLoader([]byte(schema)).LoadJSON(); err != nil {
+			t.Errorf("static/bidder-params/%s.json does not have a valid json-schema. %v", bidderName, err)
+		}
+	}
+}
+
+func TestBidderNamesValid(t *testing.T) {
+	for _, bidder := range CoreBidderNames() {
+		isReserved := IsBidderNameReserved(string(bidder))
+		assert.False(t, isReserved, "bidder %v conflicts with a reserved name", bidder)
+	}
+}
+
+// TestBidderUniquenessGatekeeping acts as a gatekeeper of bidder name uniqueness. If this test fails
+// when you're building a new adapter, please consider choosing a different bidder name to maintain the
+// current uniqueness threshold, or else start a discussion in the PR.
+func TestBidderUniquenessGatekeeping(t *testing.T) {
+	// Get List Of Bidders
+	// - Exclude duplicates of adapters for the same bidder, as it's unlikely a publisher will use both.
+	var bidders []string
+	for _, bidder := range CoreBidderNames() {
+		if bidder != BidderTripleliftNative && bidder != BidderAdkernelAdn {
+			bidders = append(bidders, string(bidder))
+		}
+	}
+
+	currentThreshold := 6
+	measuredThreshold := minUniquePrefixLength(bidders)
+
+	assert.NotZero(t, measuredThreshold, "BidderMap contains duplicate bidder name values.")
+	assert.LessOrEqual(t, measuredThreshold, currentThreshold)
+}
+
+// minUniquePrefixLength measures the minimun amount of characters needed to uniquely identify
+// one of the strings, or returns 0 if there are duplicates.
+func minUniquePrefixLength(b []string) int {
+	targetingKeyMaxLength := 20
+	for prefixLength := 1; prefixLength <= targetingKeyMaxLength; prefixLength++ {
+		if uniqueForPrefixLength(b, prefixLength) {
+			return prefixLength
+		}
+	}
+	return 0
+}
+
+func uniqueForPrefixLength(b []string, prefixLength int) bool {
+	m := make(map[string]struct{})
+
+	if prefixLength <= 0 {
+		return false
+	}
+
+	for i, n := range b {
+		ns := string(n)
+
+		if len(ns) > prefixLength {
+			ns = ns[0:prefixLength]
+		}
+
+		m[ns] = struct{}{}
+
+		if len(m) != i+1 {
+			return false
+		}
+	}
+
+	return true
+}
diff --git a/router/router_test.go b/router/router_test.go
index a86d0f72..65fe299e 100644
--- a/router/router_test.go
+++ b/router/router_test.go
@@ -180,5 +180,43 @@ func TestLoadDefaultAliasesNoInfo(t *testing.T) {
 
 	assert.JSONEq(t, string(expectedJSON), string(aliasJSON))
 	assert.Equal(t, expectedAliases, defAliases)
+}
+
+func TestValidateDefaultAliases(t *testing.T) {
+	var testCases = []struct {
+		description   string
+		givenAliases  map[string]string
+		expectedError string
+	}{
+		{
+			description:   "None",
+			givenAliases:  map[string]string{},
+			expectedError: "",
+		},
+		{
+			description:   "Valid",
+			givenAliases:  map[string]string{"aAlias": "a"},
+			expectedError: "",
+		},
+		{
+			description:   "Invalid",
+			givenAliases:  map[string]string{"all": "a"},
+			expectedError: "default request alias errors (1 error):\n  1: alias all is a reserved bidder name and cannot be used\n",
+		},
+		{
+			description:   "Mixed",
+			givenAliases:  map[string]string{"aAlias": "a", "all": "a"},
+			expectedError: "default request alias errors (1 error):\n  1: alias all is a reserved bidder name and cannot be used\n",
+		},
+	}
+
+	for _, test := range testCases {
+		err := validateDefaultAliases(test.givenAliases)
 
+		if test.expectedError == "" {
+			assert.NoError(t, err, test.description)
+		} else {
+			assert.EqualError(t, err, test.expectedError, test.description)
+		}
+	}
 }
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 66a0dfc2..b6f10dc0 100755
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -103,6 +103,7 @@ func TestNewSyncerMap(t *testing.T) {
 		openrtb_ext.BidderAdot:         true,
 		openrtb_ext.BidderAdprime:      true,
 		openrtb_ext.BidderApplogy:      true,
+		openrtb_ext.BidderEpom:         true,
 		openrtb_ext.BidderDecenterAds:  true,
 		openrtb_ext.BidderInMobi:       true,
 		openrtb_ext.BidderKidoz:        true,