Repository: prebid/prebid-server
Pull request #3640 (2024-04-25 02:24:50)

Freewheel: add video dur and cat

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 7ecef76137890e417a80c0586d0764f202fb99c4 
#### freewheelssp 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8826102276_1714011935/freewheelssp.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:19:	MakeRequests	83.3%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:64:	MakeBids	95.2%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:109:	Builder		100.0%
total:										(statements)	90.2%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, d0f5acf4e91c28bf05916477d096d741c5870a67 
#### freewheelssp 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8826273087_1714013313/freewheelssp.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:19:	MakeRequests	83.3%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:64:	MakeBids	95.2%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:109:	Builder		100.0%
total:										(statements)	90.2%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 098262dcb099ee2dbf3edccaf7e93a2d9f3894c1 
#### freewheelssp 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8828877752_1714031283/freewheelssp.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:19:	MakeRequests	83.3%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:64:	MakeBids	95.2%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:109:	Builder		100.0%
total:										(statements)	90.2%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, c3853431abadc34371f44f0c8a7efa1af532a591 
#### freewheelssp 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8833549280_1714051925/freewheelssp.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:19:	MakeRequests	83.3%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:64:	MakeBids	95.2%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:109:	Builder		100.0%
total:										(statements)	90.2%

 ``` 

github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, a26a59899a2e30c8d71003b358bc2577a2bac32c 
#### freewheelssp 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8876877957_1714385629/freewheelssp.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:19:	MakeRequests	83.3%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:64:	MakeBids	95.2%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:109:	Builder		100.0%
total:										(statements)	90.2%

 ``` 

gargcreation1992: @mwang-sticky PR checks are failing because of - `expected.ImpIDs must contain at least one imp ID`
github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 37db0dfcc84bc5ec6af7f019b504e0149d7b5078 
#### freewheelssp 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8881969436_1714407690/freewheelssp.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:19:	MakeRequests	83.3%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:64:	MakeBids	95.2%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:109:	Builder		100.0%
total:										(statements)	90.2%

 ``` 

mwang-sticky: > @mwang-sticky PR checks are failing because of - `expected.ImpIDs must contain at least one imp ID`

https://github.com/prebid/prebid-server/commit/30082c04295370d46d57db4526c80be8f52806f2#diff-d83f728c5918299ec6f5a8d22447adb9fefac2726ab505dea12ba609dd79e068 ?
github-actions[bot]: ## Code coverage summary 
Note: 
- Prebid team doesn't anticipate tests covering code paths that might result in marshal and unmarshal errors 
- Coverage summary encompasses all commits leading up to the latest one, 9924f995deb80b837658d8d3a6a78c72d8456b79 
#### freewheelssp 
Refer [here](https://htmlpreview.github.io/?https://github.com/prebid/prebid-server/coverage-preview/.github/preview/8890221230_1714458108/freewheelssp.html) for heat map coverage report 
``` 
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:19:	MakeRequests	83.3%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:64:	MakeBids	95.2%
github.com/prebid/prebid-server/v2/adapters/freewheelssp/freewheelssp.go:109:	Builder		100.0%
total:										(statements)	90.2%

 ``` 


Patch:
diff --git a/adapters/alkimi/alkimi.go b/adapters/alkimi/alkimi.go
index 9b49c3dd..88703073 100644
--- a/adapters/alkimi/alkimi.go
+++ b/adapters/alkimi/alkimi.go
@@ -54,8 +54,7 @@ func (adapter *adapter) MakeRequests(request *openrtb2.BidRequest, req *adapters
 	if err != nil {
 		errs = append(errs, err)
 	} else {
-		reqBidder := buildBidderRequest(adapter, encoded)
-		reqBidder.ImpIDs = openrtb_ext.GetImpIDs(reqCopy.Imp)
+		reqBidder := buildBidderRequest(adapter, encoded, openrtb_ext.GetImpIDs(reqCopy.Imp))
 		reqsBidder = append(reqsBidder, reqBidder)
 	}
 	return
@@ -107,7 +106,7 @@ func updateImps(bidRequest openrtb2.BidRequest) ([]openrtb2.Imp, []error) {
 	return updatedImps, errs
 }
 
-func buildBidderRequest(adapter *adapter, encoded []byte) *adapters.RequestData {
+func buildBidderRequest(adapter *adapter, encoded []byte, impIDs []string) *adapters.RequestData {
 	headers := http.Header{}
 	headers.Add("Content-Type", "application/json;charset=utf-8")
 	headers.Add("Accept", "application/json")
@@ -117,6 +116,7 @@ func buildBidderRequest(adapter *adapter, encoded []byte) *adapters.RequestData
 		Uri:     adapter.endpoint,
 		Body:    encoded,
 		Headers: headers,
+		ImpIDs:  impIDs,
 	}
 	return reqBidder
 }
diff --git a/adapters/flipp/flipp.go b/adapters/flipp/flipp.go
index f0c32a77..55a3ce7b 100644
--- a/adapters/flipp/flipp.go
+++ b/adapters/flipp/flipp.go
@@ -3,6 +3,7 @@ package flipp
 import (
 	"encoding/base64"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"net/http"
 	"net/url"
@@ -25,8 +26,6 @@ const (
 	defaultCurrency = "USD"
 )
 
-var uuidGenerator uuidutil.UUIDGenerator
-
 var (
 	count    int64 = 1
 	adTypes        = []int64{4309, 641}
@@ -34,14 +33,19 @@ var (
 )
 
 type adapter struct {
-	endpoint string
+	endpoint      string
+	uuidGenerator uuidutil.UUIDGenerator
 }
 
+var (
+	errRequestEmpty = errors.New("adapterRequest is empty")
+)
+
 // Builder builds a new instance of the Flipp adapter for the given bidder with the given config.
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
-	uuidGenerator = uuidutil.UUIDRandomGenerator{}
 	bidder := &adapter{
-		endpoint: config.Endpoint,
+		endpoint:      config.Endpoint,
+		uuidGenerator: uuidutil.UUIDRandomGenerator{},
 	}
 	return bidder, nil
 }
@@ -59,12 +63,12 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E
 		adapterRequests = append(adapterRequests, adapterReq)
 	}
 	if len(adapterRequests) == 0 {
-		return nil, append(errors, fmt.Errorf("adapterRequest is empty"))
+		return nil, append(errors, errRequestEmpty)
 	}
 	return adapterRequests, errors
 }
 
-func (a *adapter) makeRequest(request *openrtb2.BidRequest, campaignRequestBody CampaignRequestBody) (*adapters.RequestData, error) {
+func (a *adapter) makeRequest(request *openrtb2.BidRequest, campaignRequestBody CampaignRequestBody, impID string) (*adapters.RequestData, error) {
 	campaignRequestBodyJSON, err := json.Marshal(campaignRequestBody)
 	if err != nil {
 		return nil, err
@@ -80,6 +84,7 @@ func (a *adapter) makeRequest(request *openrtb2.BidRequest, campaignRequestBody
 		Uri:     a.endpoint,
 		Body:    campaignRequestBodyJSON,
 		Headers: headers,
+		ImpIDs:  []string{impID},
 	}, err
 }
 
@@ -132,8 +137,7 @@ func (a *adapter) processImp(request *openrtb2.BidRequest, imp openrtb2.Imp) (*a
 	} else if flippExtParams.UserKey != "" && paramsUserKeyPermitted(request) {
 		userKey = flippExtParams.UserKey
 	} else {
-
-		uid, err := uuidGenerator.Generate()
+		uid, err := a.uuidGenerator.Generate()
 		if err != nil {
 			return nil, fmt.Errorf("unable to generate user uuid. %v", err)
 		}
@@ -152,8 +156,7 @@ func (a *adapter) processImp(request *openrtb2.BidRequest, imp openrtb2.Imp) (*a
 		},
 	}
 
-	adapterReq, err := a.makeRequest(request, campaignRequestBody)
-	adapterReq.ImpIDs = []string{imp.ID}
+	adapterReq, err := a.makeRequest(request, campaignRequestBody, imp.ID)
 	if err != nil {
 		return nil, fmt.Errorf("make request failed with err %v", err)
 	}
diff --git a/adapters/freewheelssp/freewheelssp.go b/adapters/freewheelssp/freewheelssp.go
index b11cf7c3..df6f756b 100644
--- a/adapters/freewheelssp/freewheelssp.go
+++ b/adapters/freewheelssp/freewheelssp.go
@@ -87,11 +87,20 @@ func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest
 
 	for _, seatBid := range bidResp.SeatBid {
 		for i := range seatBid.Bid {
-			b := &adapters.TypedBid{
-				Bid:     &seatBid.Bid[i],
-				BidType: bidType,
+			bid := seatBid.Bid[i]
+			bidVideo := openrtb_ext.ExtBidPrebidVideo{}
+			if len(bid.Cat) > 0 {
+				bidVideo.PrimaryCategory = bid.Cat[0]
 			}
-			bidResponse.Bids = append(bidResponse.Bids, b)
+			if bid.Dur > 0 {
+				bidVideo.Duration = int(bid.Dur)
+			}
+			adTypeBid := &adapters.TypedBid{
+				Bid:      &bid,
+				BidType:  bidType,
+				BidVideo: &bidVideo,
+			}
+			bidResponse.Bids = append(bidResponse.Bids, adTypeBid)
 		}
 	}
 	return bidResponse, nil
diff --git a/adapters/trustedstack/trustedstack.go b/adapters/trustedstack/trustedstack.go
new file mode 100644
index 00000000..f79f43ff
--- /dev/null
+++ b/adapters/trustedstack/trustedstack.go
@@ -0,0 +1,108 @@
+package trustedstack
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/url"
+
+	"github.com/prebid/openrtb/v20/openrtb2"
+	"github.com/prebid/prebid-server/v2/adapters"
+	"github.com/prebid/prebid-server/v2/config"
+	"github.com/prebid/prebid-server/v2/openrtb_ext"
+)
+
+type adapter struct {
+	endpoint string
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+
+	reqJson, err := json.Marshal(request)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+
+	return []*adapters.RequestData{{
+		Method:  "POST",
+		Uri:     a.endpoint,
+		Body:    reqJson,
+		Headers: headers,
+		ImpIDs:  openrtb_ext.GetImpIDs(request.Imp),
+	}}, nil
+}
+
+func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	var errs []error
+
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if err := adapters.CheckResponseStatusCodeForErrors(response); err != nil {
+		return nil, []error{err}
+	}
+
+	var bidResp openrtb2.BidResponse
+
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+
+	bidResponse := adapters.NewBidderResponse()
+
+	for _, sb := range bidResp.SeatBid {
+		for i := range sb.Bid {
+			bidType, err := getBidMediaTypeFromMtype(&sb.Bid[i])
+			if err != nil {
+				errs = append(errs, err)
+			} else {
+				b := &adapters.TypedBid{
+					Bid:     &sb.Bid[i],
+					BidType: bidType,
+				}
+				bidResponse.Bids = append(bidResponse.Bids, b)
+			}
+		}
+	}
+	return bidResponse, errs
+}
+
+// Builder builds a new instance of the Trustedstack adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	url := buildEndpoint(config.Endpoint, server.ExternalUrl)
+	return &adapter{
+		endpoint: url,
+	}, nil
+}
+
+func getBidMediaTypeFromMtype(bid *openrtb2.Bid) (openrtb_ext.BidType, error) {
+	switch bid.MType {
+	case openrtb2.MarkupBanner:
+		return openrtb_ext.BidTypeBanner, nil
+	case openrtb2.MarkupVideo:
+		return openrtb_ext.BidTypeVideo, nil
+	case openrtb2.MarkupNative:
+		return openrtb_ext.BidTypeNative, nil
+	default:
+		return "", fmt.Errorf("unable to fetch mediaType for imp: %s", bid.ImpID)
+	}
+}
+
+func buildEndpoint(trustedstackUrl, hostUrl string) string {
+
+	if len(hostUrl) == 0 {
+		return trustedstackUrl
+	}
+	urlObject, err := url.Parse(trustedstackUrl)
+	if err != nil {
+		return trustedstackUrl
+	}
+	values := urlObject.Query()
+	values.Add("src", hostUrl)
+	urlObject.RawQuery = values.Encode()
+	return urlObject.String()
+}
diff --git a/config/config.go b/config/config.go
index d2f21039..d4ef73a6 100644
--- a/config/config.go
+++ b/config/config.go
@@ -54,6 +54,8 @@ type Configuration struct {
 	// Note that StoredVideo refers to stored video requests, and has nothing to do with caching video creatives.
 	StoredVideo     StoredRequests `mapstructure:"stored_video_req"`
 	StoredResponses StoredRequests `mapstructure:"stored_responses"`
+	// StoredRequestsTimeout defines the number of milliseconds before a timeout occurs with stored requests fetch
+	StoredRequestsTimeout int `mapstructure:"stored_requests_timeout_ms"`
 
 	MaxRequestSize       int64             `mapstructure:"max_request_size"`
 	Analytics            Analytics         `mapstructure:"analytics"`
@@ -132,6 +134,9 @@ func (cfg *Configuration) validate(v *viper.Viper) []error {
 	var errs []error
 	errs = cfg.AuctionTimeouts.validate(errs)
 	errs = cfg.StoredRequests.validate(errs)
+	if cfg.StoredRequestsTimeout <= 0 {
+		errs = append(errs, fmt.Errorf("cfg.stored_requests_timeout_ms must be > 0. Got %d", cfg.StoredRequestsTimeout))
+	}
 	errs = cfg.StoredRequestsAMP.validate(errs)
 	errs = cfg.Accounts.validate(errs)
 	errs = cfg.CategoryMapping.validate(errs)
@@ -964,6 +969,7 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) {
 	v.SetDefault("category_mapping.filesystem.enabled", true)
 	v.SetDefault("category_mapping.filesystem.directorypath", "./static/category-mapping")
 	v.SetDefault("category_mapping.http.endpoint", "")
+	v.SetDefault("stored_requests_timeout_ms", 50)
 	v.SetDefault("stored_requests.database.connection.driver", "")
 	v.SetDefault("stored_requests.database.connection.dbname", "")
 	v.SetDefault("stored_requests.database.connection.host", "")
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index 4a6574e8..9895f447 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -534,7 +534,7 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req
 		return nil, nil, nil, nil, []error{err}
 	}
 
-	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(storedRequestTimeoutMillis)*time.Millisecond)
+	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(deps.cfg.StoredRequestsTimeout)*time.Millisecond)
 	defer cancel()
 
 	storedRequests, _, errs := deps.storedReqFetcher.FetchRequests(ctx, []string{ampParams.StoredRequestID}, nil)
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 4f014b96..28b2aa3d 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -58,7 +58,6 @@ import (
 	"github.com/prebid/prebid-server/v2/version"
 )
 
-const storedRequestTimeoutMillis = 50
 const ampChannel = "amp"
 const appChannel = "app"
 const secCookieDeprecation = "Sec-Cookie-Deprecation"
@@ -469,7 +468,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric
 		return
 	}
 
-	timeout := parseTimeout(requestJson, time.Duration(storedRequestTimeoutMillis)*time.Millisecond)
+	timeout := parseTimeout(requestJson, time.Duration(deps.cfg.StoredRequestsTimeout)*time.Millisecond)
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index f74a4a22..451e1f90 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -503,7 +503,7 @@ func createImpressionTemplate(imp openrtb2.Imp, video *openrtb2.Video) openrtb2.
 }
 
 func (deps *endpointDeps) loadStoredImp(storedImpId string) (openrtb2.Imp, []error) {
-	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(storedRequestTimeoutMillis)*time.Millisecond)
+	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(deps.cfg.StoredRequestsTimeout)*time.Millisecond)
 	defer cancel()
 
 	impr := openrtb2.Imp{}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 9f96cdbf..486bae21 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -181,6 +181,7 @@ import (
 	"github.com/prebid/prebid-server/v2/adapters/trafficgate"
 	"github.com/prebid/prebid-server/v2/adapters/triplelift"
 	"github.com/prebid/prebid-server/v2/adapters/triplelift_native"
+	"github.com/prebid/prebid-server/v2/adapters/trustedstack"
 	"github.com/prebid/prebid-server/v2/adapters/ucfunnel"
 	"github.com/prebid/prebid-server/v2/adapters/undertone"
 	"github.com/prebid/prebid-server/v2/adapters/unicorn"
@@ -391,6 +392,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderTrafficGate:       trafficgate.Builder,
 		openrtb_ext.BidderTriplelift:        triplelift.Builder,
 		openrtb_ext.BidderTripleliftNative:  triplelift_native.Builder,
+		openrtb_ext.BidderTrustedstack:      trustedstack.Builder,
 		openrtb_ext.BidderUcfunnel:          ucfunnel.Builder,
 		openrtb_ext.BidderUndertone:         undertone.Builder,
 		openrtb_ext.BidderUnicorn:           unicorn.Builder,
diff --git a/exchange/utils.go b/exchange/utils.go
index d5e94316..e64ac6aa 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -11,7 +11,6 @@ import (
 
 	"github.com/prebid/prebid-server/v2/ortb"
 
-	"github.com/buger/jsonparser"
 	"github.com/prebid/go-gdpr/vendorconsent"
 	gpplib "github.com/prebid/go-gpp"
 	gppConstants "github.com/prebid/go-gpp/constants"
@@ -32,6 +31,8 @@ import (
 	"github.com/prebid/prebid-server/v2/util/ptrutil"
 )
 
+var errInvalidRequestExt = errors.New("request.ext is invalid")
+
 var channelTypeMap = map[metrics.RequestType]config.ChannelType{
 	metrics.ReqTypeAMP:       config.ChannelAMP,
 	metrics.ReqTypeORTB2App:  config.ChannelApp,
@@ -64,7 +65,8 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 	bidAdjustmentFactors map[string]float64,
 ) (allowedBidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) {
 	req := auctionReq.BidRequestWrapper
-	aliases, errs := parseAliases(req.BidRequest)
+
+	requestAliases, requestAliasesGVLIDs, errs := getRequestAliases(req)
 	if len(errs) > 0 {
 		return
 	}
@@ -79,19 +81,14 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 		return
 	}
 
-	aliasesGVLIDs, errs := parseAliasesGVLIDs(req.BidRequest)
-	if len(errs) > 0 {
-		return
-	}
-
 	var allBidderRequests []BidderRequest
 	var allBidderRequestErrs []error
-	allBidderRequests, allBidderRequestErrs = getAuctionBidderRequests(auctionReq, requestExt, rs.bidderToSyncerKey, impsByBidder, aliases, rs.hostSChainNode)
+	allBidderRequests, allBidderRequestErrs = getAuctionBidderRequests(auctionReq, requestExt, rs.bidderToSyncerKey, impsByBidder, requestAliases, rs.hostSChainNode)
 	if allBidderRequestErrs != nil {
 		errs = append(errs, allBidderRequestErrs...)
 	}
 
-	bidderNameToBidderReq := buildBidResponseRequest(req.BidRequest, bidderImpWithBidResp, aliases, auctionReq.BidderImpReplaceImpID)
+	bidderNameToBidderReq := buildBidResponseRequest(req.BidRequest, bidderImpWithBidResp, requestAliases, auctionReq.BidderImpReplaceImpID)
 	//this function should be executed after getAuctionBidderRequests
 	allBidderRequests = mergeBidderRequests(allBidderRequests, bidderNameToBidderReq)
 
@@ -114,7 +111,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 		errs = append(errs, err)
 	}
 
-	ccpaEnforcer, err := extractCCPA(req.BidRequest, rs.privacyConfig, &auctionReq.Account, aliases, channelTypeMap[auctionReq.LegacyLabels.RType], gpp)
+	ccpaEnforcer, err := extractCCPA(req.BidRequest, rs.privacyConfig, &auctionReq.Account, requestAliases, channelTypeMap[auctionReq.LegacyLabels.RType], gpp)
 	if err != nil {
 		errs = append(errs, err)
 	}
@@ -141,7 +138,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 		}
 
 		gdprRequestInfo := gdpr.RequestInfo{
-			AliasGVLIDs: aliasesGVLIDs,
+			AliasGVLIDs: requestAliasesGVLIDs,
 			Consent:     consent,
 			GDPRSignal:  gdprSignal,
 			PublisherID: auctionReq.LegacyLabels.PubID,
@@ -264,14 +261,14 @@ func ccpaEnabled(account *config.Account, privacyConfig config.Privacy, requestT
 	return privacyConfig.CCPA.Enforce
 }
 
-func extractCCPA(orig *openrtb2.BidRequest, privacyConfig config.Privacy, account *config.Account, aliases map[string]string, requestType config.ChannelType, gpp gpplib.GppContainer) (privacy.PolicyEnforcer, error) {
+func extractCCPA(orig *openrtb2.BidRequest, privacyConfig config.Privacy, account *config.Account, requestAliases map[string]string, requestType config.ChannelType, gpp gpplib.GppContainer) (privacy.PolicyEnforcer, error) {
 	// Quick extra wrapper until RequestWrapper makes its way into CleanRequests
 	ccpaPolicy, err := ccpa.ReadFromRequestWrapper(&openrtb_ext.RequestWrapper{BidRequest: orig}, gpp)
 	if err != nil {
 		return privacy.NilPolicyEnforcer{}, err
 	}
 
-	validBidders := GetValidBidders(aliases)
+	validBidders := GetValidBidders(requestAliases)
 	ccpaParsedPolicy, err := ccpaPolicy.Parse(validBidders)
 	if err != nil {
 		return privacy.NilPolicyEnforcer{}, err
@@ -321,7 +318,7 @@ func getAuctionBidderRequests(auctionRequest AuctionRequest,
 	requestExt *openrtb_ext.ExtRequest,
 	bidderToSyncerKey map[string]string,
 	impsByBidder map[string][]openrtb2.Imp,
-	aliases map[string]string,
+	requestAliases map[string]string,
 	hostSChainNode *openrtb2.SupplyChainNode) ([]BidderRequest, []error) {
 
 	bidderRequests := make([]BidderRequest, 0, len(impsByBidder))
@@ -349,7 +346,7 @@ func getAuctionBidderRequests(auctionRequest AuctionRequest,
 
 	var errs []error
 	for bidder, imps := range impsByBidder {
-		coreBidder, isRequestAlias := resolveBidder(bidder, aliases)
+		coreBidder, isRequestAlias := resolveBidder(bidder, requestAliases)
 
 		reqCopy := *req.BidRequest
 		reqCopy.Imp = imps
@@ -795,36 +792,23 @@ func resolveBidder(bidder string, requestAliases map[string]string) (openrtb_ext
 	return normalisedBidderName, false
 }
 
-// parseAliases parses the aliases from the BidRequest
-func parseAliases(orig *openrtb2.BidRequest) (map[string]string, []error) {
-	var aliases map[string]string
-	if value, dataType, _, err := jsonparser.Get(orig.Ext, openrtb_ext.PrebidExtKey, "aliases"); dataType == jsonparser.Object && err == nil {
-		if err := jsonutil.Unmarshal(value, &aliases); err != nil {
-			return nil, []error{err}
-		}
-	} else if dataType != jsonparser.NotExist && err != jsonparser.KeyPathNotFoundError {
-		return nil, []error{err}
+func getRequestAliases(req *openrtb_ext.RequestWrapper) (map[string]string, map[string]uint16, []error) {
+	reqExt, err := req.GetRequestExt()
+	if err != nil {
+		return nil, nil, []error{errInvalidRequestExt}
 	}
-	return aliases, nil
-}
 
-// parseAliasesGVLIDs parses the Bidder Alias GVLIDs from the BidRequest
-func parseAliasesGVLIDs(orig *openrtb2.BidRequest) (map[string]uint16, []error) {
-	var aliasesGVLIDs map[string]uint16
-	if value, dataType, _, err := jsonparser.Get(orig.Ext, openrtb_ext.PrebidExtKey, "aliasgvlids"); dataType == jsonparser.Object && err == nil {
-		if err := jsonutil.Unmarshal(value, &aliasesGVLIDs); err != nil {
-			return nil, []error{err}
-		}
-	} else if dataType != jsonparser.NotExist && err != jsonparser.KeyPathNotFoundError {
-		return nil, []error{err}
+	if prebid := reqExt.GetPrebid(); prebid != nil {
+		return prebid.Aliases, prebid.AliasGVLIDs, nil
 	}
-	return aliasesGVLIDs, nil
+
+	return nil, nil, nil
 }
 
-func GetValidBidders(aliases map[string]string) map[string]struct{} {
+func GetValidBidders(requestAliases map[string]string) map[string]struct{} {
 	validBidders := openrtb_ext.BuildBidderNameHashSet()
 
-	for k := range aliases {
+	for k := range requestAliases {
 		validBidders[k] = struct{}{}
 	}
 
@@ -964,13 +948,13 @@ func applyFPD(fpd map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyD
 
 func buildBidResponseRequest(req *openrtb2.BidRequest,
 	bidderImpResponses stored_responses.BidderImpsWithBidResponses,
-	aliases map[string]string,
+	requestAliases map[string]string,
 	bidderImpReplaceImpID stored_responses.BidderImpReplaceImpID) map[openrtb_ext.BidderName]BidderRequest {
 
 	bidderToBidderResponse := make(map[openrtb_ext.BidderName]BidderRequest)
 
 	for bidderName, impResps := range bidderImpResponses {
-		resolvedBidder, isRequestAlias := resolveBidder(string(bidderName), aliases)
+		resolvedBidder, isRequestAlias := resolveBidder(string(bidderName), requestAliases)
 		bidderToBidderResponse[bidderName] = BidderRequest{
 			BidRequest:            req,
 			BidderCoreName:        resolvedBidder,
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 93c6a49e..7a5b0a5c 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -199,6 +199,7 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderTrafficGate,
 	BidderTriplelift,
 	BidderTripleliftNative,
+	BidderTrustedstack,
 	BidderUcfunnel,
 	BidderUndertone,
 	BidderUnicorn,
@@ -485,6 +486,7 @@ const (
 	BidderTrafficGate       BidderName = "trafficgate"
 	BidderTriplelift        BidderName = "triplelift"
 	BidderTripleliftNative  BidderName = "triplelift_native"
+	BidderTrustedstack      BidderName = "trustedstack"
 	BidderUcfunnel          BidderName = "ucfunnel"
 	BidderUndertone         BidderName = "undertone"
 	BidderUnicorn           BidderName = "unicorn"
diff --git a/openrtb_ext/imp_trustedstack.go b/openrtb_ext/imp_trustedstack.go
new file mode 100644
index 00000000..22914929
--- /dev/null
+++ b/openrtb_ext/imp_trustedstack.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ExtImpTrustedstack struct {
+	Cid  string `json:"cid"`
+	Crid string `json:"crid"`
+}
diff --git a/static/bidder-info/freewheelssp.yaml b/static/bidder-info/freewheelssp.yaml
index 8c9286cb..0c0a11ed 100644
--- a/static/bidder-info/freewheelssp.yaml
+++ b/static/bidder-info/freewheelssp.yaml
@@ -13,4 +13,4 @@ capabilities:
 userSync:
   iframe:
     url: "https://ads.stickyadstv.com/pbs-user-sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&r={{.RedirectURL}}"
-    userMacro: "{viewerid}"
\ No newline at end of file
+    userMacro: "{viewerid}"
diff --git a/static/bidder-info/ix.yaml b/static/bidder-info/ix.yaml
index 4ab520ad..398865b9 100644
--- a/static/bidder-info/ix.yaml
+++ b/static/bidder-info/ix.yaml
@@ -23,4 +23,5 @@ userSync:
     url: "https://ssum-sec.casalemedia.com/usermatch?s=184674&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gppsid={{.GPPSID}}&cb={{.RedirectURL}}"
     # ix appends the user id to end of the redirect url and does not utilize a macro
 openrtb:
+  version: 2.6
   gpp-supported: true
diff --git a/static/bidder-info/mobfoxpb.yaml b/static/bidder-info/mobfoxpb.yaml
index 0a1e78ec..cfd548b5 100644
--- a/static/bidder-info/mobfoxpb.yaml
+++ b/static/bidder-info/mobfoxpb.yaml
@@ -1,6 +1,6 @@
 endpoint: "http://bes.mobfox.com/?c=__route__&m=__method__&key=__key__"
 maintainer:
-  email: "platform@mobfox.com"
+  email: "support@mobfox.com"
 capabilities:
   app:
     mediaTypes:
diff --git a/static/bidder-info/pgamssp.yaml b/static/bidder-info/pgamssp.yaml
index a762d8f1..4cb4ad4e 100644
--- a/static/bidder-info/pgamssp.yaml
+++ b/static/bidder-info/pgamssp.yaml
@@ -15,5 +15,5 @@ capabilities:
     - native
 userSync:
   redirect:
-    url: "https://cs.pgammedia.com/pserver?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir={{.RedirectURL}}"
+    url: "https://cs.pgammedia.com/pserver?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redir={{.RedirectURL}}"
     userMacro: "[UID]"
diff --git a/static/bidder-info/trustedstack.yaml b/static/bidder-info/trustedstack.yaml
new file mode 100644
index 00000000..ffa48d26
--- /dev/null
+++ b/static/bidder-info/trustedstack.yaml
@@ -0,0 +1,24 @@
+endpoint: "https://prebid-adapter.trustedstack.com/rtb/pb/trustedstacks2s"
+extra_info: "https://trustedstack.golang.pbs.com"
+maintainer:
+  email: "product@trustedstack.com"
+gvlVendorID: 1288
+endpointCompression: gzip
+openrtb:
+  version: 2.6
+modifyingVastXmlAllowed: true
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
+userSync:
+  redirect:
+    url: https://hb.trustedstack.com/cksync?cs=1&type=pbs&ovsid=setstatuscode&bidder=trustedstack&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}
+    userMacro: "<vsid>"
\ No newline at end of file
diff --git a/static/bidder-params/trustedstack.json b/static/bidder-params/trustedstack.json
new file mode 100644
index 00000000..b52e7bf3
--- /dev/null
+++ b/static/bidder-params/trustedstack.json
@@ -0,0 +1,22 @@
+{
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "Trustedstack Adapter Params",
+    "description": "A schema which validates params accepted by the Trustedstack adapter",
+    "type": "object",
+    "properties": {
+      "cid": {
+        "type": "string",
+        "minLength": 1,
+        "description": "The customer id provided by Trustedstack."
+      },
+      "crid": {
+        "type": "string",
+        "minLength": 1,
+        "description": "The placement id provided by Trustedstack."
+      }
+    },
+    "required": [
+      "cid",
+      "crid"
+    ]
+}
\ No newline at end of file


Test patch:
diff --git a/adapters/flipp/flipp_test.go b/adapters/flipp/flipp_test.go
index 487e1802..10f2cfad 100644
--- a/adapters/flipp/flipp_test.go
+++ b/adapters/flipp/flipp_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/prebid/openrtb/v20/openrtb2"
+	"github.com/prebid/prebid-server/v2/adapters"
 	"github.com/prebid/prebid-server/v2/adapters/adapterstest"
 	"github.com/prebid/prebid-server/v2/config"
 	"github.com/prebid/prebid-server/v2/openrtb_ext"
@@ -23,7 +24,8 @@ func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderFlipp, config.Adapter{
 		Endpoint: "http://example.com/pserver"},
 		config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
-	uuidGenerator = TestUUIDGenerator{}
+
+	setFakeUUIDGenerator(bidder)
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
@@ -32,8 +34,12 @@ func TestJsonSamples(t *testing.T) {
 	adapterstest.RunJSONBidderTest(t, "flipptest", bidder)
 }
 
-func TestParamsUserKeyPermitted(t *testing.T) {
+func setFakeUUIDGenerator(bidder adapters.Bidder) {
+	bidderFlipp, _ := bidder.(*adapter)
+	bidderFlipp.uuidGenerator = TestUUIDGenerator{}
+}
 
+func TestParamsUserKeyPermitted(t *testing.T) {
 	t.Run("Coppa is in effect", func(t *testing.T) {
 		request := &openrtb2.BidRequest{
 			Regs: &openrtb2.Regs{
diff --git a/adapters/freewheelssp/freewheelssptest/exemplary/multi-imp.json b/adapters/freewheelssp/freewheelssptest/exemplary/multi-imp.json
index 3060341c..2562a812 100644
--- a/adapters/freewheelssp/freewheelssptest/exemplary/multi-imp.json
+++ b/adapters/freewheelssp/freewheelssptest/exemplary/multi-imp.json
@@ -89,7 +89,10 @@
                   "adid": "7857",
                   "adm": "<?xml version='1.0' encoding='UTF-8'?><VAST version='2.0'></VAST>",
                   "cid": "4001",
-                  "crid": "7857"
+                  "crid": "7857",
+                  "adomain":["freewheel.com"],
+                  "cat": ["IAB10"],
+                  "dur": 14
                 },
                 {
                   "id": "12346_freewheelssp-test_2",
@@ -98,7 +101,9 @@
                   "adid": "7933",
                   "adm": "<?xml version='1.0' encoding='UTF-8'?><VAST version='2.0'></VAST>",
                   "cid": "3476",
-                  "crid": "7933"
+                  "crid": "7933",
+                  "adomain":["freewheel.com"],
+                  "dur":10
                 }
               ],
               "seat": "freewheelsspTv"
@@ -123,9 +128,16 @@
             "adid": "7857",
             "adm": "<?xml version='1.0' encoding='UTF-8'?><VAST version='2.0'></VAST>",
             "cid": "4001",
-            "crid": "7857"
+            "crid": "7857",
+            "adomain":["freewheel.com"],
+            "cat": ["IAB10"],
+            "dur": 14
           },
-          "type": "video"
+          "type": "video",
+          "video" : {
+            "duration" : 14,
+            "primary_category": "IAB10"
+          }
         },
         {
           "bid":  {
@@ -135,9 +147,14 @@
             "adid": "7933",
             "adm": "<?xml version='1.0' encoding='UTF-8'?><VAST version='2.0'></VAST>",
             "cid": "3476",
-            "crid": "7933"
+            "crid": "7933",
+            "adomain":["freewheel.com"],
+            "dur":10
           },
-          "type": "video"
+          "type": "video",
+          "video" : {
+            "duration" : 10
+          }
         }
       ]
     }
diff --git a/adapters/trustedstack/params_test.go b/adapters/trustedstack/params_test.go
new file mode 100644
index 00000000..55812126
--- /dev/null
+++ b/adapters/trustedstack/params_test.go
@@ -0,0 +1,54 @@
+package trustedstack
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/v2/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/trustedstack.json
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderTrustedstack, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected trustedstack params: %s", validParam)
+		}
+	}
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderTrustedstack, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
+
+var validParams = []string{
+	`{"cid":"123", "crid":"1234"}`,
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{"cid":"", "crid":""}`,
+	`{"cid":"only cid is present"}`,
+	`{"crid":"only crid is present"}`,
+	`{"ccid":"123","ccrid":"123"}`,
+	`{"aid":123, "placementId":"123", "siteId":"321"}`,
+}
diff --git a/adapters/trustedstack/trustedstack_test.go b/adapters/trustedstack/trustedstack_test.go
new file mode 100644
index 00000000..c2e1742c
--- /dev/null
+++ b/adapters/trustedstack/trustedstack_test.go
@@ -0,0 +1,31 @@
+package trustedstack
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/prebid/prebid-server/v2/adapters/adapterstest"
+	"github.com/prebid/prebid-server/v2/config"
+	"github.com/prebid/prebid-server/v2/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderTrustedstack, config.Adapter{
+		Endpoint:         "https://example.trustedstack.com/rtb/prebid",
+		ExtraAdapterInfo: "http://localhost:8080/extrnal_url",
+	}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "trustedstacktest", bidder)
+}
+
+func TestEndpointTemplateMalformed(t *testing.T) {
+	_, buildErr := Builder(openrtb_ext.BidderTrustedstack, config.Adapter{
+		Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+	assert.Nil(t, buildErr)
+}
diff --git a/adapters/trustedstack/trustedstacktest/exemplary/multi-format.json b/adapters/trustedstack/trustedstacktest/exemplary/multi-format.json
new file mode 100644
index 00000000..21d0bb1e
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/exemplary/multi-format.json
@@ -0,0 +1,84 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 320,
+                            "h": 50
+                        }
+                    ]
+                },
+                "video": {
+                    "mimes": [
+                        "video/mp4"
+                    ],
+                    "protocols": [
+                        2,
+                        5
+                    ],
+                    "w": 320,
+                    "h": 480
+                },
+                "ext": {
+                    "bidder": {
+                        "cid": "8CUTSTCID",
+                        "crid": "999999999"
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 320,
+                                        "h": 50
+                                    }
+                                ]
+                            },
+                            "video": {
+                                "mimes": [
+                                    "video/mp4"
+                                ],
+                                "protocols": [
+                                    2,
+                                    5
+                                ],
+                                "w": 320,
+                                "h": 480
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "cid": "8CUTSTCID",
+                                    "crid": "999999999"
+                                }
+                            }
+                        }
+                    ]
+                },
+                "impIDs": ["1"]
+            },
+
+            "mockResponse": {
+                "status": 204,
+                "body": ""
+            }
+        }
+    ],
+
+    "expectedBidResponses": []
+}
diff --git a/adapters/trustedstack/trustedstacktest/exemplary/multi-imps.json b/adapters/trustedstack/trustedstacktest/exemplary/multi-imps.json
new file mode 100644
index 00000000..dc22967d
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/exemplary/multi-imps.json
@@ -0,0 +1,135 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 320,
+                            "h": 50
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "cid": "8CUTSTCID",
+                        "crid": "999999999"
+                    }
+                }
+            },
+            {
+                "id": "2",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 50
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "cid": "8CUTSESHA",
+                        "crid": "9999999789"
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 320,
+                                        "h": 50
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "cid": "8CUTSTCID",
+                                    "crid": "999999999"
+                                }
+                            }
+                        },
+                        {
+                            "id": "2",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 50
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "cid": "8CUTSESHA",
+                                    "crid": "9999999789"
+                                }
+                            }
+                        }
+                    ]
+                },
+                "impIDs": ["1","2"]
+            },
+
+            "mockResponse": {
+                "status": 200,
+                "body": {
+                    "id": "test-request-id",
+                    "seatbid": [
+                        {
+                            "seat": "trustedstack",
+                            "bid": [
+                                {
+                                    "id": "test-bid-id",
+                                    "impid": "1",
+                                    "price": 1.50,
+                                    "adm": "some-test-ad",
+                                    "crid": "test-crid",
+                                    "h": 50,
+                                    "w": 320,
+                                    "mtype": 1
+                                }
+                            ]
+                        }
+                    ],
+                    "cur": "USD"
+                }
+            }
+        }
+    ],
+
+    "expectedBidResponses": [
+        {
+            "currency": "USD",
+            "bids": [
+                {
+                    "bid": {
+                        "id": "test-bid-id",
+                        "impid": "1",
+                        "price": 1.50,
+                        "adm": "some-test-ad",
+                        "crid": "test-crid",
+                        "w": 320,
+                        "h": 50,
+                        "mtype": 1
+                    },
+                    "type": "banner"
+                }
+            ]
+        }
+    ]
+}
diff --git a/adapters/trustedstack/trustedstacktest/exemplary/no-bid.json b/adapters/trustedstack/trustedstacktest/exemplary/no-bid.json
new file mode 100644
index 00000000..195064c8
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/exemplary/no-bid.json
@@ -0,0 +1,61 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "cid": "8CUTSTCID",
+                        "crid": "999999999"
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "cid": "8CUTSTCID",
+                                    "crid": "999999999"
+                                }
+                            }
+                        }
+                    ]
+                },
+                "impIDs":["1"]
+            },
+            "mockResponse": {
+                "status": 204,
+                "body": {}
+            }
+        }
+    ],
+
+    "expectedBidResponses": []
+}
diff --git a/adapters/trustedstack/trustedstacktest/exemplary/optional-params.json b/adapters/trustedstack/trustedstacktest/exemplary/optional-params.json
new file mode 100644
index 00000000..105b5d29
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/exemplary/optional-params.json
@@ -0,0 +1,63 @@
+{
+    "mockBidRequest": {
+        "id": "test-request-id",
+        "imp": [
+            {
+                "id": "1",
+                "banner": {
+                    "format": [
+                        {
+                            "w": 300,
+                            "h": 250
+                        }
+                    ]
+                },
+                "ext": {
+                    "bidder": {
+                        "cid": "8CUTSTCID",
+                        "crid": "999999999",
+                        "tagid_src": "ext"
+                    }
+                }
+            }
+        ]
+    },
+
+    "httpCalls": [
+        {
+            "expectedRequest": {
+                "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+                "body": {
+                    "id": "test-request-id",
+                    "imp": [
+                        {
+                            "id": "1",
+                            "banner": {
+                                "format": [
+                                    {
+                                        "w": 300,
+                                        "h": 250
+                                    }
+                                ]
+                            },
+                            "ext": {
+                                "bidder": {
+                                    "cid": "8CUTSTCID",
+                                    "crid": "999999999",
+                                    "tagid_src": "ext"
+                                }
+                            }
+                        }
+                    ]
+                },
+                "impIDs":["1"]
+            },
+            "mockResponse": {
+                "status": 204,
+                "body": {}
+            }
+        }
+    ],
+
+    "expectedBidResponses": []
+}
diff --git a/adapters/trustedstack/trustedstacktest/exemplary/simple-banner.json b/adapters/trustedstack/trustedstacktest/exemplary/simple-banner.json
new file mode 100644
index 00000000..c0fb7519
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/exemplary/simple-banner.json
@@ -0,0 +1,101 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "1",
+        "banner": {
+          "format": [
+            {
+              "w": 320,
+              "h": 50
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "cid": "8CUTSTCID",
+            "crid": "999999999"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "1",
+              "banner": {
+                "format": [
+                  {
+                    "w": 320,
+                    "h": 50
+                  }
+                ]
+              },
+              "ext": {
+                "bidder": {
+                  "cid": "8CUTSTCID",
+                  "crid": "999999999"
+                }
+              }
+            }
+          ]
+        },
+        "impIDs":["1"]
+      },
+
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "trustedstack",
+              "bid": [
+                {
+                  "id": "test-bid-id",
+                  "impid": "1",
+                  "price": 1.50,
+                  "adm": "some-test-ad",
+                  "crid": "test-crid",
+                  "h": 50,
+                  "w": 320,
+                  "mtype": 1
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-bid-id",
+            "impid": "1",
+            "price": 1.50,
+            "adm": "some-test-ad",
+            "crid": "test-crid",
+            "w": 320,
+            "h": 50,
+            "mtype": 1
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/trustedstack/trustedstacktest/exemplary/simple-native.json b/adapters/trustedstack/trustedstacktest/exemplary/simple-native.json
new file mode 100644
index 00000000..7af9ce8e
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/exemplary/simple-native.json
@@ -0,0 +1,91 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "1",
+        "native": {
+          "request": "sample-native-request"
+        },
+        "ext": {
+          "bidder": {
+            "cid": "8CUTSTCID",
+            "crid": "999999999"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "1",
+              "native": {
+                "request": "sample-native-request"
+              },
+              "ext": {
+                "bidder": {
+                  "cid": "8CUTSTCID",
+                  "crid": "999999999"
+                }
+              }
+            }
+          ]
+        },
+        "impIDs":["1"]
+      },
+
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "trustedstack",
+              "bid": [
+                {
+                  "id": "test-bid-id",
+                  "impid": "1",
+                  "price": 1.50,
+                  "adm": "some-test-ad",
+                  "crid": "test-crid",
+                  "h": 50,
+                  "w": 320,
+                  "mtype": 4
+                }
+              ]
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-bid-id",
+            "impid": "1",
+            "price": 1.50,
+            "adm": "some-test-ad",
+            "crid": "test-crid",
+            "w": 320,
+            "h": 50,
+            "mtype": 4
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/trustedstack/trustedstacktest/exemplary/simple-video.json b/adapters/trustedstack/trustedstacktest/exemplary/simple-video.json
new file mode 100644
index 00000000..65357342
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/exemplary/simple-video.json
@@ -0,0 +1,104 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "1",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "protocols": [
+            2,
+            5
+          ],
+          "w": 320,
+          "h": 480
+        },
+        "ext": {
+          "bidder": {
+            "cid": "8CUTSTCID",
+            "crid": "999999999"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "1",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "protocols": [
+                  2,
+                  5
+                ],
+                "w": 320,
+                "h": 480
+              },
+              "ext": {
+                "bidder": {
+                  "cid": "8CUTSTCID",
+                  "crid": "999999999"
+                }
+              }
+            }
+          ]
+        },
+        "impIDs":["1"]
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "cur": "USD",
+          "seatbid": [
+            {
+              "seat": "trustedstack",
+              "bid": [
+                {
+                  "id": "test-bid-id",
+                  "impid": "1",
+                  "price": 2.50,
+                  "adm": "some-test-ad",
+                  "crid": "test-crid",
+                  "w": 320,
+                  "h": 480,
+                  "mtype": 2
+                }
+              ]
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "test-bid-id",
+            "impid": "1",
+            "price": 2.50,
+            "adm": "some-test-ad",
+            "crid": "test-crid",
+            "w": 320,
+            "h": 480,
+            "mtype": 2
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/trustedstack/trustedstacktest/supplemental/invalid-req-400-status-code-bad-request.json b/adapters/trustedstack/trustedstacktest/supplemental/invalid-req-400-status-code-bad-request.json
new file mode 100644
index 00000000..7c2cb7c6
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/supplemental/invalid-req-400-status-code-bad-request.json
@@ -0,0 +1,97 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "cid": "8CUTSTCID",
+            "crid": "999999999"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "w": 640,
+              "h": 480,
+              "minduration": 120,
+              "maxduration": 150
+            },
+            "ext": {
+              "bidder": {
+                "cid": "8CUTSTCID",
+                "crid": "999999999"
+              }
+            }
+          }
+        ],
+        "app":  {
+          "publisher": {
+            "id": "123456789"
+          },
+          "cat": [
+            "IAB22-1"
+          ],
+          "bundle": "com.app.awesome",
+          "name": "Awesome App",
+          "domain": "awesomeapp.com",
+          "id": "123456789"
+        },
+        "user": {
+          "buyeruid": "0000-000-000-0000"
+        },
+        "tmax": 1000
+      },
+      "impIDs": ["some-impression-id"]
+    },
+    "mockResponse": {
+      "status": 400
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trustedstack/trustedstacktest/supplemental/invalid-req-500-status-code-bad-request.json b/adapters/trustedstack/trustedstacktest/supplemental/invalid-req-500-status-code-bad-request.json
new file mode 100644
index 00000000..6d57a294
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/supplemental/invalid-req-500-status-code-bad-request.json
@@ -0,0 +1,98 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "0000-000-000-0000"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "cid": "8CUTSTCID",
+            "crid": "999999999"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "w": 640,
+              "h": 480,
+              "minduration": 120,
+              "maxduration": 150
+            },
+            "ext": {
+              "bidder": {
+                "cid": "8CUTSTCID",
+                "crid": "999999999"
+              }
+            }
+          }
+        ],
+        "app":  {
+          "publisher": {
+            "id": "123456789"
+          },
+          "cat": [
+            "IAB22-1"
+          ],
+          "bundle": "com.app.awesome",
+          "name": "Awesome App",
+          "domain": "awesomeapp.com",
+          "id": "123456789"
+        },
+        "user": {
+          "buyeruid": "0000-000-000-0000"
+        },
+        "tmax": 1000
+      },
+      "impIDs": ["some-impression-id"]
+    },
+    "mockResponse": {
+      "status": 500
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 500. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trustedstack/trustedstacktest/supplemental/ivalid-req-200-incorrect-response-mtype.json b/adapters/trustedstack/trustedstacktest/supplemental/ivalid-req-200-incorrect-response-mtype.json
new file mode 100644
index 00000000..0b1dfbb5
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/supplemental/ivalid-req-200-incorrect-response-mtype.json
@@ -0,0 +1,125 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "cid": "8CUTSTCID",
+            "crid": "999999999"
+          }
+        }
+      }
+    ],
+    "site": {
+      "domain": "www.example.com",
+      "page": "http://www.example.com",
+      "publisher": {
+        "domain": "example.com"
+      },
+      "ext": {
+        "amp": 0
+      }
+    },
+    "device": {
+      "ua": "userAgent",
+      "ip": "193.168.244.1"
+    },
+    "at": 1,
+    "tmax": 5000,
+    "cur": [
+      "USD"
+    ],
+    "regs": {
+      "gdpr": 0
+    }
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+      "body": {
+        "id": "test-request-id",
+        "imp": [
+          {
+            "id": "test-imp-id",
+            "banner": {
+              "w": 300,
+              "h": 250
+            },
+            "ext": {
+              "bidder": {
+                "cid": "8CUTSTCID",
+                "crid": "999999999"
+              }
+            }
+          }
+        ],
+        "site": {
+          "domain": "www.example.com",
+          "page": "http://www.example.com",
+          "publisher": {
+            "domain": "example.com"
+          },
+          "ext": {
+            "amp": 0
+          }
+        },
+        "device": {
+          "ua": "userAgent",
+          "ip": "193.168.244.1"
+        },
+        "at": 1,
+        "tmax": 5000,
+        "cur": [
+          "USD"
+        ],
+        "regs": {
+          "gdpr": 0
+        }
+      },
+      "impIDs": ["test-imp-id"]
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "tid",
+        "seatbid": [
+          {
+            "seat": "trustedstack",
+            "bid": [
+              {
+                "id": "randomid",
+                "impid": "test-imp-id",
+                "price": 0.500000,
+                "adid": "12345678",
+                "adm": "some-test-ad",
+                "cid": "987",
+                "crid": "12345678",
+                "h": 250,
+                "w": 300,
+                "mtype": 5
+              }
+            ]
+          }
+        ],
+        "bidid": "bid01"
+      }
+    }
+  }],
+
+  "expectedBidResponses": [{
+    "currency": "USD",
+    "bids": []
+  }],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "unable to fetch mediaType for imp: test-imp-id",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trustedstack/trustedstacktest/supplemental/valid-req-200-bid-response-from-trustedstack.json b/adapters/trustedstack/trustedstacktest/supplemental/valid-req-200-bid-response-from-trustedstack.json
new file mode 100644
index 00000000..a2388abe
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/supplemental/valid-req-200-bid-response-from-trustedstack.json
@@ -0,0 +1,133 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "cid": "8CUTSTCID",
+            "crid": "999999999"
+          }
+        }
+      }
+    ],
+    "site": {
+      "domain": "www.example.com",
+      "page": "http://www.example.com",
+      "publisher": {
+        "domain": "example.com"
+      },
+      "ext": {
+        "amp": 0
+      }
+    },
+    "device": {
+      "ua": "userAgent",
+      "ip": "193.168.244.1"
+    },
+    "at": 1,
+    "tmax": 5000,
+    "cur": [
+      "USD"
+    ],
+    "regs": {
+      "gdpr": 0
+    }
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+      "body": {
+        "id": "test-request-id",
+        "imp": [
+          {
+            "id": "test-imp-id",
+            "banner": {
+              "w": 300,
+              "h": 250
+            },
+            "ext": {
+              "bidder": {
+                "cid": "8CUTSTCID",
+                "crid": "999999999"
+              }
+            }
+          }
+        ],
+        "site": {
+          "domain": "www.example.com",
+          "page": "http://www.example.com",
+          "publisher": {
+            "domain": "example.com"
+          },
+          "ext": {
+            "amp": 0
+          }
+        },
+        "device": {
+          "ua": "userAgent",
+          "ip": "193.168.244.1"
+        },
+        "at": 1,
+        "tmax": 5000,
+        "cur": [
+          "USD"
+        ],
+        "regs": {
+          "gdpr": 0
+        }
+      },
+      "impIDs": ["test-imp-id"]
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "tid",
+        "seatbid": [
+          {
+            "seat": "trustedstack",
+            "bid": [
+              {
+                "id": "randomid",
+                "impid": "test-imp-id",
+                "price": 0.500000,
+                "adid": "12345678",
+                "adm": "some-test-ad",
+                "cid": "987",
+                "crid": "12345678",
+                "h": 250,
+                "w": 300,
+                "mtype": 1
+              }
+            ]
+          }
+        ],
+        "bidid": "bid01"
+      }
+    }
+  }],
+
+  "expectedBidResponses": [{
+    "currency": "USD",
+    "bids": [{
+      "bid": {
+        "id": "randomid",
+        "impid": "test-imp-id",
+        "price": 0.500000,
+        "adid": "12345678",
+        "adm": "some-test-ad",
+        "cid": "987",
+        "crid": "12345678",
+        "h": 250,
+        "w": 300,
+        "mtype": 1
+      },
+      "type": "banner"
+    }]
+  }]
+}
diff --git a/adapters/trustedstack/trustedstacktest/supplemental/valid-req-200-incorrect-response-format.json b/adapters/trustedstack/trustedstacktest/supplemental/valid-req-200-incorrect-response-format.json
new file mode 100644
index 00000000..c2cf9a06
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/supplemental/valid-req-200-incorrect-response-format.json
@@ -0,0 +1,121 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "cid": "8CUTSTCID",
+            "crid": "999999999"
+          }
+        }
+      }
+    ],
+    "site": {
+      "domain": "www.example.com",
+      "page": "http://www.example.com",
+      "publisher": {
+        "domain": "example.com"
+      },
+      "ext": {
+        "amp": 0
+      }
+    },
+    "device": {
+      "ua": "userAgent",
+      "ip": "193.168.244.1"
+    },
+    "at": 1,
+    "tmax": 5000,
+    "cur": [
+      "USD"
+    ],
+    "regs": {
+      "gdpr": 0
+    }
+  },
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+      "body": {
+        "id": "test-request-id",
+        "imp": [
+          {
+            "id": "test-imp-id",
+            "banner": {
+              "w": 300,
+              "h": 250
+            },
+            "ext": {
+              "bidder": {
+                "cid": "8CUTSTCID",
+                "crid": "999999999"
+              }
+            }
+          }
+        ],
+        "site": {
+          "domain": "www.example.com",
+          "page": "http://www.example.com",
+          "publisher": {
+            "domain": "example.com"
+          },
+          "ext": {
+            "amp": 0
+          }
+        },
+        "device": {
+          "ua": "userAgent",
+          "ip": "193.168.244.1"
+        },
+        "at": 1,
+        "tmax": 5000,
+        "cur": [
+          "USD"
+        ],
+        "regs": {
+          "gdpr": 0
+        }
+      },
+      "impIDs": ["test-imp-id"]
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "tid",
+        "seatbid": [
+          {
+            "seat": "trustedstack",
+            "bid": [
+              {
+                "id": "randomid",
+                "impid": "test-imp-id",
+                "price": "0.500000",
+                "adid": "12345678",
+                "adm": "some-test-ad",
+                "cid": "987",
+                "crid": "12345678",
+                "h": 250,
+                "w": 300
+              }
+            ]
+          }
+        ],
+        "bidid": "bid01"
+      }
+    }
+  }],
+
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "json: cannot unmarshal string into Go struct field Bid.seatbid.bid.price of type float64",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/trustedstack/trustedstacktest/supplemental/valid-req-204-response-from-trustedstack.json b/adapters/trustedstack/trustedstacktest/supplemental/valid-req-204-response-from-trustedstack.json
new file mode 100755
index 00000000..39e95277
--- /dev/null
+++ b/adapters/trustedstack/trustedstacktest/supplemental/valid-req-204-response-from-trustedstack.json
@@ -0,0 +1,67 @@
+{
+  "mockBidRequest": {
+    "app": {
+      "bundle": "com.example.app"
+    },
+    "id": "req-id",
+    "device": {
+      "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d",
+      "ip": "1.1.1.1",
+      "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36"
+    },
+    "imp": [
+      {
+        "ext": {
+          "bidder": {
+            "cid": "8CUTSTCID",
+            "crid": "999999999"
+          }
+        },
+        "banner": {
+          "w": 320,
+          "h": 50
+        },
+        "id": "imp-id"
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://example.trustedstack.com/rtb/prebid?src=http%3A%2F%2Fhosturl.com",
+        "body": {
+          "app": {
+            "bundle": "com.example.app"
+          },
+          "id": "req-id",
+          "device": {
+            "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d",
+            "ip": "1.1.1.1",
+            "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36"
+          },
+          "imp": [
+            {
+              "ext": {
+                "bidder": {
+                  "cid": "8CUTSTCID",
+                  "crid": "999999999"
+                }
+              },
+              "banner": {
+                "w": 320,
+                "h": 50
+              },
+              "id": "imp-id"
+            }
+          ]
+        },
+        "impIDs": ["imp-id"]
+      },
+      "mockResponse": {
+        "status": 204,
+        "body": {}
+      }
+    }
+  ],
+  "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/config/config_test.go b/config/config_test.go
index 4a553e41..414f2635 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -161,6 +161,7 @@ func TestDefaults(t *testing.T) {
 	cmpBools(t, "adapter_buyeruid_scrubbed", true, cfg.Metrics.Disabled.AdapterBuyerUIDScrubbed)
 	cmpBools(t, "adapter_gdpr_request_blocked", false, cfg.Metrics.Disabled.AdapterGDPRRequestBlocked)
 	cmpStrings(t, "certificates_file", "", cfg.PemCertsFile)
+	cmpInts(t, "stored_requests_timeout_ms", 50, cfg.StoredRequestsTimeout)
 	cmpBools(t, "stored_requests.filesystem.enabled", false, cfg.StoredRequests.Files.Enabled)
 	cmpStrings(t, "stored_requests.filesystem.directorypath", "./stored_requests/data/by_id", cfg.StoredRequests.Files.Path)
 	cmpBools(t, "auto_gen_source_tid", true, cfg.AutoGenSourceTID)
@@ -400,6 +401,7 @@ external_url: http://prebid-server.prebid.org/
 host: prebid-server.prebid.org
 port: 1234
 admin_port: 5678
+stored_requests_timeout_ms: 75
 compression:
     request:
         enable_gzip: true
@@ -612,6 +614,7 @@ func TestFullConfig(t *testing.T) {
 	cmpInts(t, "garbage_collector_threshold", 1, cfg.GarbageCollectorThreshold)
 	cmpInts(t, "auction_timeouts_ms.default", 50, int(cfg.AuctionTimeouts.Default))
 	cmpInts(t, "auction_timeouts_ms.max", 123, int(cfg.AuctionTimeouts.Max))
+	cmpInts(t, "stored_request_timeout_ms", 75, cfg.StoredRequestsTimeout)
 	cmpStrings(t, "cache.scheme", "http", cfg.CacheURL.Scheme)
 	cmpStrings(t, "cache.host", "prebidcache.net", cfg.CacheURL.Host)
 	cmpStrings(t, "cache.query", "uuid=%PBS_CACHE_UUID%", cfg.CacheURL.Query)
@@ -912,6 +915,7 @@ func TestValidateConfig(t *testing.T) {
 				Type: "none",
 			},
 		},
+		StoredRequestsTimeout: 50,
 		StoredVideo: StoredRequests{
 			Files: FileFetcherConfig{Enabled: true},
 			InMemoryCache: InMemoryCache{
@@ -1173,6 +1177,16 @@ func TestIsConfigInfoPresent(t *testing.T) {
 	}
 }
 
+func TestNegativeOrZeroStoredRequestsTimeout(t *testing.T) {
+	cfg, v := newDefaultConfig(t)
+
+	cfg.StoredRequestsTimeout = -1
+	assertOneError(t, cfg.validate(v), "cfg.stored_requests_timeout_ms must be > 0. Got -1")
+
+	cfg.StoredRequestsTimeout = 0
+	assertOneError(t, cfg.validate(v), "cfg.stored_requests_timeout_ms must be > 0. Got 0")
+}
+
 func TestNegativeRequestSize(t *testing.T) {
 	cfg, v := newDefaultConfig(t)
 	cfg.MaxRequestSize = -1
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 87c3936f..88b5b28b 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -2106,8 +2106,8 @@ func loadFile(filename string) (*exchangeSpec, error) {
 }
 
 func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
-	aliases, errs := parseAliases(&spec.IncomingRequest.OrtbRequest)
-	if len(errs) != 0 {
+	aliases, err := parseRequestAliases(spec.IncomingRequest.OrtbRequest)
+	if err != nil {
 		t.Fatalf("%s: Failed to parse aliases", filename)
 	}
 
@@ -5782,6 +5782,24 @@ func (m *mockBidder) MakeBids(internalRequest *openrtb2.BidRequest, externalRequ
 	return args.Get(0).(*adapters.BidderResponse), args.Get(1).([]error)
 }
 
+func parseRequestAliases(r openrtb2.BidRequest) (map[string]string, error) {
+	if len(r.Ext) == 0 {
+		return nil, nil
+	}
+
+	ext := struct {
+		Prebid struct {
+			Aliases map[string]string `json:"aliases"`
+		} `json:"prebid"`
+	}{}
+
+	if err := jsonutil.Unmarshal(r.Ext, &ext); err != nil {
+		return nil, err
+	}
+
+	return ext.Prebid.Aliases, nil
+}
+
 func getInfoFromImp(req *openrtb_ext.RequestWrapper) (json.RawMessage, string, error) {
 	bidRequest := req.BidRequest
 	imp := bidRequest.Imp[0]
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index eeef2e3a..69195736 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -24,6 +24,7 @@ import (
 	"github.com/prebid/prebid-server/v2/util/ptrutil"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/require"
 )
 
 const deviceUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"
@@ -3266,63 +3267,80 @@ func TestApplyFPD(t *testing.T) {
 	}
 }
 
-func Test_parseAliasesGVLIDs(t *testing.T) {
-	type args struct {
-		orig *openrtb2.BidRequest
-	}
+func TestGetRequestAliases(t *testing.T) {
 	tests := []struct {
-		name      string
-		args      args
-		want      map[string]uint16
-		wantError bool
+		name         string
+		givenRequest openrtb_ext.RequestWrapper
+		wantAliases  map[string]string
+		wantGVLIDs   map[string]uint16
+		wantError    string
 	}{
 		{
-			"AliasGVLID Parsed Correctly",
-			args{
-				orig: &openrtb2.BidRequest{
-					Ext: json.RawMessage(`{"prebid":{"aliases":{"somealiascode":"appnexus"}, "aliasgvlids":{"somealiascode":1}}}`),
+			name: "nil",
+			givenRequest: openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{},
+			},
+			wantAliases: nil,
+			wantGVLIDs:  nil,
+			wantError:   "",
+		},
+		{
+			name: "empty",
+			givenRequest: openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					Ext: json.RawMessage(`{}`),
 				},
 			},
-			map[string]uint16{"somealiascode": 1},
-			false,
+			wantAliases: nil,
+			wantGVLIDs:  nil,
+			wantError:   "",
 		},
 		{
-			"AliasGVLID parsing error",
-			args{
-				orig: &openrtb2.BidRequest{
-					Ext: json.RawMessage(`{"prebid":{"aliases":{"somealiascode":"appnexus"}, "aliasgvlids": {"somealiascode":"abc"}`),
+			name: "empty-prebid",
+			givenRequest: openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					Ext: json.RawMessage(`{"prebid":{}}`),
 				},
 			},
-			nil,
-			true,
+			wantAliases: nil,
+			wantGVLIDs:  nil,
+			wantError:   "",
 		},
 		{
-			"Invalid AliasGVLID",
-			args{
-				orig: &openrtb2.BidRequest{
-					Ext: json.RawMessage(`{"prebid":{"aliases":{"somealiascode":"appnexus"}, "aliasgvlids":"abc"}`),
+			name: "aliases-and-gvlids",
+			givenRequest: openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					Ext: json.RawMessage(`{"prebid":{"aliases":{"alias1":"bidder1"}, "aliasgvlids":{"alias1":1}}}`),
 				},
 			},
-			nil,
-			true,
+			wantAliases: map[string]string{"alias1": "bidder1"},
+			wantGVLIDs:  map[string]uint16{"alias1": 1},
+			wantError:   "",
 		},
 		{
-			"Missing AliasGVLID",
-			args{
-				orig: &openrtb2.BidRequest{
-					Ext: json.RawMessage(`{"prebid":{"aliases":{"somealiascode":"appnexus"}}`),
+			name: "malformed",
+			givenRequest: openrtb_ext.RequestWrapper{
+				BidRequest: &openrtb2.BidRequest{
+					Ext: json.RawMessage(`malformed`),
 				},
 			},
-			nil,
-			false,
+			wantAliases: nil,
+			wantGVLIDs:  nil,
+			wantError:   "request.ext is invalid",
 		},
 	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			got, err := parseAliasesGVLIDs(tt.args.orig)
-			assert.Equal(t, tt.want, got, "parseAliasesGVLIDs() got = %v, want %v", got, tt.want)
-			if !tt.wantError && err != nil {
-				t.Errorf("parseAliasesGVLIDs() expected error got nil")
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			gotAliases, gotGVLIDs, err := getRequestAliases(&test.givenRequest)
+
+			assert.Equal(t, test.wantAliases, gotAliases, "aliases")
+			assert.Equal(t, test.wantGVLIDs, gotGVLIDs, "gvlids")
+
+			if len(test.wantError) > 0 {
+				require.Len(t, err, 1, "error-len")
+				assert.EqualError(t, err[0], test.wantError, "error")
+			} else {
+				assert.Empty(t, err, "error")
 			}
 		})
 	}