diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 18ff30b..8a64d74 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -2,10 +2,10 @@ name: Test
 on:
   push:
     branches:
-      - main
+      - master
   pull_request:
     branches:
-      - main
+      - master
 jobs:
   go:
     runs-on: ubuntu-latest
diff --git a/.gitignore b/.gitignore
index 6aa8b3a..dc6f263 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
 *.makefile
+
+.idea
+.vscode
diff --git a/Makefile b/Makefile
index bdffdab..7e42be8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,10 @@
-default: test
+default: vet test
 
-.minimal.makefile:
-	curl -fsSL -o $@ https://gitlab.com/bsm/misc/raw/master/make/go/minimal.makefile
+test:
+	go test -a ./...
 
-include .minimal.makefile
+bench:
+	go test ./... -bench=. -run=NONE
+
+vet:
+	go vet ./...
diff --git a/README.md b/README.md
index 73cb6be..2485a67 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # OpenRTB
 
-[![Build Status](https://travis-ci.org/bsm/openrtb.svg?branch=master)](https://travis-ci.org/bsm/openrtb)
+[![Build Status](https://travis-ci.org/UnityTech/openrtb.svg?branch=master)](https://travis-ci.org/UnityTech/openrtb)
 
 OpenRTB structs and validations for Go.
 
@@ -13,7 +13,7 @@ Requires Go 1.8+ for proper `json.RawMessage` marshaling.
 To install, use `go get`:
 
 ```shell
-go get github.com/bsm/openrtb/v3
+go get github.com/UnityTech/openrtb/v3
 ```
 
 ## Usage
@@ -22,8 +22,11 @@ go get github.com/bsm/openrtb/v3
 package main
 
 import (
+  "encoding/json"
   "log"
-  "github.com/bsm/openrtb/v3"
+  "os"
+
+  "github.com/UnityTech/openrtb/v3"
 )
 
 func main() {
diff --git a/audio.go b/audio.go
index 1c67131..2ef6b8f 100644
--- a/audio.go
+++ b/audio.go
@@ -7,38 +7,38 @@ import (
 
 // Validation errors
 var (
-	ErrInvalidAudioNoMIMEs = errors.New("openrtb: audio has no mimes")
+	ErrInvalidAudioNoMimes = errors.New("openrtb: audio has no mimes")
 )
 
 // Audio object must be included directly in the impression object
 type Audio struct {
-	MIMEs          []string            `json:"mimes"`                 // Content MIME types supported.
-	MinDuration    int                 `json:"minduration,omitempty"` // Minimum video ad duration in seconds
-	MaxDuration    int                 `json:"maxduration,omitempty"` // Maximum video ad duration in seconds
-	Protocols      []Protocol          `json:"protocols,omitempty"`   // Video bid response protocols
-	StartDelay     StartDelay          `json:"startdelay,omitempty"`  // Indicates the start delay in seconds
-	Sequence       int                 `json:"sequence,omitempty"`    // Default: 1
-	BlockedAttrs   []CreativeAttribute `json:"battr,omitempty"`       // Blocked creative attributes
-	MaxExtended    int                 `json:"maxextended,omitempty"` // Maximum extended video ad duration
-	MinBitrate     int                 `json:"minbitrate,omitempty"`  // Minimum bit rate in Kbps
-	MaxBitrate     int                 `json:"maxbitrate,omitempty"`  // Maximum bit rate in Kbps
-	Delivery       []ContentDelivery   `json:"delivery,omitempty"`    // List of supported delivery methods
-	CompanionAds   []Banner            `json:"companionad,omitempty"`
-	APIs           []APIFramework      `json:"api,omitempty"`
-	CompanionTypes []CompanionType     `json:"companiontype,omitempty"`
-	MaxSequence    int                 `json:"maxseq,omitempty"`   // The maximumnumber of ads that canbe played in an ad pod.
-	Feed           FeedType            `json:"feed,omitempty"`     // Type of audio feed.
-	Stitched       int                 `json:"stitched,omitempty"` // Indicates if the ad is stitched with audio content or delivered independently
-	VolumeNorm     VolumeNorm          `json:"nvol,omitempty"`     // Volume normalization mode.
-	Ext            json.RawMessage     `json:"ext,omitempty"`
+	Mimes         []string  `json:"mimes"`                 // Content MIME types supported.
+	MinDuration   int       `json:"minduration,omitempty"` // Minimum video ad duration in seconds
+	MaxDuration   int       `json:"maxduration,omitempty"` // Maximum video ad duration in seconds
+	Protocols     []int     `json:"protocols,omitempty"`   // Video bid response protocols
+	StartDelay    int       `json:"startdelay,omitempty"`  // Indicates the start delay in seconds
+	Sequence      int       `json:"sequence,omitempty"`    // Default: 1
+	BAttr         []int     `json:"battr,omitempty"`       // Blocked creative attributes
+	MaxExtended   int       `json:"maxextended,omitempty"` // Maximum extended video ad duration
+	MinBitrate    int       `json:"minbitrate,omitempty"`  // Minimum bit rate in Kbps
+	MaxBitrate    int       `json:"maxbitrate,omitempty"`  // Maximum bit rate in Kbps
+	Delivery      []int     `json:"delivery,omitempty"`    // List of supported delivery methods
+	CompanionAd   []Banner  `json:"companionad,omitempty"`
+	API           []int     `json:"api,omitempty"`
+	CompanionType []int     `json:"companiontype,omitempty"`
+	MaxSequence   int       `json:"maxseq,omitempty"`   // The maximumnumber of ads that canbe played in an ad pod.
+	Feed          int       `json:"feed,omitempty"`     // Type of audio feed.
+	Stitched      int       `json:"stitched,omitempty"` // Indicates if the ad is stitched with audio content or delivered independently
+	NVol          int       `json:"nvol,omitempty"`     // Volume normalization mode.
+	Ext           Extension `json:"ext,omitempty"`
 }
 
 type jsonAudio Audio
 
 // Validate the object
 func (a *Audio) Validate() error {
-	if len(a.MIMEs) == 0 {
-		return ErrInvalidAudioNoMIMEs
+	if len(a.Mimes) == 0 {
+		return ErrInvalidAudioNoMimes
 	}
 	return nil
 }
diff --git a/audio_test.go b/audio_test.go
index 1c7c9a8..9ae00df 100644
--- a/audio_test.go
+++ b/audio_test.go
@@ -5,7 +5,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestAudio(t *testing.T) {
@@ -15,24 +15,24 @@ func TestAudio(t *testing.T) {
 	}
 
 	exp := &Audio{
-		MIMEs: []string{
+		Mimes: []string{
 			"audio/mp4",
 		},
-		MinDuration:  5,
-		MaxDuration:  30,
-		Protocols:    []Protocol{ProtocolDAAST1, ProtocolDAAST1Wrapper},
-		Sequence:     1,
-		BlockedAttrs: []CreativeAttribute{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert},
-		MaxExtended:  30,
-		MinBitrate:   300,
-		MaxBitrate:   1500,
-		Delivery:     []ContentDelivery{ContentDeliveryProgressive},
-		CompanionAds: []Banner{
-			{Width: 300, Height: 250, ID: "1234567893-1", Position: AdPositionAboveFold, BlockedAttrs: []CreativeAttribute{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}, ExpDirs: []ExpDir{ExpDirRight, ExpDirDown}},
-			{Width: 728, Height: 90, ID: "1234567893-2", Position: AdPositionAboveFold, BlockedAttrs: []CreativeAttribute{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}},
+		MinDuration: 5,
+		MaxDuration: 30,
+		Protocols:   []int{AudioProtocolDAAST1, AudioProtocolDAAST1Wrapper},
+		Sequence:    1,
+		BAttr:       []int{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert},
+		MaxExtended: 30,
+		MinBitrate:  300,
+		MaxBitrate:  1500,
+		Delivery:    []int{ContentDeliveryProgressive},
+		CompanionAd: []Banner{
+			{W: 300, H: 250, ID: "1234567893-1", Pos: AdPosAboveFold, BAttr: []int{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}, ExpDir: []int{ExpDirRight, ExpDirDown}},
+			{W: 728, H: 90, ID: "1234567893-2", Pos: AdPosAboveFold, BAttr: []int{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}},
 		},
-		APIs:           []APIFramework{APIFrameworkVPAID1, APIFrameworkVPAID2},
-		CompanionTypes: []CompanionType{CompanionTypeStatic, CompanionTypeHTML},
+		API:           []int{APIFrameworkVPAID1, APIFrameworkVPAID2},
+		CompanionType: []int{VASTCompanionStatic, VASTCompanionHTML},
 	}
 	if got := subject; !reflect.DeepEqual(exp, got) {
 		t.Errorf("expected %+v, got %+v", exp, got)
@@ -41,22 +41,22 @@ func TestAudio(t *testing.T) {
 
 func TestAudio_Validate(t *testing.T) {
 	subject := &Audio{
-		MinDuration:  5,
-		MaxDuration:  30,
-		Protocols:    []Protocol{ProtocolDAAST1, ProtocolDAAST1Wrapper},
-		Sequence:     1,
-		BlockedAttrs: []CreativeAttribute{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert},
-		MaxExtended:  30,
-		MinBitrate:   300,
-		MaxBitrate:   1500,
-		Delivery:     []ContentDelivery{ContentDeliveryProgressive},
-		CompanionAds: []Banner{
-			{Width: 300, Height: 250, ID: "1234567893-1", Position: AdPositionAboveFold, BlockedAttrs: []CreativeAttribute{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}, ExpDirs: []ExpDir{ExpDirRight, ExpDirDown}},
-			{Width: 728, Height: 90, ID: "1234567893-2", Position: AdPositionAboveFold, BlockedAttrs: []CreativeAttribute{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}},
+		MinDuration: 5,
+		MaxDuration: 30,
+		Protocols:   []int{AudioProtocolDAAST1, AudioProtocolDAAST1Wrapper},
+		Sequence:    1,
+		BAttr:       []int{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert},
+		MaxExtended: 30,
+		MinBitrate:  300,
+		MaxBitrate:  1500,
+		Delivery:    []int{ContentDeliveryProgressive},
+		CompanionAd: []Banner{
+			{W: 300, H: 250, ID: "1234567893-1", Pos: AdPosAboveFold, BAttr: []int{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}, ExpDir: []int{ExpDirRight, ExpDirDown}},
+			{W: 728, H: 90, ID: "1234567893-2", Pos: AdPosAboveFold, BAttr: []int{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}},
 		},
-		CompanionTypes: []CompanionType{CompanionTypeStatic, CompanionTypeHTML},
+		CompanionType: []int{VASTCompanionStatic, VASTCompanionHTML},
 	}
-	if exp, got := ErrInvalidAudioNoMIMEs, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidAudioNoMimes, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 }
diff --git a/banner.go b/banner.go
index 0b8fc98..5b6f049 100644
--- a/banner.go
+++ b/banner.go
@@ -1,7 +1,5 @@
 package openrtb
 
-import "encoding/json"
-
 // Banner object must be included directly in the impression object if the impression offered
 // for auction is display or rich media, or it may be optionally embedded in the video object to
 // describe the companion banners available for the linear or non-linear video ad.  The banner
@@ -9,21 +7,21 @@ import "encoding/json"
 // VAST response to dictate placement of the companion creatives when multiple companion ad
 // opportunities of the same size are available on a page.
 type Banner struct {
-	Width        int                 `json:"w,omitempty"`        // Width
-	Height       int                 `json:"h,omitempty"`        // Height
-	Formats      []Format            `json:"format,omitempty"`   // Array of format objects representing the banner sizes permitted.
-	WidthMax     int                 `json:"wmax,omitempty"`     // Width maximum DEPRECATED
-	HeightMax    int                 `json:"hmax,omitempty"`     // Height maximum DEPRECATED
-	WidthMin     int                 `json:"wmin,omitempty"`     // Width minimum DEPRECATED
-	HeightMin    int                 `json:"hmin,omitempty"`     // Height minimum DEPRECATED
-	ID           string              `json:"id,omitempty"`       // A unique identifier
-	BlockedTypes []BannerType        `json:"btype,omitempty"`    // Blocked banner types
-	BlockedAttrs []CreativeAttribute `json:"battr,omitempty"`    // Blocked creative attributes
-	Position     AdPosition          `json:"pos,omitempty"`      // Ad Position
-	MIMEs        []string            `json:"mimes,omitempty"`    // Whitelist of content MIME types supported
-	TopFrame     int                 `json:"topframe,omitempty"` // Default: 0 ("1": Delivered in top frame, "0": Elsewhere)
-	ExpDirs      []ExpDir            `json:"expdir,omitempty"`   // Specify properties for an expandable ad
-	APIs         []APIFramework      `json:"api,omitempty"`      // List of supported API frameworks
-	VCM          int                 `json:"vcm,omitempty"`      // Represents the relationship with video. 0 = concurrent, 1 = end-card
-	Ext          json.RawMessage     `json:"ext,omitempty"`
+	W        int       `json:"w,omitempty"`        // Width
+	H        int       `json:"h,omitempty"`        // Height
+	Format   []Format  `json:"format,omitempty"`   //Array of format objects representing the banner sizes permitted.
+	WMax     int       `json:"wmax,omitempty"`     // Width maximum DEPRECATED
+	HMax     int       `json:"hmax,omitempty"`     // Height maximum DEPRECATED
+	WMin     int       `json:"wmin,omitempty"`     // Width minimum DEPRECATED
+	HMin     int       `json:"hmin,omitempty"`     // Height minimum DEPRECATED
+	ID       string    `json:"id,omitempty"`       // A unique identifier
+	BType    []int     `json:"btype,omitempty"`    // Blocked creative types
+	BAttr    []int     `json:"battr,omitempty"`    // Blocked creative attributes
+	Pos      int       `json:"pos,omitempty"`      // Ad Position
+	Mimes    []string  `json:"mimes,omitempty"`    // Whitelist of content MIME types supported
+	TopFrame int       `json:"topframe,omitempty"` // Default: 0 ("1": Delivered in top frame, "0": Elsewhere)
+	ExpDir   []int     `json:"expdir,omitempty"`   // Specify properties for an expandable ad
+	Api      []int     `json:"api,omitempty"`      // List of supported API frameworks
+	VCM      int       `json:"vcm,omitempty"`      // Represents the relationship with video. 0 = concurrent, 1 = end-card
+	Ext      Extension `json:"ext,omitempty"`
 }
diff --git a/banner_test.go b/banner_test.go
index 669d80f..72b5c93 100644
--- a/banner_test.go
+++ b/banner_test.go
@@ -4,7 +4,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestBanner(t *testing.T) {
@@ -14,13 +14,13 @@ func TestBanner(t *testing.T) {
 	}
 
 	exp := &Banner{
-		Width:        728,
-		Height:       90,
-		Position:     AdPositionAboveFold,
-		BlockedTypes: []BannerType{BannerTypeFrame},
-		BlockedAttrs: []CreativeAttribute{CreativeAttributeWindowsDialogOrAlert},
-		APIs:         []APIFramework{APIFrameworkMRAID1},
-		VCM:          1,
+		W:     728,
+		H:     90,
+		Pos:   AdPosAboveFold,
+		BType: []int{BannerTypeFrame},
+		BAttr: []int{CreativeAttributeWindowsDialogOrAlert},
+		Api:   []int{APIFrameworkMRAID1},
+		VCM:   1,
 	}
 	if got := subject; !reflect.DeepEqual(exp, got) {
 		t.Errorf("expected %+v, got %+v", exp, got)
diff --git a/bid.go b/bid.go
index 25044d8..23f7350 100644
--- a/bid.go
+++ b/bid.go
@@ -1,9 +1,6 @@
 package openrtb
 
-import (
-	"encoding/json"
-	"errors"
-)
+import "errors"
 
 // Validation errors
 var (
@@ -20,41 +17,44 @@ var (
 // Cid can be used to block ads that were previously identified as inappropriate.
 // Substitution macros may allow a bidder to use a static notice URL for all of its bids.
 type Bid struct {
-	ID          string              `json:"id"`
-	ImpID       string              `json:"impid"`                    // Required string ID of the impression object to which this bid applies.
-	Price       float64             `json:"price"`                    // Bid price in CPM. Suggests using integer math for accounting to avoid rounding errors.
-	AdID        string              `json:"adid,omitempty"`           // References the ad to be served if the bid wins.
-	NoticeURL   string              `json:"nurl,omitempty"`           // Win notice URL.
-	BillingURL  string              `json:"burl,omitempty"`           // Billing notice URL.
-	LossURL     string              `json:"lurl,omitempty"`           // Loss notice URL.
-	AdMarkup    string              `json:"adm,omitempty"`            // Actual ad markup. XHTML if a response to a banner object, or VAST XML if a response to a video object.
-	AdvDomains  []string            `json:"adomain,omitempty"`        // Advertiser’s primary or top-level domain for advertiser checking; or multiple if imp rotating.
-	Bundle      string              `json:"bundle,omitempty"`         // A platform-specific application identifier intended to be unique to the app and independent of the exchange.
-	ImageURL    string              `json:"iurl,omitempty"`           // Sample image URL.
-	CampaignID  StringOrNumber      `json:"cid,omitempty"`            // Campaign ID that appears with the Ad markup.
-	CreativeID  string              `json:"crid,omitempty"`           // Creative ID for reporting content issues or defects. This could also be used as a reference to a creative ID that is posted with an exchange.
-	Tactic      string              `json:"tactic,omitempty"`         // Tactic ID to enable buyers to label bids for reporting to the exchange the tactic through which their bid was submitted.
-	Categories  []ContentCategory   `json:"cat,omitempty"`            // IAB content categories of the creative. Refer to List 5.1
-	Attrs       []CreativeAttribute `json:"attr,omitempty"`           // Array of creative attributes.
-	API         APIFramework        `json:"api,omitempty"`            // API required by the markup if applicable, NOTE: for ORTB ver <= 2.5 APIFramework supported is 1 to 6.
-	Protocol    Protocol            `json:"protocol,omitempty"`       // Video response protocol of the markup if applicable
-	MediaRating IQGRating           `json:"qagmediarating,omitempty"` // Creative media rating per IQG guidelines.
-	Language    string              `json:"language,omitempty"`       // Language of the creative using ISO-639-1-alpha-2.
-	DealID      string              `json:"dealid,omitempty"`         // DealID extension of private marketplace deals
-	Width       int                 `json:"w,omitempty"`              // Width of the ad in pixels.
-	Height      int                 `json:"h,omitempty"`              // Height of the ad in pixels.
-	WidthRatio  int                 `json:"wratio,omitempty"`         // Relative width of the creative when expressing size as a ratio.
-	HeightRatio int                 `json:"hratio,omitempty"`         // Relative height of the creative when expressing size as a ratio.
+	ID             string         `json:"id"`
+	ImpID          string         `json:"impid"`                    // Required string ID of the impression object to which this bid applies.
+	Price          float64        `json:"price"`                    // Bid price in CPM. Suggests using integer math for accounting to avoid rounding errors.
+	AdID           string         `json:"adid,omitempty"`           // References the ad to be served if the bid wins.
+	NURL           string         `json:"nurl,omitempty"`           // Win notice URL.
+	BURL           string         `json:"burl,omitempty"`           // Billing notice URL.
+	LURL           string         `json:"lurl,omitempty"`           // Loss notice URL.
+	AdMarkup       string         `json:"adm,omitempty"`            // Actual ad markup. XHTML if a response to a banner object, or VAST XML if a response to a video object.
+	AdvDomain      []string       `json:"adomain,omitempty"`        // Advertiser’s primary or top-level domain for advertiser checking; or multiple if imp rotating.
+	Bundle         string         `json:"bundle,omitempty"`         // A platform-specific application identifier intended to be unique to the app and independent of the exchange.
+	IURL           string         `json:"iurl,omitempty"`           // Sample image URL.
+	CampaignID     StringOrNumber `json:"cid,omitempty"`            // Campaign ID that appears with the Ad markup.
+	CreativeID     string         `json:"crid,omitempty"`           // Creative ID for reporting content issues or defects. This could also be used as a reference to a creative ID that is posted with an exchange.
+	Tactic         string         `json:"tactic,omitempty"`         // Tactic ID to enable buyers to label bids for reporting to the exchange the tactic through which their bid was submitted.
+	Cat            []string       `json:"cat,omitempty"`            // IAB content categories of the creative. Refer to List 5.1
+	Attr           []int          `json:"attr,omitempty"`           // Array of creative attributes.
+	API            int            `json:"api,omitempty"`            // API required by the markup if applicable, NOTE: for ORTB ver <= 2.5 APIFramework supported is 1 to 6.
+	Protocol       int            `json:"protocol,omitempty"`       // Video response protocol of the markup if applicable
+	QAGMediaRating int            `json:"qagmediarating,omitempty"` // Creative media rating per IQG guidelines.
+	Language       string         `json:"language,omitempty"`       // Language of the creative using ISO-639-1-alpha-2.
+	DealID         string         `json:"dealid,omitempty"`         // DealID extension of private marketplace deals
+	H              int            `json:"h,omitempty"`              // Height of the ad in pixels.
+	W              int            `json:"w,omitempty"`              // Width of the ad in pixels.
+	WRatio         int            `json:"wratio,omitempty"`         // Relative width of the creative when expressing size as a ratio.
+	HRatio         int            `json:"hratio,omitempty"`         // Relative height of the creative when expressing size as a ratio.
 
-	APIS             APIFramework      `json:"apis,omitempty"`      // APIS required by the markup if applicable.
+	APIS             []int             `json:"apis,omitempty"`      // APIS required by the markup if applicable.
 	LangB            string            `json:"langb,omitempty"`     // Language of the creative using IETF BCP 47. Only one of language or langb should be present.
 	Duration         int               `json:"dur,omitempty"`       // Duration of the video or audio creative in seconds.
 	MarkupType       MarkupType        `json:"mtype,omitempty"`     // Creative markup so that it can properly be associated.
 	SlotInPod        SlotPositionInPod `json:"slotinpod,omitempty"` // Indicates that the bid response is only eligible for a specific position.
 	CategoryTaxonomy CategoryTaxonomy  `json:"cattax,omitempty"`    // Defines the taxonomy in use.
 
-	Exp int             `json:"exp,omitempty"` // Advisory as to the number of seconds the bidder is willing to wait between the auction and the actual impression.
-	Ext json.RawMessage `json:"ext,omitempty"`
+	Exp         int       `json:"exp,omitempty"` // Advisory as to the number of seconds the bidder is willing to wait between the auction and the actual impression.
+	ContentType string    `json:"-"`             // Content of the bid
+	MediaType   string    `json:"-"`             // Media of the impression e.g. video/display
+	IsOMEnabled bool      `json:"-"`             // Flag to send indicate to the Sdk whether or not to run om scripts
+	Ext         Extension `json:"ext,omitempty"`
 }
 
 // Validate required attributes
diff --git a/bid_test.go b/bid_test.go
index 9b1d0f9..fed7861 100644
--- a/bid_test.go
+++ b/bid_test.go
@@ -5,7 +5,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestBid(t *testing.T) {
@@ -19,14 +19,14 @@ func TestBid(t *testing.T) {
 		ImpID:      "1",
 		Price:      0.751371,
 		AdID:       "52a5516d29e435137c6f6e74",
-		NoticeURL:  "http://ads.com/win/112770_1386565997?won=${AUCTION_PRICE}",
+		NURL:       "http://ads.com/win/112770_1386565997?won=${AUCTION_PRICE}",
 		AdMarkup:   "<html/>",
-		AdvDomains: []string{"ads.com"},
-		ImageURL:   "http://ads.com/112770_1386565997.jpeg",
+		AdvDomain:  []string{"ads.com"},
+		IURL:       "http://ads.com/112770_1386565997.jpeg",
 		CampaignID: "52a5516d29e435137c6f6e74",
 		CreativeID: "52a5516d29e435137c6f6e74_1386565997",
 		DealID:     "example_deal",
-		Attrs:      []CreativeAttribute{},
+		Attr:       []int{},
 	}
 	if got := subject; !reflect.DeepEqual(exp, got) {
 		t.Errorf("expected %+v, got %+v", exp, got)
@@ -35,11 +35,40 @@ func TestBid(t *testing.T) {
 
 func TestBid_Validate(t *testing.T) {
 	subject := &Bid{}
-	if exp, got := ErrInvalidBidNoID, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidBidNoID, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 	subject = &Bid{ID: "BIDID"}
-	if exp, got := ErrInvalidBidNoImpID, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidBidNoImpID, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 }
+
+func TestLoopmeARBid(t *testing.T) {
+	var subject *Bid
+	if err := fixture("bid.loopme.ar", &subject); err != nil {
+		t.Fatalf("expected no error, got %v", err)
+	}
+
+	exp := &Bid{
+		ID:         "5d4b3b3be8250678c813f475",
+		ImpID:      "1",
+		Price:      29.7,
+		AdID:       "2056515",
+		NURL:       "https://us.fake-url.com/tr?et=BID_WIN&a_ecp=29.7&a_price=${AUCTION_PRICE:BF}&name=imp_nurl",
+		AdMarkup:   "<img>",
+		AdvDomain:  []string{"unity.com"},
+		IURL:       "https://fake-url.net/assets/2690-e3c0-aa99-9efd38cd1fa6.jpg",
+		CampaignID: "2001626",
+		CreativeID: "2056515",
+		Attr:       []int{99},
+		Protocol:   2,
+		Cat:        []string{"IAB19"},
+		LURL:       "https://fake-url.com?et=BID_LOSS&meta=&a_id=${AUCTION_ID}&a_bid_id=${AUCTION_BID_ID}&a_loss_id=${AUCTION_LOSS}",
+		H:          320,
+		W:          480,
+	}
+	if got := subject; !reflect.DeepEqual(exp, got) {
+		t.Errorf("expected %+v, got %+v", exp, got)
+	}
+}
diff --git a/bidrequest.go b/bidrequest.go
index 48bfc65..4c88c39 100644
--- a/bidrequest.go
+++ b/bidrequest.go
@@ -1,7 +1,6 @@
 package openrtb
 
 import (
-	"encoding/json"
 	"errors"
 )
 
@@ -16,41 +15,43 @@ var (
 // attribute is required as is at least one "imp" (i.e., impression) object.  Other attributes are
 // optional since an exchange may establish default values.
 type BidRequest struct {
-	ID                string            `json:"id"` // Unique ID of the bid request
-	Impressions       []Impression      `json:"imp,omitempty"`
-	Site              *Site             `json:"site,omitempty"`
-	App               *App              `json:"app,omitempty"`
-	Device            *Device           `json:"device,omitempty"`
-	User              *User             `json:"user,omitempty"`
-	Test              int               `json:"test,omitempty"`    // Indicator of test mode in which auctions are not billable, where 0 = live mode, 1 = test mode
-	AuctionType       int               `json:"at"`                // Auction type, where 1 = First Price, 2 = Second Price Plus. Exchange-specific auction types can be defined using values greater than 500.
-	TimeMax           int               `json:"tmax,omitempty"`    // Maximum amount of time in milliseconds to submit a bid
-	Seats             []string          `json:"wseat,omitempty"`   // Array of buyer seats allowed to bid on this auction
-	BlockedSeats      []string          `json:"bseat,omitempty"`   // Array of buyer seats blocked to bid on this auction
-	Languages         []string          `json:"wlang,omitempty"`   // Allowed list of languages for creatives using ISO-639-1-alpha-2. Omission implies no specific restrictions, but buyers would be advised to consider language attribute in the Device and/or Content objects if available. Only one of wlang or wlangb should be present.
-	LanguagesB        []string          `json:"wlangb,omitempty"`  // Allowed list of languages for creatives using IETF BCP 47I. Omission implies no specific restrictions, but buyers would be advised to consider language attribute in the Device and/or Content objects if available. Only one of wlang or wlangb should be present.
-	AllImpressions    int               `json:"allimps,omitempty"` // Flag to indicate whether exchange can verify that all impressions offered represent all of the impressions available in context, Default: 0
-	Currencies        []string          `json:"cur,omitempty"`     // Array of allowed currencies
-	BlockedCategories []ContentCategory `json:"bcat,omitempty"`    // Blocked Advertiser Categories.
-	BlockedAdvDomains []string          `json:"badv,omitempty"`    // Array of strings of blocked toplevel domains of advertisers
-	BlockedApps       []string          `json:"bapp,omitempty"`    // Block list of applications by their platform-specific exchange-independent application identifiers. On Android, these should be bundle or package names (e.g., com.foo.mygame).  On iOS, these are numeric IDs.
-	Source            *Source           `json:"source,omitempty"`  // A Source object that provides data about the inventory source and which entity makes the final decision
-	Regulations       *Regulations      `json:"regs,omitempty"`
-	Ext               json.RawMessage   `json:"ext,omitempty"`
+	ID          string       `json:"id"` // Unique ID of the bid request
+	Imp         []Impression `json:"imp,omitempty"`
+	Site        *Site        `json:"site,omitempty"`
+	App         *App         `json:"app,omitempty"`
+	Device      *Device      `json:"device,omitempty"`
+	User        *User        `json:"user,omitempty"`
+	Test        int          `json:"test,omitempty"`    // Indicator of test mode in which auctions are not billable, where 0 = live mode, 1 = test mode
+	AuctionType int          `json:"at"`                // Auction type, where 1 = First Price, 2 = Second Price Plus. Exchange-specific auction types can be defined using values greater than 500.
+	TMax        int          `json:"tmax,omitempty"`    // Maximum amount of time in milliseconds to submit a bid
+	WSeat       []string     `json:"wseat,omitempty"`   // Array of buyer seats allowed to bid on this auction
+	BSeat       []string     `json:"bseat,omitempty"`   // Array of buyer seats blocked to bid on this auction
+	WLang       []string     `json:"wlang,omitempty"`   // Allowed list of languages for creatives using ISO-639-1-alpha-2. Omission implies no specific restrictions, but buyers would be advised to consider language attribute in the Device and/or Content objects if available. Only one of wlang or wlangb should be present.
+	LanguagesB  []string     `json:"wlangb,omitempty"`  // Allowed list of languages for creatives using IETF BCP 47I. Omission implies no specific restrictions, but buyers would be advised to consider language attribute in the Device and/or Content objects if available. Only one of wlang or wlangb should be present.
+	AllImps     int          `json:"allimps,omitempty"` // Flag to indicate whether exchange can verify that all impressions offered represent all of the impressions available in context, Default: 0
+	Cur         []string     `json:"cur,omitempty"`     // Array of allowed currencies
+	Bcat        []string     `json:"bcat,omitempty"`    // Blocked Advertiser Categories.
+	BAdv        []string     `json:"badv,omitempty"`    // Array of strings of blocked toplevel domains of advertisers
+	BApp        []string     `json:"bapp,omitempty"`    // Block list of applications by their platform-specific exchange-independent application identifiers. On Android, these should be bundle or package names (e.g., com.foo.mygame).  On iOS, these are numeric IDs.
+	Source      *Source      `json:"source,omitempty"`  // A Source object that provides data about the inventory source and which entity makes the final decision
+	Regs        *Regulations `json:"regs,omitempty"`
+	Ext         Extension    `json:"ext,omitempty"`
+
+	Pmp *Pmp `json:"pmp,omitempty"` // DEPRECATED: kept for backwards compatibility
 }
 
 // Validate the request
 func (req *BidRequest) Validate() error {
 	if req.ID == "" {
 		return ErrInvalidReqNoID
-	} else if len(req.Impressions) == 0 {
+	} else if len(req.Imp) == 0 {
 		return ErrInvalidReqNoImps
 	} else if req.Site != nil && req.App != nil {
 		return ErrInvalidReqMultiInv
 	}
 
-	for i := range req.Impressions {
-		imp := req.Impressions[i]
+	for i := range req.Imp {
+		imp := req.Imp[i]
 		if err := (&imp).Validate(); err != nil {
 			return err
 		}
diff --git a/bidrequest_test.go b/bidrequest_test.go
index 33ff7a4..59419fb 100644
--- a/bidrequest_test.go
+++ b/bidrequest_test.go
@@ -5,7 +5,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestBidRequest(t *testing.T) {
@@ -17,11 +17,11 @@ func TestBidRequest(t *testing.T) {
 	privacyPolicy := 1
 	exp := &BidRequest{
 		ID: "1234534625254",
-		Impressions: []Impression{
+		Imp: []Impression{
 			{
 				ID:     "1",
 				Secure: 1,
-				Banner: &Banner{Width: 300, Height: 250, Position: AdPositionAboveFold, BlockedAttrs: []CreativeAttribute{CreativeAttributeUserInitiated}},
+				Banner: &Banner{W: 300, H: 250, Pos: AdPosAboveFold, BAttr: []int{CreativeAttributeUserInitiated}},
 			},
 		},
 		Site: &Site{
@@ -29,31 +29,31 @@ func TestBidRequest(t *testing.T) {
 				ID:            "234563",
 				Name:          "Site ABCD",
 				Domain:        "siteabcd.com",
-				Categories:    []ContentCategory{ContentCategoryAutoParts, ContentCategoryAutoRepair},
+				Cat:           []string{string(ContentCategoryAutoParts), string(ContentCategoryAutoRepair)},
 				Publisher:     &Publisher{ID: "pub12345", Name: "Publisher A"},
 				PrivacyPolicy: &privacyPolicy,
 				Content: &Content{
 					Keywords: "keyword a,keyword b,keyword c",
 				},
 			},
-			Page:     "http://siteabcd.com/page.htm",
-			Referrer: "http://referringsite.com/referringpage.htm",
+			Page: "http://siteabcd.com/page.htm",
+			Ref:  "http://referringsite.com/referringpage.htm",
 		},
 		Device: &Device{
-			UA:           "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16",
-			IP:           "64.124.253.1",
-			OS:           "OS X",
-			JS:           1,
-			FlashVersion: "10.1",
+			UA:       "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16",
+			IP:       "64.124.253.1",
+			OS:       "OS X",
+			JS:       1,
+			FlashVer: "10.1",
 		},
 		User: &User{
 			ID:       "45asdf987656789adfad4678rew656789",
 			BuyerUID: "5df678asd8987656asdf78987654",
 		},
-		Test:              1,
-		AuctionType:       2,
-		TimeMax:           120,
-		BlockedAdvDomains: []string{"company1.com", "company2.com"},
+		Test:        1,
+		AuctionType: 2,
+		TMax:        120,
+		BAdv:        []string{"company1.com", "company2.com"},
 	}
 	if got := subject; !reflect.DeepEqual(exp, got) {
 		t.Errorf("expected %+v, got %+v", exp, got)
@@ -74,15 +74,15 @@ func TestBidRequest_complex(t *testing.T) {
 
 func TestBidRequest_Validate(t *testing.T) {
 	subject := &BidRequest{}
-	if exp, got := ErrInvalidReqNoID, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidReqNoID, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 	subject = &BidRequest{ID: "RID"}
-	if exp, got := ErrInvalidReqNoImps, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidReqNoImps, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
-	subject = &BidRequest{ID: "A", Impressions: []Impression{{ID: "1"}}, Site: &Site{}, App: &App{}}
-	if exp, got := ErrInvalidReqMultiInv, subject.Validate(); !errors.Is(exp, got) {
+	subject = &BidRequest{ID: "A", Imp: []Impression{{ID: "1"}}, Site: &Site{}, App: &App{}}
+	if exp, got := ErrInvalidReqMultiInv, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 }
diff --git a/bidresponse.go b/bidresponse.go
index 58dde72..345fbb4 100644
--- a/bidresponse.go
+++ b/bidresponse.go
@@ -1,7 +1,6 @@
 package openrtb
 
 import (
-	"encoding/json"
 	"errors"
 )
 
@@ -17,25 +16,25 @@ var (
 // No-Bids on all impressions should be indicated as a HTTP 204 response.
 // For no-bids on specific impressions, the bidder should omit these from the bid response.
 type BidResponse struct {
-	ID         string          `json:"id"`                   // Reflection of the bid request ID for logging purposes
-	SeatBids   []SeatBid       `json:"seatbid"`              // Array of seatbid objects
-	BidID      string          `json:"bidid,omitempty"`      // Optional response tracking ID for bidders
-	Currency   string          `json:"cur,omitempty"`        // Bid currency
-	CustomData string          `json:"customdata,omitempty"` // Encoded user features
-	NBR        NBR             `json:"nbr,omitempty"`        // Reason for not bidding, where 0 = unknown error, 1 = technical error, 2 = invalid request, 3 = known web spider, 4 = suspected Non-Human Traffic, 5 = cloud, data center, or proxy IP, 6 = unsupported device, 7 = blocked publisher or site, 8 = unmatched user
-	Ext        json.RawMessage `json:"ext,omitempty"`        // Custom specifications in JSon
+	ID         string    `json:"id"`                   // Reflection of the bid request ID for logging purposes
+	SeatBid    []SeatBid `json:"seatbid"`              // Array of seatbid objects
+	BidID      string    `json:"bidid,omitempty"`      // Optional response tracking ID for bidders
+	Currency   string    `json:"cur,omitempty"`        // Bid currency
+	CustomData string    `json:"customdata,omitempty"` // Encoded user features
+	NBR        int       `json:"nbr,omitempty"`        // Reason for not bidding, where 0 = unknown error, 1 = technical error, 2 = invalid request, 3 = known web spider, 4 = suspected Non-Human Traffic, 5 = cloud, data center, or proxy IP, 6 = unsupported device, 7 = blocked publisher or site, 8 = unmatched user
+	Ext        Extension `json:"ext,omitempty"`        // Custom specifications in JSon
 }
 
 // Validate required attributes
 func (res *BidResponse) Validate() error {
 	if res.ID == "" {
 		return ErrInvalidRespNoID
-	} else if len(res.SeatBids) == 0 {
+	} else if len(res.SeatBid) == 0 {
 		return ErrInvalidRespNoSeatBids
 	}
 
-	for i := range res.SeatBids {
-		sb := res.SeatBids[i]
+	for i := range res.SeatBid {
+		sb := res.SeatBid[i]
 		if err := (&sb).Validate(); err != nil {
 			return err
 		}
diff --git a/bidresponse_test.go b/bidresponse_test.go
index 39fb075..bc3de35 100644
--- a/bidresponse_test.go
+++ b/bidresponse_test.go
@@ -5,7 +5,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestBidResponse(t *testing.T) {
@@ -16,20 +16,20 @@ func TestBidResponse(t *testing.T) {
 
 	exp := &BidResponse{
 		ID: "BID-4-ZIMP-4b309eae-504a-4252-a8a8-4c8ceee9791a",
-		SeatBids: []SeatBid{
+		SeatBid: []SeatBid{
 			{
-				Bids: []Bid{
+				Bid: []Bid{
 					{
 						ID:         "32a69c6ba388f110487f9d1e63f77b22d86e916b",
 						ImpID:      "32a69c6ba388f110487f9d1e63f77b22d86e916b",
 						Price:      0.065445,
 						AdID:       "529833ce55314b19e8796116",
-						NoticeURL:  "http://ads.com/win/529833ce55314b19e8796116?won=${auction_price}",
+						NURL:       "http://ads.com/win/529833ce55314b19e8796116?won=${auction_price}",
 						AdMarkup:   "<iframe src=\"foo.bar\"/>",
-						AdvDomains: []string{},
+						AdvDomain:  []string{},
 						CampaignID: "529833ce55314b19e8796116",
 						CreativeID: "529833ce55314b19e8796116_1385706446",
-						Attrs:      []CreativeAttribute{},
+						Attr:       []int{},
 					},
 				},
 				Seat: "772",
@@ -56,11 +56,11 @@ func TestBidResponse_complex(t *testing.T) {
 
 func TestBidResponse_Validate(t *testing.T) {
 	subject := &BidResponse{}
-	if exp, got := ErrInvalidRespNoID, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidRespNoID, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 	subject = &BidResponse{ID: "RID"}
-	if exp, got := ErrInvalidRespNoSeatBids, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidRespNoSeatBids, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 }
diff --git a/content.go b/content.go
index d4b8d0d..fc75af3 100644
--- a/content.go
+++ b/content.go
@@ -1,42 +1,40 @@
 package openrtb
 
-import "encoding/json"
-
 // Content object describes the content in which the impression will appear, which may be syndicated or nonsyndicated
 // content. This object may be useful when syndicated content contains impressions and does
 // not necessarily match the publisher's general content. The exchange might or might not have
 // knowledge of the page where the content is running, as a result of the syndication method. For
 // example might be a video impression embedded in an iframe on an unknown web property or device.
 type Content struct {
-	ID                 string            `json:"id,omitempty"`                 // ID uniquely identifying the content.
-	Episode            int               `json:"episode,omitempty"`            // Episode number (typically applies to video content).
-	Title              string            `json:"title,omitempty"`              // Content title.
-	Series             string            `json:"series,omitempty"`             // Content series.
-	Season             string            `json:"season,omitempty"`             // Content season.
-	Artist             string            `json:"artist,omitempty"`             // Artist credited with the content.
-	Genre              string            `json:"genre,omitempty"`              // Genre that best describes the content
-	Album              string            `json:"album,omitempty"`              // Album to which the content belongs; typically for audio.
-	ISRC               string            `json:"isrc,omitempty"`               // International Standard Recording Code conforming to ISO - 3901.
-	Producer           *Producer         `json:"producer,omitempty"`           // The producer.
-	URL                string            `json:"url,omitempty"`                // URL of the content, for buy-side contextualization or review.
-	CategoryTaxonomy   CategoryTaxonomy  `json:"cattax,omitempty"`             // Defines the taxonomy in use.
-	Categories         []ContentCategory `json:"cat,omitempty"`                // Array of IAB content categories that describe the content.
-	ProductionQuality  ProductionQuality `json:"prodq,omitempty"`              // Production quality per IAB's classification.
-	VideoQuality       ProductionQuality `json:"videoquality,omitempty"`       // DEPRECATED. Video quality per IAB's classification.
-	Context            ContentContext    `json:"context,omitempty"`            // Type of content (game, video, text, etc.).
-	ContentRating      string            `json:"contentrating,omitempty"`      // Content rating (e.g., MPAA).
-	UserRating         string            `json:"userrating,omitempty"`         // User rating of the content (e.g., number of stars, likes, etc.).
-	MediaRating        IQGRating         `json:"qagmediarating,omitempty"`     // Media rating per QAG guidelines.
-	Keywords           string            `json:"keywords,omitempty"`           // Comma separated list of keywords describing the content.
-	LiveStream         int               `json:"livestream,omitempty"`         // 0 = not live, 1 = content is live (e.g., stream, live blog).
-	SourceRelationship int               `json:"sourcerelationship,omitempty"` // 0 = indirect, 1 = direct.
-	Length             int               `json:"len,omitempty"`                // Length of content in seconds; appropriate for video or audio.
-	Language           string            `json:"language,omitempty"`           // Content language using ISO-639-1-alpha-2.
-	LanguageB          string            `json:"langb,omitempty"`              // Content language using IETF BCP 47. Only one of language or langb should be present.
-	Embeddable         int               `json:"embeddable,omitempty"`         // Indicator of whether or not the content is embeddable (e.g., an embeddable video player), where 0 = no, 1 = yes.
-	Data               []Data            `json:"data,omitempty"`               // Additional content data.
-	Network            *ChannelEntity    `json:"network,omitempty"`            // Details about the network the content is on.
-	Channel            *ChannelEntity    `json:"channel,omitempty"`            // Details about the channel the content is on.
-	KwArray            []string          `json:"kwarray,omitempty"`            // Array of keywords about the site. Only one of ‘keywords’ or‘kwarray’ may be present.
-	Ext                json.RawMessage   `json:"ext,omitempty"`
+	ID                 string           `json:"id,omitempty"`                 // ID uniquely identifying the content.
+	Episode            int              `json:"episode,omitempty"`            // Episode number (typically applies to video content).
+	Title              string           `json:"title,omitempty"`              // Content title.
+	Series             string           `json:"series,omitempty"`             // Content series.
+	Season             string           `json:"season,omitempty"`             // Content season.
+	Artist             string           `json:"artist,omitempty"`             // Artist credited with the content.
+	Genre              string           `json:"genre,omitempty"`              // Genre that best describes the content
+	Album              string           `json:"album,omitempty"`              // Album to which the content belongs; typically for audio.
+	ISRC               string           `json:"isrc,omitempty"`               // International Standard Recording Code conforming to ISO - 3901.
+	Producer           *Producer        `json:"producer,omitempty"`           // The producer.
+	URL                string           `json:"url,omitempty"`                // URL of the content, for buy-side contextualization or review.
+	CategoryTaxonomy   CategoryTaxonomy `json:"cattax,omitempty"`             // Defines the taxonomy in use.
+	Cat                []string         `json:"cat,omitempty"`                // Array of IAB content categories that describe the content.
+	ProdQuality        int              `json:"prodq,omitempty"`              // Production quality per IAB's classification.
+	VideoQuality       int              `json:"videoquality,omitempty"`       // DEPRECATED. Video quality per IAB's classification.
+	Context            int              `json:"context,omitempty"`            // Type of content (game, video, text, etc.).
+	ContentRating      string           `json:"contentrating,omitempty"`      // Content rating (e.g., MPAA).
+	UserRating         string           `json:"userrating,omitempty"`         // User rating of the content (e.g., number of stars, likes, etc.).
+	QAGMediaRating     int              `json:"qagmediarating,omitempty"`     // Media rating per QAG guidelines.
+	Keywords           string           `json:"keywords,omitempty"`           // Comma separated list of keywords describing the content.
+	LiveStream         int              `json:"livestream,omitempty"`         // 0 = not live, 1 = content is live (e.g., stream, live blog).
+	SourceRelationship int              `json:"sourcerelationship,omitempty"` // 0 = indirect, 1 = direct.
+	Len                int              `json:"len,omitempty"`                // Length of content in seconds; appropriate for video or audio.
+	Language           string           `json:"language,omitempty"`           // Content language using ISO-639-1-alpha-2.
+	LanguageB          string           `json:"langb,omitempty"`              // Content language using IETF BCP 47. Only one of language or langb should be present.
+	Embeddable         int              `json:"embeddable,omitempty"`         // Indicator of whether or not the content is embeddable (e.g., an embeddable video player), where 0 = no, 1 = yes.
+	Data               []Data           `json:"data,omitempty"`               // Additional content data.
+	Network            *ChannelEntity   `json:"network,omitempty"`            // Details about the network the content is on.
+	Channel            *ChannelEntity   `json:"channel,omitempty"`            // Details about the channel the content is on.
+	KwArray            []string         `json:"kwarray,omitempty"`            // Array of keywords about the site. Only one of ‘keywords’ or‘kwarray’ may be present.
+	Ext                Extension        `json:"ext,omitempty"`
 }
diff --git a/content_test.go b/content_test.go
index 2e414fd..289ca62 100644
--- a/content_test.go
+++ b/content_test.go
@@ -4,7 +4,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestContent(t *testing.T) {
@@ -41,7 +41,7 @@ func TestContent_quoted(t *testing.T) {
 		URL:                "http://cdnp.tremormedia.com/video/1984.flv",
 		LiveStream:         0,
 		ContentRating:      "G",
-		Length:             129,
+		Len:                129,
 		Series:             "book reading",
 		VideoQuality:       2,
 		Context:            1,
diff --git a/device.go b/device.go
index 485925a..26706e9 100644
--- a/device.go
+++ b/device.go
@@ -1,42 +1,41 @@
 package openrtb
 
-import "encoding/json"
-
 // Device object provides information pertaining to the device including its hardware,
 // platform, location, and carrier. This device can refer to a mobile handset, a desktop computer,
 // set top box or other digital device.
 type Device struct {
-	UA           string          `json:"ua,omitempty"`             // User agent
-	Sua          *UserAgent      `json:"sua,omitempty"`            // Structured User agent. It's more accurate than UA
-	Geo          *Geo            `json:"geo,omitempty"`            // Location of the device assumed to be the user’s current location
-	DNT          int             `json:"dnt,omitempty"`            // "1": Do not track
-	LMT          int             `json:"lmt,omitempty"`            // "1": Limit Ad Tracking
-	IP           string          `json:"ip,omitempty"`             // IPv4
-	IPv6         string          `json:"ipv6,omitempty"`           // IPv6
-	DeviceType   DeviceType      `json:"devicetype,omitempty"`     // The general type of device.
-	Make         string          `json:"make,omitempty"`           // Device make
-	Model        string          `json:"model,omitempty"`          // Device model
-	OS           string          `json:"os,omitempty"`             // Device OS
-	OSVersion    string          `json:"osv,omitempty"`            // Device OS version
-	HWVersion    string          `json:"hwv,omitempty"`            // Hardware version of the device (e.g., "5S" for iPhone 5S).
-	Height       int             `json:"h,omitempty"`              // Physical height of the screen in pixels.
-	Width        int             `json:"w,omitempty"`              // Physical width of the screen in pixels.
-	PPI          int             `json:"ppi,omitempty"`            // Screen size as pixels per linear inch.
-	PixelRatio   float64         `json:"pxratio,omitempty"`        // The ratio of physical pixels to device independent pixels.
-	JS           int             `json:"js,omitempty"`             // Javascript status ("0": Disabled, "1": Enabled)
-	GeoFetch     int             `json:"geofetch,omitempty"`       // Indicates if the geolocation API will be available to JavaScript code running in the banner,
-	FlashVersion string          `json:"flashver,omitempty"`       // Flash version
-	Language     string          `json:"language,omitempty"`       // Browser language using ISO-639-1-alpha-2. Only one of language or langb should be present.
-	LanguageB    string          `json:"langb,omitempty"`          // Browser language using IETF BCP 47. Only one of language or langb should be present.
-	Carrier      string          `json:"carrier,omitempty"`        // Carrier or ISP derived from the IP address
-	MCCMNC       string          `json:"mccmnc,omitempty"`         // Mobile carrier as the concatenated MCC-MNC code (e.g., "310-005" identifies Verizon Wireless CDMA in the USA).
-	ConnType     ConnType        `json:"connectiontype,omitempty"` // Network connection type.
-	IFA          string          `json:"ifa,omitempty"`            // Native identifier for advertisers
-	IDSHA1       string          `json:"didsha1,omitempty"`        // SHA1 hashed device ID
-	IDMD5        string          `json:"didmd5,omitempty"`         // MD5 hashed device ID
-	PIDSHA1      string          `json:"dpidsha1,omitempty"`       // SHA1 hashed platform device ID
-	PIDMD5       string          `json:"dpidmd5,omitempty"`        // MD5 hashed platform device ID
-	MacSHA1      string          `json:"macsha1,omitempty"`        // SHA1 hashed device ID; IMEI when available, else MEID or ESN
-	MacMD5       string          `json:"macmd5,omitempty"`         // MD5 hashed device ID; IMEI when available, else MEID or ESN
-	Ext          json.RawMessage `json:"ext,omitempty"`
+	UA         string     `json:"ua,omitempty"`             // User agent
+	Sua        *UserAgent `json:"sua,omitempty"`            // Structured User agent. It's more accurate than UA
+	Geo        *Geo       `json:"geo,omitempty"`            // Location of the device assumed to be the user’s current location
+	DNT        int        `json:"dnt,omitempty"`            // "1": Do not track
+	LMT        int        `json:"lmt,omitempty"`            // "1": Limit Ad Tracking
+	IP         string     `json:"ip,omitempty"`             // IPv4
+	IPv6       string     `json:"ipv6,omitempty"`           // IPv6
+	DeviceType int        `json:"devicetype,omitempty"`     // The general type of device.
+	Make       string     `json:"make,omitempty"`           // Device make
+	Model      string     `json:"model,omitempty"`          // Device model
+	OS         string     `json:"os,omitempty"`             // Device OS
+	OSVer      string     `json:"osv,omitempty"`            // Device OS version
+	HwVer      string     `json:"hwv,omitempty"`            // Hardware version of the device (e.g., "5S" for iPhone 5S).
+	H          int        `json:"h,omitempty"`              // Physical height of the screen in pixels.
+	W          int        `json:"w,omitempty"`              // Physical width of the screen in pixels.
+	PPI        int        `json:"ppi,omitempty"`            // Screen size as pixels per linear inch.
+	PxRatio    float64    `json:"pxratio,omitempty"`        // The ratio of physical pixels to device independent pixels.
+	JS         int        `json:"js,omitempty"`             // Javascript status ("0": Disabled, "1": Enabled)
+	GeoFetch   int        `json:"geofetch,omitempty"`       // Indicates if the geolocation API will be available to JavaScript code running in the banner,
+	FlashVer   string     `json:"flashver,omitempty"`       // Flash version
+	Language   string     `json:"language,omitempty"`       // Browser language
+	LanguageB  string     `json:"langb,omitempty"`          // Browser language using IETF BCP 47. Only one of language or langb should be present.
+	Carrier    string     `json:"carrier,omitempty"`        // Carrier or ISP derived from the IP address
+	MCCMNC     string     `json:"mccmnc,omitempty"`         // Mobile carrier as the concatenated MCC-MNC code (e.g., "310-005" identifies Verizon Wireless CDMA in the USA).
+	ConnType   int        `json:"connectiontype,omitempty"` // Network connection type.
+	IFA        string     `json:"ifa,omitempty"`            // Native identifier for advertisers
+	IDSHA1     string     `json:"didsha1,omitempty"`        // SHA1 hashed device ID
+	IDMD5      string     `json:"didmd5,omitempty"`         // MD5 hashed device ID
+	PIDSHA1    string     `json:"dpidsha1,omitempty"`       // SHA1 hashed platform device ID
+	PIDMD5     string     `json:"dpidmd5,omitempty"`        // MD5 hashed platform device ID
+	MacSHA1    string     `json:"macsha1,omitempty"`        // SHA1 hashed device ID; IMEI when available, else MEID or ESN
+	MacMD5     string     `json:"macmd5,omitempty"`         // MD5 hashed device ID; IMEI when available, else MEID or ESN
+	OAID       string     `json:"oaid,omitempty"`           // Open Advertising Tracking ID
+	Ext        Extension  `json:"ext,omitempty"`
 }
diff --git a/device_test.go b/device_test.go
index 988043d..d7f8b95 100644
--- a/device_test.go
+++ b/device_test.go
@@ -4,7 +4,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestDevice(t *testing.T) {
@@ -18,13 +18,13 @@ func TestDevice(t *testing.T) {
 		UA:  "Mozilla/5.0 (iPhone; CPU iPhone OS 6_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
 		IP:  "123.145.167.189",
 		Geo: &Geo{
-			Latitude:  35.012345,
-			Longitude: -115.12345,
-			Country:   "USA",
-			Region:    "CA",
-			Metro:     "803",
-			City:      "Los Angeles",
-			ZIP:       "90049",
+			Lat:     35.012345,
+			Lon:     -115.12345,
+			Country: "USA",
+			Region:  "CA",
+			Metro:   "803",
+			City:    "Los Angeles",
+			Zip:     "90049",
 		},
 		PIDSHA1:    "AA000DFE74168477C70D291f574D344790E0BB11",
 		PIDMD5:     "AA003EABFB29E6F759F3BDAB34E50BB11",
@@ -33,11 +33,12 @@ func TestDevice(t *testing.T) {
 		Make:       "Apple",
 		Model:      "iPhone",
 		OS:         "iOS",
-		OSVersion:  "6.1",
+		OSVer:      "6.1",
 		JS:         1,
 		ConnType:   ConnTypeCell,
 		MCCMNC:     "722-341",
 		DeviceType: DeviceTypeMobile,
+		OAID:       "1fe9a970-efbb-29e0-0bdd-f5dbbf751ab5",
 	}
 	if got := subject; !reflect.DeepEqual(exp, got) {
 		t.Errorf("expected %+v, got %+v", exp, got)
diff --git a/extension.go b/extension.go
new file mode 100644
index 0000000..4398ecb
--- /dev/null
+++ b/extension.go
@@ -0,0 +1,23 @@
+package openrtb
+
+import (
+	"encoding/json"
+	"errors"
+)
+
+// Extension is a raw encoded JSON value.
+// It implements Marshaler and Unmarshaler, defined in encoding/json package
+type Extension json.RawMessage
+
+// MarshalJSON returns e as the JSON encoding of e.
+func (e Extension) MarshalJSON() ([]byte, error) {
+	return json.RawMessage(e).MarshalJSON()
+}
+
+// UnmarshalJSON sets *e to a copy of data.
+func (e *Extension) UnmarshalJSON(data []byte) error {
+	if e == nil {
+		return errors.New("openrtb.Extension: UnmarshalJSON on nil pointer")
+	}
+	return (*json.RawMessage)(e).UnmarshalJSON(data)
+}
diff --git a/extension_test.go b/extension_test.go
new file mode 100644
index 0000000..70de18d
--- /dev/null
+++ b/extension_test.go
@@ -0,0 +1,39 @@
+package openrtb_test
+
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+
+	. "github.com/UnityTech/openrtb/v3"
+)
+
+func TestExtension(t *testing.T) {
+	var _ json.Marshaler = (Extension)(nil)
+	var _ json.Unmarshaler = (*Extension)(nil)
+
+	t.Run("should marshal JSON", func(t *testing.T) {
+		subject := Extension(`{"foo":"bar"}`)
+
+		got, err := json.Marshal(subject)
+		if err != nil {
+			t.Fatal(err.Error())
+		}
+		exp := []byte(`{"foo":"bar"}`)
+		if !reflect.DeepEqual(exp, got) {
+			t.Errorf("expected %+v, got %+v", exp, got)
+		}
+	})
+
+	t.Run("should unmarshal JSON", func(t *testing.T) {
+		subject := Extension([]byte{})
+		err := json.Unmarshal([]byte(`{"foo":"bar"}`), &subject)
+		if err != nil {
+			t.Fatal(err.Error())
+		}
+		exp := Extension(`{"foo":"bar"}`)
+		if got := subject; !reflect.DeepEqual(exp, got) {
+			t.Errorf("expected %+v, got %+v", exp, got)
+		}
+	})
+}
diff --git a/go.mod b/go.mod
index 5d8e195..204a70e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
-module github.com/bsm/openrtb/v3
+module github.com/UnityTech/openrtb/v3
 
-go 1.13
+go 1.18
diff --git a/impression.go b/impression.go
index 589daf6..c0d1c66 100644
--- a/impression.go
+++ b/impression.go
@@ -1,9 +1,6 @@
 package openrtb
 
-import (
-	"encoding/json"
-	"errors"
-)
+import "errors"
 
 // Validation errors
 var (
@@ -19,23 +16,28 @@ var (
 // The presence of Banner, Video, and/or Native objects
 // subordinate to the Imp object indicates the type of impression being offered.
 type Impression struct {
-	ID                    string          `json:"id"` // A unique identifier for this impression
-	Banner                *Banner         `json:"banner,omitempty"`
-	Video                 *Video          `json:"video,omitempty"`
-	Audio                 *Audio          `json:"audio,omitempty"`
-	Native                *Native         `json:"native,omitempty"`
-	PMP                   *PMP            `json:"pmp,omitempty"`               // A reference to the PMP object containing any Deals eligible for the impression object.
-	DisplayManager        string          `json:"displaymanager,omitempty"`    // Name of ad mediation partner, SDK technology, etc
-	DisplayManagerVersion string          `json:"displaymanagerver,omitempty"` // Version of the above
-	Interstitial          int             `json:"instl,omitempty"`             // Interstitial, Default: 0 ("1": Interstitial, "0": Something else)
-	TagID                 string          `json:"tagid,omitempty"`             // IDentifier for specific ad placement or ad tag
-	BidFloor              float64         `json:"bidfloor,omitempty"`          // Bid floor for this impression in CPM
-	BidFloorCurrency      string          `json:"bidfloorcur,omitempty"`       // Currency of bid floor
-	Secure                NumberOrString  `json:"secure,omitempty"`            // Flag to indicate whether the impression requires secure HTTPS URL creative assets and markup.
-	Quantity              *Quantity       `json:"qty,omitempty"`          // Includes the impression multiplier, and describes its source.
-	Exp                   int             `json:"exp,omitempty"`          // Advisory as to the number of seconds that may elapse between the auction and the actual impression.
-	IFrameBusters         []string        `json:"iframebuster,omitempty"` // Array of names for supportediframe busters.
-	Ext                   json.RawMessage `json:"ext,omitempty"`
+	ID                string         `json:"id"` // A unique identifier for this impression
+	Metric            []*Metric      `json:"metric,omitempty"`
+	Banner            *Banner        `json:"banner,omitempty"`
+	Video             *Video         `json:"video,omitempty"`
+	Audio             *Audio         `json:"audio,omitempty"`
+	Native            *Native        `json:"native,omitempty"`
+	Pmp               *Pmp           `json:"pmp,omitempty"`               // A reference to the PMP object containing any Deals eligible for the impression object.
+	DisplayManager    string         `json:"displaymanager,omitempty"`    // Name of ad mediation partner, SDK technology, etc
+	DisplayManagerVer string         `json:"displaymanagerver,omitempty"` // Version of the above
+	Clickbrowser      int            `json:"clickbrowser"`                // Indicates the type of browser opened upon clicking the creative in an app, where 0 = embedded, 1 = native
+	Instl             int            `json:"instl,omitempty"`             // Interstitial, Default: 0 ("1": Interstitial, "0": Something else)
+	TagID             string         `json:"tagid,omitempty"`             // IDentifier for specific ad placement or ad tag
+	BidFloor          float64        `json:"bidfloor,omitempty"`          // Bid floor for this impression in CPM
+	BidFloorCurrency  string         `json:"bidfloorcur,omitempty"`       // Currency of bid floor
+	Secure            NumberOrString `json:"secure,omitempty"`            // Flag to indicate whether the impression requires secure HTTPS URL creative assets and markup.
+	Quantity          *Quantity      `json:"qty,omitempty"`               // Includes the impression multiplier, and describes its source.
+	Exp               int            `json:"exp,omitempty"`               // Advisory as to the number of seconds that may elapse between the auction and the actual impression.
+	IFrameBuster      []string       `json:"iframebuster,omitempty"`      // Array of names for supportediframe busters.
+	ContentType       string         `json:"-"`                           // Used to attribute bid to a content - not sent in request
+	MediaType         string         `json:"-"`                           // media of the impression e.g. video/display
+	Rewarded          int            `json:"rwdd"`                        //Indicates whether the user receives a reward for viewing the ad
+	Ext               Extension      `json:"ext,omitempty"`
 }
 
 func (imp *Impression) assetCount() int {
diff --git a/impression_test.go b/impression_test.go
index 95046a3..3a4cc2c 100644
--- a/impression_test.go
+++ b/impression_test.go
@@ -5,7 +5,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestImpression(t *testing.T) {
@@ -16,12 +16,24 @@ func TestImpression(t *testing.T) {
 
 	exp := &Impression{
 		ID: "1",
+		Metric: []*Metric{
+			{
+				Type:   "viewable_percentage_1",
+				Value:  0.99,
+				Vendor: "MOAT",
+			},
+			{
+				Type:   "viewable_percentage_2",
+				Value:  0.98,
+				Vendor: "MOAT",
+			},
+		},
 		Banner: &Banner{
-			Width:  300,
-			Height: 250,
+			W: 300,
+			H: 250,
 		},
 		BidFloor: 0.03,
-		PMP: &PMP{
+		Pmp: &Pmp{
 			Private: 1,
 			Deals: []Deal{
 				{
@@ -34,6 +46,7 @@ func TestImpression(t *testing.T) {
 		Quantity: &Quantity{
 			Multiplier: 1.0,
 		},
+		Rewarded: 1,
 	}
 	if got := subject; !reflect.DeepEqual(exp, got) {
 		t.Errorf("expected %+v, got %+v", exp, got)
@@ -42,11 +55,11 @@ func TestImpression(t *testing.T) {
 
 func TestImpression_Validate(t *testing.T) {
 	subject := &Impression{}
-	if exp, got := ErrInvalidImpNoID, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidImpNoID, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 	subject = &Impression{ID: "IMPID", Banner: &Banner{}, Video: &Video{}}
-	if exp, got := ErrInvalidImpMultiAssets, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidImpMultiAssets, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 }
diff --git a/inventory.go b/inventory.go
index 29b33f9..f3feba4 100644
--- a/inventory.go
+++ b/inventory.go
@@ -1,20 +1,18 @@
 package openrtb
 
-import "encoding/json"
-
 // Inventory contains inventory specific attributes
 type Inventory struct {
-	ID                string            `json:"id,omitempty"` // ID on the exchange
-	Name              string            `json:"name,omitempty"`
-	Domain            string            `json:"domain,omitempty"`
-	Categories        []ContentCategory `json:"cat,omitempty"`           // Array of IAB content categories
-	SectionCategories []ContentCategory `json:"sectioncat,omitempty"`    // Array of IAB content categories for subsection
-	PageCategories    []ContentCategory `json:"pagecat,omitempty"`       // Array of IAB content categories for page
-	PrivacyPolicy     *int              `json:"privacypolicy,omitempty"` // Default: 1 ("1": has a privacy policy)
-	Publisher         *Publisher        `json:"publisher,omitempty"`     // Details about the Publisher
-	Content           *Content          `json:"content,omitempty"`       // Details about the Content
-	Keywords          string            `json:"keywords,omitempty"`      // Comma separated list of keywords about the site.
-	Ext               json.RawMessage   `json:"ext,omitempty"`
+	ID            string     `json:"id,omitempty"` // ID on the exchange
+	Name          string     `json:"name,omitempty"`
+	Domain        string     `json:"domain,omitempty"`
+	Cat           []string   `json:"cat,omitempty"`           // Array of IAB content categories
+	SectionCat    []string   `json:"sectioncat,omitempty"`    // Array of IAB content categories for subsection
+	PageCat       []string   `json:"pagecat,omitempty"`       // Array of IAB content categories for page
+	PrivacyPolicy *int       `json:"privacypolicy,omitempty"` // Default: 1 ("1": has a privacy policy)
+	Publisher     *Publisher `json:"publisher,omitempty"`     // Details about the Publisher
+	Content       *Content   `json:"content,omitempty"`       // Details about the Content
+	Keywords      string     `json:"keywords,omitempty"`      // Comma separated list of keywords about the site.
+	Ext           Extension  `json:"ext,omitempty"`
 }
 
 // GetPrivacyPolicy returns the privacy policy value
@@ -30,18 +28,19 @@ func (a *Inventory) GetPrivacyPolicy() int {
 // "site" object.
 type App struct {
 	Inventory
-	Bundle   string `json:"bundle,omitempty"`   // App bundle or package name
-	StoreURL string `json:"storeurl,omitempty"` // App store URL for an installed app
-	Version  string `json:"ver,omitempty"`      // App version
-	Paid     int    `json:"paid,omitempty"`     // "1": Paid, "2": Free
+	Bundle   string    `json:"bundle,omitempty"`   // App bundle or package name
+	StoreURL string    `json:"storeurl,omitempty"` // App store URL for an installed app
+	Ver      string    `json:"ver,omitempty"`      // App version
+	Paid     int       `json:"paid,omitempty"`     // "1": Paid, "2": Free
+	Ext      Extension `json:"ext,omitempty"`      // Placeholder for exchange-specific extensions to OpenRTB
 }
 
 // Site object should be included if the ad supported content is part of a website (as opposed to
 // an application).  A bid request must not contain both a site object and an app object.
 type Site struct {
 	Inventory
-	Page     string `json:"page,omitempty"`   // URL of the page
-	Referrer string `json:"ref,omitempty"`    // Referrer URL
-	Search   string `json:"search,omitempty"` // Search string that caused naviation
-	Mobile   int    `json:"mobile,omitempty"` // Mobile ("1": site is mobile optimised)
+	Page   string `json:"page,omitempty"`   // URL of the page
+	Ref    string `json:"ref,omitempty"`    // Referrer URL
+	Search string `json:"search,omitempty"` // Search string that caused naviation
+	Mobile int    `json:"mobile,omitempty"` // Mobile ("1": site is mobile optimised)
 }
diff --git a/inventory_test.go b/inventory_test.go
index c5a011f..4bda401 100644
--- a/inventory_test.go
+++ b/inventory_test.go
@@ -4,7 +4,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestApp(t *testing.T) {
@@ -15,9 +15,9 @@ func TestApp(t *testing.T) {
 
 	exp := &App{
 		Inventory: Inventory{
-			ID:         "agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA",
-			Name:       "Yahoo Weather",
-			Categories: []ContentCategory{"weather", ContentCategoryScience, ContentCategoryWeather},
+			ID:   "agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA",
+			Name: "Yahoo Weather",
+			Cat:  []string{"weather", string(ContentCategoryScience), string(ContentCategoryWeather)},
 			Publisher: &Publisher{
 				ID:     "agltb3B1Yi1pbmNyDAsSA0FwcBiJkfTUCV",
 				Name:   "yahoo",
@@ -25,7 +25,7 @@ func TestApp(t *testing.T) {
 			},
 		},
 		Bundle:   "628677149",
-		Version:  "1.0.2",
+		Ver:      "1.0.2",
 		StoreURL: "https://itunes.apple.com/id628677149",
 	}
 	if got := subject; !reflect.DeepEqual(exp, got) {
@@ -41,9 +41,9 @@ func TestSite(t *testing.T) {
 
 	exp := &Site{
 		Inventory: Inventory{
-			ID:         "102855",
-			Categories: []ContentCategory{ContentCategoryAdvertising},
-			Domain:     "http://www.usabarfinder.com",
+			ID:     "102855",
+			Cat:    []string{string(ContentCategoryAdvertising)},
+			Domain: "http://www.usabarfinder.com",
 			Publisher: &Publisher{
 				ID:     "8953",
 				Name:   "local.com",
diff --git a/metric.go b/metric.go
new file mode 100644
index 0000000..ae2cb63
--- /dev/null
+++ b/metric.go
@@ -0,0 +1,12 @@
+package openrtb
+
+// Metric object is associated with an impression as an array of metrics. These metrics can offer insight into
+// the impression to assist with decisioning such as average recent viewability, click-through rate, etc. Each
+// metric is identified by its type, reports the value of the metric, and optionally identifies the source or
+// vendor measuring the value.
+type Metric struct {
+	Type   string    `json:"type"`
+	Value  float64   `json:"value"`
+	Vendor string    `json:"vendor"`
+	Ext    Extension `json:"ext,omitempty"`
+}
diff --git a/metric_test.go b/metric_test.go
new file mode 100644
index 0000000..3f256ec
--- /dev/null
+++ b/metric_test.go
@@ -0,0 +1,24 @@
+package openrtb_test
+
+import (
+	"reflect"
+	"testing"
+
+	. "github.com/UnityTech/openrtb/v3"
+)
+
+func TestMetric(t *testing.T) {
+	var subject *Metric
+	if err := fixture("metric", &subject); err != nil {
+		t.Fatalf("expected no error, got %v", err)
+	}
+
+	exp := &Metric{
+		Type:   "viewable_percentage",
+		Value:  0.99,
+		Vendor: "MOAT",
+	}
+	if got := subject; !reflect.DeepEqual(exp, got) {
+		t.Errorf("expected %+v, got %+v", exp, got)
+	}
+}
diff --git a/native.go b/native.go
index 5192ea7..6955055 100644
--- a/native.go
+++ b/native.go
@@ -1,7 +1,5 @@
 package openrtb
 
-import "encoding/json"
-
 // Native object represents a native type impression. Native ad units are intended to blend seamlessly into
 // the surrounding content (e.g., a sponsored Twitter or Facebook post). As such, the response must be
 // well-structured to afford the publisher fine-grained control over rendering.
@@ -10,9 +8,9 @@ import "encoding/json"
 // banner and/or video by also including as Imp subordinates the Banner and/or Video objects,
 // respectively. However, any given bid for the impression must conform to one of the offered types.
 type Native struct {
-	Request      json.RawMessage     `json:"request"`         // Request payload complying with the Native Ad Specification.
-	Version      string              `json:"ver,omitempty"`   // Version of the Native Ad Specification to which request complies; highly recommended for efficient parsing.
-	APIs         []APIFramework      `json:"api,omitempty"`   // List of supported API frameworks for this impression.
-	BlockedAttrs []CreativeAttribute `json:"battr,omitempty"` // Blocked creative attributes
-	Ext          json.RawMessage     `json:"ext,omitempty"`
+	Request Extension `json:"request"`         // Request payload complying with the Native Ad Specification.
+	Ver     string    `json:"ver,omitempty"`   // Version of the Native Ad Specification to which request complies; highly recommended for efficient parsing.
+	API     []int     `json:"api,omitempty"`   // List of supported API frameworks for this impression.
+	BAttr   []int     `json:"battr,omitempty"` // Blocked creative attributes
+	Ext     Extension `json:"ext,omitempty"`
 }
diff --git a/native/request/asset.go b/native/request/asset.go
index 6d0df9e..136ea25 100644
--- a/native/request/asset.go
+++ b/native/request/asset.go
@@ -1,17 +1,17 @@
 package request
 
-import "encoding/json"
+import "github.com/UnityTech/openrtb/v3"
 
 // Asset is the main container object for each asset requested or supported by Exchange
 // on behalf of the rendering client.  Only one of the {title,img,video,data}
 // objects should be present in each object.  The id is to be unique within the
 // AssetObject array so that the response can be aligned.
 type Asset struct {
-	ID       int             `json:"id"`                 // Unique asset ID, assigned by exchange
-	Required int             `json:"required,omitempty"` // Set to 1 if asset is required
-	Title    *Title          `json:"title,omitempty"`    // Title object for title assets
-	Image    *Image          `json:"img,omitempty"`      // Image object for image assets
-	Video    *Video          `json:"video,omitempty"`    // Video object for video assets
-	Data     *Data           `json:"data,omitempty"`     // Data object for brand name, description, ratings, prices etc.
-	Ext      json.RawMessage `json:"ext,omitempty"`
+	ID       int               `json:"id"`                 // Unique asset ID, assigned by exchange
+	Required int               `json:"required,omitempty"` // Set to 1 if asset is required
+	Title    *Title            `json:"title,omitempty"`    // Title object for title assets
+	Image    *Image            `json:"img,omitempty"`      // Image object for image assets
+	Video    *Video            `json:"video,omitempty"`    // Video object for video assets
+	Data     *Data             `json:"data,omitempty"`     // Data object for brand name, description, ratings, prices etc.
+	Ext      openrtb.Extension `json:"ext,omitempty"`
 }
diff --git a/native/request/data.go b/native/request/data.go
index 33f8ea0..66a86e2 100644
--- a/native/request/data.go
+++ b/native/request/data.go
@@ -1,6 +1,6 @@
 package request
 
-import "encoding/json"
+import "github.com/UnityTech/openrtb/v3"
 
 // DataTypeID enum.
 type DataTypeID int
@@ -24,7 +24,7 @@ const (
 
 // Data is the native data object.
 type Data struct {
-	TypeID DataTypeID      `json:"type"` // Type ID of the element supported by the publisher. The publisher can display this information in an appropriate format
-	Length int             `json:"len"`  // Maximum length of the text in the element’s response
-	Ext    json.RawMessage `json:"ext,omitempty"`
+	TypeID DataTypeID        `json:"type"` // Type ID of the element supported by the publisher. The publisher can display this information in an appropriate format
+	Length int               `json:"len"`  // Maximum length of the text in the element’s response
+	Ext    openrtb.Extension `json:"ext,omitempty"`
 }
diff --git a/native/request/image.go b/native/request/image.go
index 1f83323..8f950d7 100644
--- a/native/request/image.go
+++ b/native/request/image.go
@@ -1,6 +1,6 @@
 package request
 
-import "encoding/json"
+import "github.com/UnityTech/openrtb/v3"
 
 // ImageTypeID enum.
 type ImageTypeID int
@@ -22,6 +22,6 @@ type Image struct {
 	HeightMin int `json:"hmin,omitempty"` // The minimum requested height of the image in pixels
 	// Either h/w or hmin/wmin should be transmitted. If only h/w is included, it
 	// should be considered an exact requirement
-	MIMEs []string        `json:"mimes,omitempty"` // Whitelist of content MIME types supported
-	Ext   json.RawMessage `json:"ext,omitempty"`
+	Mimes []string          `json:"mimes,omitempty"` // Whitelist of content MIME types supported
+	Ext   openrtb.Extension `json:"ext,omitempty"`
 }
diff --git a/native/request/request.go b/native/request/request.go
index c1dcad9..8946010 100644
--- a/native/request/request.go
+++ b/native/request/request.go
@@ -1,6 +1,6 @@
 package request
 
-import "encoding/json"
+import "github.com/UnityTech/openrtb/v3"
 
 // LayoutID enum.
 type LayoutID int
@@ -36,19 +36,25 @@ const (
 	ContextTypeContent ContextTypeID = 1 // newsfeed, article, image gallery, video gallery
 	ContextTypeSocial  ContextTypeID = 2 // social network feed, email, chat
 	ContextTypeProduct ContextTypeID = 3 // product listings, details, recommendations, reviews
+)
+
+// ContextSubTypeID enum.
+type ContextSubTypeID int
 
-	ContextSubTypeGeneral        ContextTypeID = 10
-	ContextSubTypeArticle        ContextTypeID = 11
-	ContextSubTypeVideo          ContextTypeID = 12
-	ContextSubTypeAudio          ContextTypeID = 13
-	ContextSubTypeImage          ContextTypeID = 14
-	ContextSubTypeUserGenerated  ContextTypeID = 15
-	ContextSubTypeSocial         ContextTypeID = 20
-	ContextSubTypeEmail          ContextTypeID = 21
-	ContextSubTypeChat           ContextTypeID = 22
-	ContextSubTypeSelling        ContextTypeID = 30
-	ContextSubTypeAppStore       ContextTypeID = 31
-	ContextSubTypeProductReviews ContextTypeID = 32
+// ContextSubTypeID enum values.
+const (
+	ContextSubTypeGeneral        ContextSubTypeID = 10
+	ContextSubTypeArticle        ContextSubTypeID = 11
+	ContextSubTypeVideo          ContextSubTypeID = 12
+	ContextSubTypeAudio          ContextSubTypeID = 13
+	ContextSubTypeImage          ContextSubTypeID = 14
+	ContextSubTypeUserGenerated  ContextSubTypeID = 15
+	ContextSubTypeSocial         ContextSubTypeID = 20
+	ContextSubTypeEmail          ContextSubTypeID = 21
+	ContextSubTypeChat           ContextSubTypeID = 22
+	ContextSubTypeSelling        ContextSubTypeID = 30
+	ContextSubTypeAppStore       ContextSubTypeID = 31
+	ContextSubTypeProductReviews ContextSubTypeID = 32
 )
 
 // PlacementTypeID enum.
@@ -70,14 +76,14 @@ const (
 // banner and/or video by also including as Imp subordinates the Banner and/or Video objects,
 // respectively. However, any given bid for the impression must conform to one of the offered types.
 type Request struct {
-	Version          string          `json:"ver,omitempty"`            // Version of the Native Markup
-	LayoutID         LayoutID        `json:"layout,omitempty"`         // DEPRECATED The Layout ID of the native ad
-	AdUnitID         AdUnitID        `json:"adunit,omitempty"`         // DEPRECATED The Ad unit ID of the native ad
-	ContextTypeID    ContextTypeID   `json:"context,omitempty"`        // The context in which the ad appears
-	ContextSubTypeID ContextTypeID   `json:"contextsubtype,omitempty"` // A more detailed context in which the ad appears
-	PlacementTypeID  PlacementTypeID `json:"plcmttype,omitempty"`      // The design/format/layout of the ad unit being offered
-	PlacementCount   int             `json:"plcmtcnt,omitempty"`       // The number of identical placements in this Layout
-	Sequence         int             `json:"seq,omitempty"`            // 0 for the first ad, 1 for the second ad, and so on
-	Assets           []Asset         `json:"assets"`                   // An array of Asset Objects
-	Ext              json.RawMessage `json:"ext,omitempty"`
+	Ver              string            `json:"ver,omitempty"`            // Version of the Native Markup
+	LayoutID         LayoutID          `json:"layout,omitempty"`         // DEPRECATED The Layout ID of the native ad
+	AdUnitID         AdUnitID          `json:"adunit,omitempty"`         // DEPRECATED The Ad unit ID of the native ad
+	ContextTypeID    ContextTypeID     `json:"context,omitempty"`        // The context in which the ad appears
+	ContextSubTypeID ContextSubTypeID  `json:"contextsubtype,omitempty"` // A more detailed context in which the ad appears
+	PlacementTypeID  PlacementTypeID   `json:"plcmttype,omitempty"`      // The design/format/layout of the ad unit being offered
+	PlacementCount   int               `json:"plcmtcnt,omitempty"`       // The number of identical placements in this Layout
+	Sequence         int               `json:"seq,omitempty"`            // 0 for the first ad, 1 for the second ad, and so on
+	Assets           []Asset           `json:"assets"`                   // An array of Asset Objects
+	Ext              openrtb.Extension `json:"ext,omitempty"`
 }
diff --git a/native/request/request_test.go b/native/request/request_test.go
index a96b847..8bd33c2 100644
--- a/native/request/request_test.go
+++ b/native/request/request_test.go
@@ -6,8 +6,8 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/bsm/openrtb/v3"
-	. "github.com/bsm/openrtb/v3/native/request"
+	"github.com/UnityTech/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3/native/request"
 )
 
 func TestRequest(t *testing.T) {
@@ -17,7 +17,7 @@ func TestRequest(t *testing.T) {
 	}
 
 	exp := &Request{
-		Version:          "1.1",
+		Ver:              "1.1",
 		ContextTypeID:    ContextTypeSocial,
 		ContextSubTypeID: ContextSubTypeSocial,
 		PlacementTypeID:  PlacementTypeID(11),
@@ -25,10 +25,10 @@ func TestRequest(t *testing.T) {
 		Sequence:         2,
 		Assets: []Asset{
 			{ID: 123, Required: 1, Title: &Title{Length: 140}},
-			{ID: 128, Image: &Image{TypeID: ImageTypeMain, WidthMin: 836, HeightMin: 627, Width: 1000, Height: 800, MIMEs: []string{"image/jpg"}}},
+			{ID: 128, Image: &Image{TypeID: ImageTypeMain, WidthMin: 836, HeightMin: 627, Width: 1000, Height: 800, Mimes: []string{"image/jpg"}}},
 			{ID: 126, Required: 1, Data: &Data{TypeID: DataTypeSponsored, Length: 25}},
 			{ID: 127, Required: 1, Data: &Data{TypeID: DataTypeDesc, Length: 140}},
-			{ID: 4, Video: &Video{MinDuration: 15, MaxDuration: 30, Protocols: []openrtb.Protocol{openrtb.ProtocolVAST2, openrtb.ProtocolVAST3}, MIMEs: []string{"video/mp4"}}},
+			{ID: 4, Video: &Video{MinDuration: 15, MaxDuration: 30, Protocols: []int{openrtb.VideoProtoVAST2, openrtb.VideoProtoVAST3}, Mimes: []string{"video/mp4"}}},
 		},
 	}
 	if got := subject; !reflect.DeepEqual(exp, got) {
diff --git a/native/request/title.go b/native/request/title.go
index a539af1..bdaa000 100644
--- a/native/request/title.go
+++ b/native/request/title.go
@@ -1,9 +1,9 @@
 package request
 
-import "encoding/json"
+import "github.com/UnityTech/openrtb/v3"
 
 // Title is the native title object.
 type Title struct {
-	Length int             `json:"len"` // Maximum length of the text in the title element
-	Ext    json.RawMessage `json:"ext,omitempty"`
+	Length int               `json:"len"` // Maximum length of the text in the title element
+	Ext    openrtb.Extension `json:"ext,omitempty"`
 }
diff --git a/native/request/video.go b/native/request/video.go
index f9b3a4c..a59cb65 100644
--- a/native/request/video.go
+++ b/native/request/video.go
@@ -1,16 +1,13 @@
 package request
 
-import (
-	"encoding/json"
-
-	"github.com/bsm/openrtb/v3"
-)
+import "github.com/UnityTech/openrtb/v3"
 
 // Video is the native video object.
+// TODO unclear if its the same as imp.video https://github.com/openrtb/OpenRTB/issues/26
 type Video struct {
-	MIMEs       []string           `json:"mimes,omitempty"`       // Whitelist of content MIME types supported
-	MinDuration int                `json:"minduration,omitempty"` // Minimum video ad duration in seconds
-	MaxDuration int                `json:"maxduration,omitempty"` // Maximum video ad duration in seconds
-	Protocols   []openrtb.Protocol `json:"protocols,omitempty"`   // Video bid response protocols
-	Ext         json.RawMessage    `json:"ext,omitempty"`
+	Mimes       []string          `json:"mimes,omitempty"`       // Whitelist of content MIME types supported
+	MinDuration int               `json:"minduration,omitempty"` // Minimum video ad duration in seconds
+	MaxDuration int               `json:"maxduration,omitempty"` // Maximum video ad duration in seconds
+	Protocols   []int             `json:"protocols,omitempty"`   // Video bid response protocols
+	Ext         openrtb.Extension `json:"ext,omitempty"`
 }
diff --git a/native/response/asset.go b/native/response/asset.go
index 4f0c0ea..d0846a0 100644
--- a/native/response/asset.go
+++ b/native/response/asset.go
@@ -1,6 +1,6 @@
 package response
 
-import "encoding/json"
+import "github.com/UnityTech/openrtb/v3"
 
 // Asset corresponds to the Asset Object in the request. The main container object for
 // each asset requested or supported by Exchange on behalf of the rendering
@@ -9,12 +9,12 @@ import "encoding/json"
 // should be null/absent. The id is to be unique within the AssetObject array so
 // that the response can be aligned.
 type Asset struct {
-	ID       int             `json:"id"`                 // Unique asset ID, assigned by exchange, must match one of the asset IDs in request
-	Required int             `json:"required,omitempty"` // Set to 1 if asset is required
-	Title    *Title          `json:"title,omitempty"`    // Title object for title assets
-	Image    *Image          `json:"img,omitempty"`      // Image object for image assets
-	Video    *Video          `json:"video,omitempty"`    // Video object for video assets
-	Data     *Data           `json:"data,omitempty"`     // Data object for brand name, description, ratings, prices etc.
-	Link     *Link           `json:"link,omitempty"`     // Link object for call to actions. The link object applies if the asset item is activated (clicked)
-	Ext      json.RawMessage `json:"ext,omitempty"`
+	ID       int               `json:"id"`                 // Unique asset ID, assigned by exchange, must match one of the asset IDs in request
+	Required int               `json:"required,omitempty"` // Set to 1 if asset is required
+	Title    *Title            `json:"title,omitempty"`    // Title object for title assets
+	Image    *Image            `json:"img,omitempty"`      // Image object for image assets
+	Video    *Video            `json:"video,omitempty"`    // Video object for video assets
+	Data     *Data             `json:"data,omitempty"`     // Data object for brand name, description, ratings, prices etc.
+	Link     *Link             `json:"link,omitempty"`     // Link object for call to actions. The link object applies if the asset item is activated (clicked)
+	Ext      openrtb.Extension `json:"ext,omitempty"`
 }
diff --git a/native/response/data.go b/native/response/data.go
index 20c9bd2..b90136e 100644
--- a/native/response/data.go
+++ b/native/response/data.go
@@ -1,10 +1,10 @@
 package response
 
-import "encoding/json"
+import "github.com/UnityTech/openrtb/v3"
 
 // Data object contains response data.
 type Data struct {
-	Label string          `json:"label,omitempty"` // The optional formatted string name of the data type to be displayed
-	Value string          `json:"value"`           // The formatted string of data to be displayed. Can contain a formatted value such as “5 stars” or “$10” or “3.4 stars out of 5”
-	Ext   json.RawMessage `json:"ext,omitempty"`
+	Label string            `json:"label,omitempty"` // The optional formatted string name of the data type to be displayed
+	Value string            `json:"value"`           // The formatted string of data to be displayed. Can contain a formatted value such as “5 stars” or “$10” or “3.4 stars out of 5”
+	Ext   openrtb.Extension `json:"ext,omitempty"`
 }
diff --git a/native/response/image.go b/native/response/image.go
index de4b5b8..276f250 100644
--- a/native/response/image.go
+++ b/native/response/image.go
@@ -1,11 +1,11 @@
 package response
 
-import "encoding/json"
+import "github.com/UnityTech/openrtb/v3"
 
 // Image object contains response image.
 type Image struct {
-	URL    string          `json:"url,omitempty"` // URL of the image asset
-	Width  int             `json:"w,omitempty"`   // Width of the image in pixels
-	Height int             `json:"h,omitempty"`   // Height of the image in pixels
-	Ext    json.RawMessage `json:"ext,omitempty"`
+	URL    string            `json:"url,omitempty"` // URL of the image asset
+	Width  int               `json:"w,omitempty"`   // Width of the image in pixels
+	Height int               `json:"h,omitempty"`   // Height of the image in pixels
+	Ext    openrtb.Extension `json:"ext,omitempty"`
 }
diff --git a/native/response/link.go b/native/response/link.go
index 6743ed6..5bf5798 100644
--- a/native/response/link.go
+++ b/native/response/link.go
@@ -1,11 +1,11 @@
 package response
 
-import "encoding/json"
+import "github.com/UnityTech/openrtb/v3"
 
 // Link object contains response link.
 type Link struct {
-	URL           string          `json:"url"`                     // Landing URL of the clickable link
-	ClickTrackers []string        `json:"clicktrackers,omitempty"` // List of third-party tracker URLs to be fired on click of the URL
-	FallbackURL   string          `json:"fallback,omitempty"`      // Fallback URL for deeplink. To be used if the URL given in url is not supported by the device.
-	Ext           json.RawMessage `json:"ext,omitempty"`
+	URL           string            `json:"url"`                     // Landing URL of the clickable link
+	ClickTrackers []string          `json:"clicktrackers,omitempty"` // List of third-party tracker URLs to be fired on click of the URL
+	FallbackURL   string            `json:"fallback,omitempty"`      // Fallback URL for deeplink. To be used if the URL given in url is not supported by the device.
+	Ext           openrtb.Extension `json:"ext,omitempty"`
 }
diff --git a/native/response/response.go b/native/response/response.go
index 500464d..9d6ea06 100644
--- a/native/response/response.go
+++ b/native/response/response.go
@@ -1,17 +1,13 @@
 package response
 
-import (
-	"encoding/json"
-
-	"github.com/bsm/openrtb/v3"
-)
+import "github.com/UnityTech/openrtb/v3"
 
 // Response is the native object is the top level JSON object which identifies a native response.
 type Response struct {
-	Version     openrtb.StringOrNumber `json:"ver,omitempty"`         // Version of the Native Markup
+	Ver         openrtb.StringOrNumber `json:"ver,omitempty"`         // Version of the Native Markup
 	Assets      []Asset                `json:"assets"`                // An array of Asset Objects
 	Link        Link                   `json:"link"`                  // Destination Link. This is default link object for the ad
 	ImpTrackers []string               `json:"imptrackers,omitempty"` // Array of impression tracking URLs, expected to return a 1x1 image or 204 response
 	JSTracker   string                 `json:"jstracker,omitempty"`   // Optional JavaScript impression tracker. This is a valid HTML, Javascript is already wrapped in <script> tags. It should be executed at impression time where it can be supported
-	Ext         json.RawMessage        `json:"ext,omitempty"`
+	Ext         openrtb.Extension      `json:"ext,omitempty"`
 }
diff --git a/native/response/response_test.go b/native/response/response_test.go
index c257bc0..908524d 100644
--- a/native/response/response_test.go
+++ b/native/response/response_test.go
@@ -6,7 +6,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3/native/response"
+	. "github.com/UnityTech/openrtb/v3/native/response"
 )
 
 func TestResponse(t *testing.T) {
@@ -16,7 +16,7 @@ func TestResponse(t *testing.T) {
 	}
 
 	exp := &Response{
-		Version:     "1.1",
+		Ver:         "1.1",
 		ImpTrackers: []string{"http://imptracker.com"},
 		JSTracker:   "<script>track()</script>",
 		Link: Link{
diff --git a/native/response/title.go b/native/response/title.go
index b0c71ac..fda13d3 100644
--- a/native/response/title.go
+++ b/native/response/title.go
@@ -1,9 +1,9 @@
 package response
 
-import "encoding/json"
+import "github.com/UnityTech/openrtb/v3"
 
 // Title wraps title information.
 type Title struct {
-	Text string          `json:"text"` // The text associated with the text element
-	Ext  json.RawMessage `json:"ext,omitempty"`
+	Text string            `json:"text"` // The text associated with the text element
+	Ext  openrtb.Extension `json:"ext,omitempty"`
 }
diff --git a/native_test.go b/native_test.go
index 4ff6315..57e43cf 100644
--- a/native_test.go
+++ b/native_test.go
@@ -1,11 +1,10 @@
 package openrtb_test
 
 import (
-	"encoding/json"
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestNative(t *testing.T) {
@@ -15,8 +14,8 @@ func TestNative(t *testing.T) {
 	}
 
 	exp := &Native{
-		Request: json.RawMessage(`"PAYLOAD"`),
-		Version: "2",
+		Request: Extension(`"PAYLOAD"`),
+		Ver:     "2",
 	}
 	if got := subject; !reflect.DeepEqual(exp, got) {
 		t.Errorf("expected %+v, got %+v", exp, got)
diff --git a/numbers.go b/numbers.go
index 515f6ff..46ee57b 100644
--- a/numbers.go
+++ b/numbers.go
@@ -2,6 +2,7 @@ package openrtb
 
 import (
 	"encoding/json"
+	"errors"
 	"strconv"
 )
 
@@ -51,3 +52,27 @@ func (n *StringOrNumber) UnmarshalJSON(data []byte) error {
 	}
 	return nil
 }
+
+// BoolOrNumber attemps to fix OpenRTB incompatibilities where a field is expected as bool but the spec expects int values. This was not seen till now, but some mediation partners will follow the spec closely.
+type BoolOrNumber bool
+
+// UnmarshalJSON implements json.Unmarshaler
+func (b *BoolOrNumber) UnmarshalJSON(data []byte) error {
+	var val interface{}
+	if err := json.Unmarshal(data, &val); err != nil {
+		return err
+	}
+
+	switch v := val.(type) {
+	case bool:
+		*b = BoolOrNumber(v)
+	case float64:
+		// When unmarshaling JSON into an interface value, Unmarshal stores JSON numbers in the interface value float64
+		*b = BoolOrNumber(v != 0)
+	default:
+
+		return errors.New("BoolOrInt: invalid type")
+	}
+
+	return nil
+}
diff --git a/numbers_test.go b/numbers_test.go
index a5d8549..543ad6c 100644
--- a/numbers_test.go
+++ b/numbers_test.go
@@ -4,7 +4,7 @@ import (
 	"encoding/json"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestNumberOrString(t *testing.T) {
diff --git a/openrtb.go b/openrtb.go
index 330de38..ed67aac 100644
--- a/openrtb.go
+++ b/openrtb.go
@@ -1,7 +1,5 @@
 package openrtb
 
-import "encoding/json"
-
 // ContentCategory as defined in section 5.1
 type ContentCategory string
 
@@ -426,181 +424,142 @@ const (
 	ContentCategoryCopyrightInfringement ContentCategory = "IAB26-4"
 )
 
-// BannerType as defined in section 5.2.
-type BannerType int
-
 // 5.2 Banner Ad Types
 const (
-	BannerTypeXHTMLText BannerType = 1
-	BannerTypeXHTML     BannerType = 2
-	BannerTypeJS        BannerType = 3
-	BannerTypeFrame     BannerType = 4
+	BannerTypeXHTMLText = 1
+	BannerTypeXHTML     = 2
+	BannerTypeJS        = 3
+	BannerTypeFrame     = 4
 )
 
-// CreativeAttribute as defined in section 5.3.
-type CreativeAttribute int
-
 // 5.3 Creative Attributes
 const (
-	CreativeAttributeAudioAdAutoPlay                 CreativeAttribute = 1
-	CreativeAttributeAudioAdUserInitiated            CreativeAttribute = 2
-	CreativeAttributeExpandableAuto                  CreativeAttribute = 3
-	CreativeAttributeExpandableUserInitiatedClick    CreativeAttribute = 4
-	CreativeAttributeExpandableUserInitiatedRollover CreativeAttribute = 5
-	CreativeAttributeInBannerVideoAdAutoPlay         CreativeAttribute = 6
-	CreativeAttributeInBannerVideoAdUserInitiated    CreativeAttribute = 7
-	CreativeAttributePop                             CreativeAttribute = 8
-	CreativeAttributeProvocativeOrSuggestiveImagery  CreativeAttribute = 9
-	CreativeAttributeExtremeAnimation                CreativeAttribute = 10
-	CreativeAttributeSurveys                         CreativeAttribute = 11
-	CreativeAttributeTextOnly                        CreativeAttribute = 12
-	CreativeAttributeUserInitiated                   CreativeAttribute = 13
-	CreativeAttributeWindowsDialogOrAlert            CreativeAttribute = 14
-	CreativeAttributeHasAudioWithPlayer              CreativeAttribute = 15
-	CreativeAttributeAdProvidesSkipButton            CreativeAttribute = 16
-	CreativeAttributeAdobeFlash                      CreativeAttribute = 17
+	CreativeAttributeAudioAdAutoPlay                 = 1
+	CreativeAttributeAudioAdUserInitiated            = 2
+	CreativeAttributeExpandableAuto                  = 3
+	CreativeAttributeExpandableUserInitiatedClick    = 4
+	CreativeAttributeExpandableUserInitiatedRollover = 5
+	CreativeAttributeInBannerVideoAdAutoPlay         = 6
+	CreativeAttributeInBannerVideoAdUserInitiated    = 7
+	CreativeAttributePop                             = 8
+	CreativeAttributeProvocativeOrSuggestiveImagery  = 9
+	CreativeAttributeExtremeAnimation                = 10
+	CreativeAttributeSurveys                         = 11
+	CreativeAttributeTextOnly                        = 12
+	CreativeAttributeUserInitiated                   = 13
+	CreativeAttributeWindowsDialogOrAlert            = 14
+	CreativeAttributeHasAudioWithPlayer              = 15
+	CreativeAttributeAdProvidesSkipButton            = 16
+	CreativeAttributeAdobeFlash                      = 17
 )
 
-// AdPosition as defined in section 5.4.
-type AdPosition int
-
 // 5.4 Ad Position
 const (
-	AdPositionUnknown    AdPosition = 0
-	AdPositionAboveFold  AdPosition = 1
-	AdPositionBelowFold  AdPosition = 3
-	AdPositionHeader     AdPosition = 4
-	AdPositionFooter     AdPosition = 5
-	AdPositionSidebar    AdPosition = 6
-	AdPositionFullscreen AdPosition = 7
+	AdPosUnknown    = 0
+	AdPosAboveFold  = 1
+	AdPosBelowFold  = 3
+	AdPosHeader     = 4
+	AdPosFooter     = 5
+	AdPosSidebar    = 6
+	AdPosFullscreen = 7
 )
 
-// ExpDir as defined in section 5.5.
-type ExpDir int
-
 // 5.5 Expandable Direction
 const (
-	ExpDirUnknown    ExpDir = 0
-	ExpDirLeft       ExpDir = 1
-	ExpDirRight      ExpDir = 2
-	ExpDirUp         ExpDir = 3
-	ExpDirDown       ExpDir = 4
-	ExpDirFullScreen ExpDir = 5
+	ExpDirUnknown    = 0
+	ExpDirLeft       = 1
+	ExpDirRight      = 2
+	ExpDirUp         = 3
+	ExpDirDown       = 4
+	ExpDirFullScreen = 5
 )
 
-// APIFramework as defined in Adcom1.0.
-type APIFramework int
-
-// API Frameworks options as defined in Adcom1.0.
+// 5.6 API Frameworks
 const (
-	APIFrameworkUnknown APIFramework = 0
-	APIFrameworkVPAID1  APIFramework = 1
-	APIFrameworkVPAID2  APIFramework = 2
-	APIFrameworkMRAID1  APIFramework = 3
-	APIFrameworkORMMA   APIFramework = 4
-	APIFrameworkMRAID2  APIFramework = 5
-	APIFrameworkMRAID3  APIFramework = 6
-	APIFrameworkOMID    APIFramework = 7
-	APIFrameworkSIMID1  APIFramework = 8
-	APIFrameworkSIMID11 APIFramework = 9
+	APIFrameworkUnknown = 0
+	APIFrameworkVPAID1  = 1
+	APIFrameworkVPAID2  = 2
+	APIFrameworkMRAID1  = 3
+	APIFrameworkORMMA   = 4
+	APIFrameworkMRAID2  = 5
+	APIFrameworkMRAID3  = 6
+	APIFrameworkOMID1   = 7
+	APIFrameworkSIMID1  = 8
+	APIFrameworkSIMID11 = 9
 )
 
-// VideoLinearity as defined in section 5.7.
-type VideoLinearity int
-
 // 5.7 Video Linearity
 const (
-	VideoLinearityUnknown   VideoLinearity = 0
-	VideoLinearityLinear    VideoLinearity = 1
-	VideoLinearityNonLinear VideoLinearity = 2
+	VideoLinearityUnknown   = 0
+	VideoLinearityLinear    = 1
+	VideoLinearityNonLinear = 2
 )
 
-// Protocol as defined in section 5.8.
-type Protocol int
-
 // 5.8 Video and Audio Bid Response Protocols
 const (
-	ProtocolUnknown       Protocol = 0
-	ProtocolVAST1         Protocol = 1
-	ProtocolVAST2         Protocol = 2
-	ProtocolVAST3         Protocol = 3
-	ProtocolVAST1Wrapper  Protocol = 4
-	ProtocolVAST2Wrapper  Protocol = 5
-	ProtocolVAST3Wrapper  Protocol = 6
-	ProtocolVAST4         Protocol = 7
-	ProtocolVAST4Wrapper  Protocol = 8
-	ProtocolDAAST1        Protocol = 9
-	ProtocolDAAST1Wrapper Protocol = 10
+	ProtocolUnknown            = 0
+	VideoProtoVAST1            = 1
+	VideoProtoVAST2            = 2
+	VideoProtoVAST3            = 3
+	VideoProtoVAST1Wrapper     = 4
+	VideoProtoVAST2Wrapper     = 5
+	VideoProtoVAST3Wrapper     = 6
+	VideoProtoVAST4            = 7
+	VideoProtoVAST4Wrapper     = 8
+	AudioProtocolDAAST1        = 9
+	AudioProtocolDAAST1Wrapper = 10
 )
 
-// VideoPlacement as defined in section 5.9.
-type VideoPlacement int
-
-// Video Placement Types
+// 5.9 Video Placement Types
 const (
-	VideoPlacementUnknown      VideoPlacement = 0
-	VideoPlacementInStream     VideoPlacement = 1
-	VideoPlacementInBanner     VideoPlacement = 2
-	VideoPlacementInArticle    VideoPlacement = 3
-	VideoPlacementInFeed       VideoPlacement = 4
-	VideoPlacementInterstitial VideoPlacement = 5
+	VideoPlacementUnknown      = 0
+	VideoPlacementInStream     = 1
+	VideoPlacementInBanner     = 2
+	VideoPlacementInArticle    = 3
+	VideoPlacementInFeed       = 4
+	VideoPlacementInterstitial = 5
 )
 
-// VideoPlayback as defined in section 5.10.
-type VideoPlayback int
-
 // 5.10 Video Playback Methods
 const (
-	VideoPlaybackUnknown          VideoPlayback = 0
-	VideoPlaybackPageLoadSoundOn  VideoPlayback = 1
-	VideoPlaybackPageLoadSoundOff VideoPlayback = 2
-	VideoPlaybackClickToPlay      VideoPlayback = 3
-	VideoPlaybackMouseOver        VideoPlayback = 4
-	VideoPlaybackEnterSoundOn     VideoPlayback = 5
-	VideoPlaybackEnterSoundOff    VideoPlayback = 6
+	VideoPlaybackUnknown       = 0
+	VideoPlaybackAutoSoundOn   = 1
+	VideoPlaybackAutoSoundOff  = 2
+	VideoPlaybackClickToPlay   = 3
+	VideoPlaybackMouseOver     = 4
+	VideoPlaybackEnterSoundOn  = 5
+	VideoPlaybackEnterSoundOff = 6
 )
 
-// StartDelay as defined in section 5.12.
-type StartDelay int
-
 // 5.12 Video Start Delay
 const (
-	StartDelayPreRoll         StartDelay = 0
-	StartDelayGenericMidRoll  StartDelay = -1
-	StartDelayGenericPostRoll StartDelay = -2
+	VideoStartDelayPreRoll         = 0
+	VideoStartDelayGenericMidRoll  = -1
+	VideoStartDelayGenericPostRoll = -2
 )
 
-// ProductionQuality as defined in section 5.13.
-type ProductionQuality int
-
 // 5.13 Video Quality
 const (
-	ProductionQualityUnknown      ProductionQuality = 0
-	ProductionQualityProfessional ProductionQuality = 1
-	ProductionQualityProsumer     ProductionQuality = 2
-	ProductionQualityUGC          ProductionQuality = 3
+	VideoQualityUnknown      = 0
+	VideoQualityProfessional = 1
+	VideoQualityProsumer     = 2
+	VideoQualityUGC          = 3
 )
 
-// CompanionType as defined in section 5.14.
-type CompanionType int
-
-// 5.14  Companion Types
+// 5.14 Companion Types
 const (
-	CompanionTypeUnknown CompanionType = 0
-	CompanionTypeStatic  CompanionType = 1
-	CompanionTypeHTML    CompanionType = 2
-	CompanionTypeIFrame  CompanionType = 3
+	VASTCompanionUnknown = 0
+	VASTCompanionStatic  = 1
+	VASTCompanionHTML    = 2
+	VASTCompanionIFrame  = 3
 )
 
-// ContentDelivery as defined in section 5.15.
-type ContentDelivery int
-
 // 5.15 Content Delivery Methods
 const (
-	ContentDeliveryUnknown     ContentDelivery = 0
-	ContentDeliveryStreaming   ContentDelivery = 1
-	ContentDeliveryProgressive ContentDelivery = 2
-	ContentDeliveryDownload    ContentDelivery = 3
+	ContentDeliveryUnknown     = 0
+	ContentDeliveryStreaming   = 1
+	ContentDeliveryProgressive = 2
+	ContentDeliveryDownload    = 3
 )
 
 // FeedType as defined in section 5.16.
@@ -626,87 +585,56 @@ const (
 	VolumeNormCustom   VolumeNorm = 4
 )
 
-// ContentContext as defined in section 5.18.
-type ContentContext int
-
-// UnmarshalJSON implements json.Unmarshaler
-func (n *ContentContext) UnmarshalJSON(data []byte) (err error) {
-	var v int
-
-	if len(data) > 2 && data[0] == '"' && data[len(data)-1] == '"' {
-		err = json.Unmarshal(data[1:len(data)-1], &v)
-	} else {
-		err = json.Unmarshal(data, &v)
-	}
-	if err != nil {
-		return err
-	}
-
-	*n = ContentContext(v)
-	return nil
-}
-
 // 5.18 Content Context
 const (
-	ContentContextVideo       ContentContext = 1
-	ContentContextGame        ContentContext = 2
-	ContentContextMusic       ContentContext = 3
-	ContentContextApplication ContentContext = 4
-	ContentContextText        ContentContext = 5
-	ContentContextOther       ContentContext = 6
-	ContentContextUnknown     ContentContext = 7
+	ContextVideo       = 1
+	ContextGame        = 2
+	ContextMusic       = 3
+	ContextApplication = 4
+	ContextText        = 5
+	ContextOther       = 6
+	ContextUnknown     = 7
 )
 
-// IQGRating as defined in section 5.19.
-type IQGRating int
-
 // 5.19 IQG Media Ratings
 const (
-	IQGRatingUnknown IQGRating = 0
-	IQGRatingAll     IQGRating = 1
-	IQGRatingOver12  IQGRating = 2
-	IQGRatingMature  IQGRating = 3
+	QAGUnknown = 0
+	QAGAll     = 1
+	QAGOver12  = 2
+	QAGMature  = 3
 )
 
-// LocationType as defined in section 5.20.
-type LocationType int
-
 // 5.20 Location Type
 const (
-	LocationTypeUnknown LocationType = 0
-	LocationTypeGPS     LocationType = 1
-	LocationTypeIP      LocationType = 2
-	LocationTypeUser    LocationType = 3
+	LocationTypeUnknown = 0
+	LocationTypeGPS     = 1
+	LocationTypeIP      = 2
+	LocationTypeUser    = 3
 )
 
-// DeviceType as defined in section 5.21.
-type DeviceType int
-
 // 5.21 Device Type
 const (
-	DeviceTypeUnknown   DeviceType = 0
-	DeviceTypeMobile    DeviceType = 1
-	DeviceTypePC        DeviceType = 2
-	DeviceTypeTV        DeviceType = 3
-	DeviceTypePhone     DeviceType = 4
-	DeviceTypeTablet    DeviceType = 5
-	DeviceTypeConnected DeviceType = 6
-	DeviceTypeSetTopBox DeviceType = 7
-	DeviceTypeOOH       DeviceType = 8
+	DeviceTypeUnknown   = 0
+	DeviceTypeMobile    = 1
+	DeviceTypePC        = 2
+	DeviceTypeTV        = 3
+	DeviceTypePhone     = 4
+	DeviceTypeTablet    = 5
+	DeviceTypeConnected = 6
+	DeviceTypeSetTopBox = 7
+	DeviceTypeOOH       = 8
 )
 
-// ConnType as defined in section 5.22.
-type ConnType int
-
 // 5.22 Connection Type
 const (
-	ConnTypeUnknown  ConnType = 0
-	ConnTypeEthernet ConnType = 1
-	ConnTypeWIFI     ConnType = 2
-	ConnTypeCell     ConnType = 3
-	ConnTypeCell2G   ConnType = 4
-	ConnTypeCell3G   ConnType = 5
-	ConnTypeCell4G   ConnType = 6
+	ConnTypeUnknown  = 0
+	ConnTypeEthernet = 1
+	ConnTypeWIFI     = 2
+	ConnTypeCell     = 3
+	ConnTypeCell2G   = 4
+	ConnTypeCell3G   = 5
+	ConnTypeCell4G   = 6
+	ConnTypeCell5G   = 7
 )
 
 // IPLocation as defined in section 5.23.
@@ -721,20 +649,17 @@ const (
 	IPLocationNetAquity   IPLocation = 4
 )
 
-// NBR as defined in section 5.24.
-type NBR int
-
 // 5.24 No-Bid Reason Codes
 const (
-	NBRUnknownError      NBR = 0
-	NBRTechnicalError    NBR = 1
-	NBRInvalidRequest    NBR = 2
-	NBRKnownSpider       NBR = 3
-	NBRSuspectedNonHuman NBR = 4
-	NBRProxyIP           NBR = 5
-	NBRUnsupportedDevice NBR = 6
-	NBRBlockedSite       NBR = 7
-	NBRUnmatchedUser     NBR = 8
+	NBRUnknownError      = 0
+	NBRTechnicalError    = 1
+	NBRInvalidRequest    = 2
+	NBRKnownSpider       = 3
+	NBRSuspectedNonHuman = 4
+	NBRProxyIP           = 5
+	NBRUnsupportedDevice = 6
+	NBRBlockedSite       = 7
+	NBRUnmatchedUser     = 8
 )
 
 /*************************************************************************
@@ -751,11 +676,11 @@ const (
 
 // ThirdParty abstract attributes.
 type ThirdParty struct {
-	ID         string            `json:"id,omitempty"`
-	Name       string            `json:"name,omitempty"`
-	Categories []ContentCategory `json:"cat,omitempty"` // Array of IAB content categories
-	Domain     string            `json:"domain,omitempty"`
-	Ext        json.RawMessage   `json:"ext,omitempty"`
+	ID     string    `json:"id,omitempty"`
+	Name   string    `json:"name,omitempty"`
+	Cat    []string  `json:"cat,omitempty"` // Array of IAB content categories
+	Domain string    `json:"domain,omitempty"`
+	Ext    Extension `json:"ext,omitempty"`
 }
 
 // Publisher object itself and all of its parameters are optional, so default values are not
@@ -773,20 +698,20 @@ type Producer ThirdParty
 // (such as IP geo lookup), or by user registration information (for example provided to a publisher
 // through a user registration).
 type Geo struct {
-	Latitude      float64         `json:"lat,omitempty"`           // Latitude from -90 to 90
-	Longitude     float64         `json:"lon,omitempty"`           // Longitude from -180 to 180
-	Type          LocationType    `json:"type,omitempty"`          // Indicate the source of the geo data
-	Accuracy      int             `json:"accuracy,omitempty"`      // Estimated location accuracy in meters; recommended when lat/lon are specified and derived from a device’s location services
-	LastFix       int             `json:"lastfix,omitempty"`       // Number of seconds since this geolocation fix was established.
-	IPService     IPLocation      `json:"ipservice,omitempty"`     // Service or provider used to determine geolocation from IP address if applicable
-	Country       string          `json:"country,omitempty"`       // Country using ISO 3166-1 Alpha 3
-	Region        string          `json:"region,omitempty"`        // Region using ISO 3166-2
-	RegionFIPS104 string          `json:"regionFIPS104,omitempty"` // Region of a country using FIPS 10-4
-	Metro         string          `json:"metro,omitempty"`
-	City          string          `json:"city,omitempty"`
-	ZIP           string          `json:"zip,omitempty"`
-	UTCOffset     int             `json:"utcoffset,omitempty"` // Local time as the number +/- of minutes from UTC
-	Ext           json.RawMessage `json:"ext,omitempty"`
+	Lat           float64   `json:"lat,omitempty"`           // Latitude from -90 to 90
+	Lon           float64   `json:"lon,omitempty"`           // Longitude from -180 to 180
+	Type          int       `json:"type,omitempty"`          // Indicate the source of the geo data
+	Accuracy      int       `json:"accuracy,omitempty"`      // Estimated location accuracy in meters; recommended when lat/lon are specified and derived from a device’s location services
+	LastFix       int       `json:"lastfix,omitempty"`       // Number of seconds since this geolocation fix was established.
+	IPService     int       `json:"ipservice,omitempty"`     // Service or provider used to determine geolocation from IP address if applicable
+	Country       string    `json:"country,omitempty"`       // Country using ISO 3166-1 Alpha 3
+	Region        string    `json:"region,omitempty"`        // Region using ISO 3166-2
+	RegionFIPS104 string    `json:"regionFIPS104,omitempty"` // Region of a country using FIPS 10-4
+	Metro         string    `json:"metro,omitempty"`
+	City          string    `json:"city,omitempty"`
+	Zip           string    `json:"zip,omitempty"`
+	UTCOffset     int       `json:"utcoffset,omitempty"` // Local time as the number +/- of minutes from UTC
+	Ext           Extension `json:"ext,omitempty"`
 }
 
 // User object contains information known or derived about the human user of the device (i.e., the
@@ -794,16 +719,18 @@ type Geo struct {
 // privacy policies. However, this user ID must be stable long enough to serve reasonably as the basis for
 // frequency capping and retargeting.
 type User struct {
-	ID          string          `json:"id,omitempty"`         // Unique consumer ID of this user on the exchange
-	BuyerID     string          `json:"buyerid,omitempty"`    // Buyer-specific ID for the user as mapped by the exchange for the buyer. At least one of buyeruid/buyerid or id is recommended. Valid for OpenRTB 2.3.
-	BuyerUID    string          `json:"buyeruid,omitempty"`   // Buyer-specific ID for the user as mapped by the exchange for the buyer. Same as BuyerID but valid for OpenRTB 2.2.
-	YearOfBirth int             `json:"yob,omitempty"`        // Year of birth as a 4-digit integer.
-	Gender      string          `json:"gender,omitempty"`     // Gender ("M": male, "F" female, "O" Other)
-	Keywords    string          `json:"keywords,omitempty"`   // Comma separated list of keywords, interests, or intent
-	CustomData  string          `json:"customdata,omitempty"` // Optional feature to pass bidder data that was set in the exchange's cookie. The string must be in base85 cookie safe characters and be in any format. Proper JSON encoding must be used to include "escaped" quotation marks.
-	Geo         *Geo            `json:"geo,omitempty"`
-	Data        []Data          `json:"data,omitempty"`
-	Ext         json.RawMessage `json:"ext,omitempty"`
+	ID         string        `json:"id,omitempty"`         // Unique consumer ID of this user on the exchange
+	BuyerID    string        `json:"buyerid,omitempty"`    // Buyer-specific ID for the user as mapped by the exchange for the buyer. At least one of buyeruid/buyerid or id is recommended. Valid for OpenRTB 2.3.
+	BuyerUID   string        `json:"buyeruid,omitempty"`   // Buyer-specific ID for the user as mapped by the exchange for the buyer. Same as BuyerID but valid for OpenRTB 2.2.
+	YOB        int           `json:"yob,omitempty"`        // Year of birth as a 4-digit integer.
+	Gender     string        `json:"gender,omitempty"`     // Gender ("M": male, "F" female, "O" Other)
+	Keywords   string        `json:"keywords,omitempty"`   // Comma separated list of keywords, interests, or intent
+	CustomData string        `json:"customdata,omitempty"` // Optional feature to pass bidder data that was set in the exchange's cookie. The string must be in base85 cookie safe characters and be in any format. Proper JSON encoding must be used to include "escaped" quotation marks.
+	Geo        *Geo          `json:"geo,omitempty"`
+	Data       []Data        `json:"data,omitempty"`
+	Consent    string        `json:"consent,omitempty"` // GDPR TCF consent string
+	EIDs       []EID         `json:"eids,omitempty"`
+	Ext        UserExtension `json:"ext,omitempty"`
 }
 
 // Data and segment objects together allow additional data about the user to be specified. This data
@@ -811,42 +738,57 @@ type User struct {
 // the id field. A bid request can mix data objects from multiple providers. The specific data providers in
 // use should be published by the exchange a priori to its bidders.
 type Data struct {
-	ID      string          `json:"id,omitempty"`
-	Name    string          `json:"name,omitempty"`
-	Segment []Segment       `json:"segment,omitempty"`
-	Ext     json.RawMessage `json:"ext,omitempty"`
+	ID      string    `json:"id,omitempty"`
+	Name    string    `json:"name,omitempty"`
+	Segment []Segment `json:"segment,omitempty"`
+	Ext     Extension `json:"ext,omitempty"`
 }
 
 // Segment objects are essentially key-value pairs that convey specific units of data about the user. The
 // parent Data object is a collection of such values from a given data provider. The specific segment
 // names and value options must be published by the exchange a priori to its bidders.
 type Segment struct {
-	ID    string          `json:"id,omitempty"`
-	Name  string          `json:"name,omitempty"`
-	Value string          `json:"value,omitempty"`
-	Ext   json.RawMessage `json:"ext,omitempty"`
+	ID    string    `json:"id,omitempty"`
+	Name  string    `json:"name,omitempty"`
+	Value string    `json:"value,omitempty"`
+	Ext   Extension `json:"ext,omitempty"`
+}
+
+// EID (Extended ID) support in the OpenRTB specification allows buyers to use audience data in real-time
+// bidding. This object can contain one or more UIDs from a single source or a technology provider.
+type EID struct {
+	Source string    `json:"source,omitempty"` // Source or technology provider responsible for the set of included IDs. Expressed as a top-level domain.
+	UIDs   []UID     `json:"uids,omitempty"`   // Array of extended ID UID objects from the given source.
+	Ext    Extension `json:"ext,omitempty"`
+}
+
+// UID objects contain a single user identifier provided as part of extended identifiers.
+type UID struct {
+	ID        string    `json:"id,omitempty"`    // The identifier for the user.
+	AgentType int       `json:"atype,omitempty"` // Type of user agent the ID is from.
+	Ext       Extension `json:"ext,omitempty"`
 }
 
 // Regulations object contains any legal, governmental, or industry regulations that apply to the request. The
 // coppa flag signals whether or not the request falls under the United States Federal Trade Commission's
 // regulations for the United States Children's Online Privacy Protection Act ("COPPA").
 type Regulations struct {
-	COPPA     int             `json:"coppa,omitempty"`      // Flag indicating if this request is subject to the COPPA regulations established by the USA FTC, where 0 = no, 1 = yes.
-	GDPR      int             `json:"gdpr,omitempty"`       // Flag that indicates whether or not the request is subject to GDPR regulations 0 = No, 1 = Yes, omission indicates Unknown.
-	USPrivacy string          `json:"us_privacy,omitempty"` // Communicates signals regarding consumer privacy under US privacy regulation.
-	Ext       json.RawMessage `json:"ext,omitempty"`
+	Coppa     int          `json:"coppa,omitempty"`      // Flag indicating if this request is subject to the COPPA regulations established by the USA FTC, where 0 = no, 1 = yes.
+	GDPR      int          `json:"gdpr,omitempty"`       // Flag that indicates whether or not the request is subject to GDPR regulations 0 = No, 1 = Yes, omission indicates Unknown.
+	USPrivacy string       `json:"us_privacy,omitempty"` // Communicates signals regarding consumer privacy under US privacy regulation.
+	Ext       RegExtension `json:"ext,omitempty"`
 }
 
 // Format object represents an allowed size (i.e., height and width combination) for a banner impression.
 // These are typically used in an array for an impression where multiple sizes are permitted.
 // It is recommended that either the w/h pair or the wratio/hratio/wmin set (i.e., for Flex Ads) be specified.
 type Format struct {
-	Width       int             `json:"w,omitempty"`       // Width in device independent pixels (DIPS).
-	Height      int             `json:"h,omitempty"`       // Height in device independent pixels (DIPS).
-	WidthRatio  int             `json:"wratio,omitempty"`  // Relative width when expressing size as a ratio.
-	HeightRatio int             `json:"hration,omitempty"` // Relative height when expressing size as a ratio.
-	WidthMin    int             `json:"wmin,omitempty"`    // The minimum width in device independent pixels (DIPS) at which the ad will be displayed the size is expressed as a ratio.
-	Ext         json.RawMessage `json:"ext,omitempty"`
+	W           int       `json:"w,omitempty"`       // Width in device independent pixels (DIPS).
+	H           int       `json:"h,omitempty"`       // Height in device independent pixels (DIPS).
+	WidthRatio  int       `json:"wratio,omitempty"`  // Relative width when expressing size as a ratio.
+	HeightRatio int       `json:"hration,omitempty"` // Relative height when expressing size as a ratio.
+	WidthMin    int       `json:"wmin,omitempty"`    // The minimum width in device independent pixels (DIPS) at which the ad will be displayed the size is expressed as a ratio.
+	Ext         Extension `json:"ext,omitempty"`
 }
 
 // PodSequence identifies the pod sequence field, for use in video content streams with one or more ad pods as defined in Adcom1.0
@@ -908,8 +850,31 @@ const (
 
 // ChannelEntity describes the network or channel an ad will be displayed on. (Reffer Section 3.2.23 and 3.2.24 OpenRTB_2.6)
 type ChannelEntity struct {
-	ID     string          `json:"id,omitempty"`
-	Name   string          `json:"name,omitempty"`
-	Domain string          `json:"domain,omitempty"`
-	Ext    json.RawMessage `json:"ext,omitempty"`
+	ID     string    `json:"id,omitempty"`
+	Name   string    `json:"name,omitempty"`
+	Domain string    `json:"domain,omitempty"`
+	Ext    Extension `json:"ext,omitempty"`
+}
+
+// RegExtension Extension object for Regulations
+type RegExtension struct {
+	GDPR      int          `json:"gdpr,omitempty"`
+	LGPD      BoolOrNumber `json:"lgpd,omitempty"`
+	PIPL      BoolOrNumber `json:"pipl,omitempty"`
+	USPrivacy string       `json:"us_privacy,omitempty"`
+}
+
+// UserExtension Extension object for User
+type UserExtension struct {
+	Consent      string   `json:"consent,omitempty"`
+	SdkData      *SdkData `json:"sdkdata,omitempty"`
+	AppStartTime int64    `json:"appStartTime,omitempty"`
+	SessionID    string   `json:"sessionId,omitempty"`
+}
+
+// SdkData object for UserExtension. Required for direct demand header bidding integration
+type SdkData struct {
+	Seq    int32 `json:"seq"`    // Number of times Header Bidding same prevaluation token has been requested by Mediation SDK
+	Loads  int32 `json:"loads"`  // Number of times same prevaluation token has been used to load AdM
+	Starts int32 `json:"starts"` // Number of times starts has been registered for the same prevaluation token
 }
diff --git a/pmp.go b/pmp.go
index 5d6b53c..d001f10 100644
--- a/pmp.go
+++ b/pmp.go
@@ -2,22 +2,25 @@ package openrtb
 
 import "encoding/json"
 
-// PMP is the Private Marketplace Object
-type PMP struct {
-	Private int             `json:"private_auction,omitempty"`
-	Deals   []Deal          `json:"deals,omitempty"`
-	Ext     json.RawMessage `json:"ext,omitempty"`
+// Pmp is the Private Marketplace Object
+type Pmp struct {
+	Private int       `json:"private_auction,omitempty"`
+	Deals   []Deal    `json:"deals,omitempty"`
+	Ext     Extension `json:"ext,omitempty"`
 }
 
 // Deal contains PMP deal information.
 type Deal struct {
-	ID               string          `json:"id,omitempty"` // Unique deal ID
-	BidFloor         float64         `json:"bidfloor,omitempty"`
-	BidFloorCurrency string          `json:"bidfloorcur,omitempty"` // Currency of bid floor
-	Seats            []string        `json:"wseat,omitempty"`       // Array of buyer seats allowed to bid on this Direct Deal.
-	AdvDomains       []string        `json:"wadomain,omitempty"`    // Array of advertiser domains allowed to bid on this Direct Deal
-	AuctionType      int             `json:"at,omitempty"`          // Optional override of the overall auction type of the bid request, where 1 = First Price, 2 = Second Price Plus, 3 = the value passed in bidfloor is the agreed upon deal price. Additional auction types can be defined by the exchange.
-	Ext              json.RawMessage `json:"ext,omitempty"`
+	ID               string    `json:"id,omitempty"` // Unique deal ID
+	BidFloor         float64   `json:"bidfloor,omitempty"`
+	BidFloorCurrency string    `json:"bidfloorcur,omitempty"` // Currency of bid floor
+	WSeat            []string  `json:"wseat,omitempty"`       // Array of buyer seats allowed to bid on this Direct Deal.
+	WAdvDomain       []string  `json:"wadomain,omitempty"`    // Array of advertiser domains allowed to bid on this Direct Deal
+	AuctionType      int       `json:"at,omitempty"`          // Optional override of the overall auction type of the bid request, where 1 = First Price, 2 = Second Price Plus, 3 = the value passed in bidfloor is the agreed upon deal price. Additional auction types can be defined by the exchange.
+	Ext              Extension `json:"ext,omitempty"`
+
+	Seats []string `json:"seats,omitempty"` // DEPRECATED: kept for backwards compatibility
+	Type  int      `json:"type,omitempty"`  // DEPRECATED: kept for backwards compatibility
 }
 
 type jsonDeal Deal
diff --git a/pmp_test.go b/pmp_test.go
index 876c2a7..7a2b690 100644
--- a/pmp_test.go
+++ b/pmp_test.go
@@ -6,16 +6,16 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestPMP(t *testing.T) {
-	var subject *PMP
+	var subject *Pmp
 	if err := fixture("pmp", &subject); err != nil {
 		t.Fatalf("expected no error, got %v", err)
 	}
 
-	exp := &PMP{
+	exp := &Pmp{
 		Private: 1,
 		Deals: []Deal{
 			{ID: "DX-1985-010A", BidFloor: 2.5, BidFloorCurrency: "", AuctionType: 2},
@@ -28,7 +28,7 @@ func TestPMP(t *testing.T) {
 }
 
 func TestPMP_MarshalJSON(t *testing.T) {
-	subject := &PMP{Deals: []Deal{{}}}
+	subject := &Pmp{Deals: []Deal{{}}}
 	exp := []byte(`{"deals":[{"at":2}]}`)
 	if got, err := json.Marshal(subject); err != nil {
 		t.Fatalf("expected no error, got %v", err)
diff --git a/quantity.go b/quantity.go
index 9ec3fee..010587b 100644
--- a/quantity.go
+++ b/quantity.go
@@ -1,7 +1,6 @@
 package openrtb
 
 import (
-	"encoding/json"
 	"errors"
 )
 
@@ -23,7 +22,7 @@ type Quantity struct {
 	Multiplier float64               `json:"multiplier"`
 	SourceType MeasurementSourceType `json:"sourcetype,omitempty"`
 	Vendor     string                `json:"vendor,omitempty"`
-	Ext        *json.RawMessage       `json:"ext,omitempty"`
+	Ext        Extension             `json:"ext,omitempty"`
 }
 
 func (qty *Quantity) Validate() error {
diff --git a/quantity_test.go b/quantity_test.go
index 2206437..54a1e41 100644
--- a/quantity_test.go
+++ b/quantity_test.go
@@ -4,7 +4,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestQuantity(t *testing.T) {
diff --git a/seatbid.go b/seatbid.go
index 6e601fe..1ace4a3 100644
--- a/seatbid.go
+++ b/seatbid.go
@@ -1,7 +1,6 @@
 package openrtb
 
 import (
-	"encoding/json"
 	"errors"
 )
 
@@ -12,10 +11,10 @@ import (
 // Group attribute can be used to specify if a seat is willing to accept any impressions that it can win (default) or if it is
 // only interested in winning any if it can win them all (i.e., all or nothing).
 type SeatBid struct {
-	Bids  []Bid           `json:"bid"`             // Array of bid objects; each realtes to an imp, if exchange supported can have many bid objects.
-	Seat  string          `json:"seat,omitempty"`  // ID of the bidder seat optional string ID of the bidder seat on whose behalf this bid is made.
-	Group int             `json:"group,omitempty"` // '1' means impression must be won-lost as a group; default is '0'.
-	Ext   json.RawMessage `json:"ext,omitempty"`
+	Bid   []Bid     `json:"bid"`             // Array of bid objects; each realtes to an imp, if exchange supported can have many bid objects.
+	Seat  string    `json:"seat,omitempty"`  // ID of the bidder seat optional string ID of the bidder seat on whose behalf this bid is made.
+	Group int       `json:"group,omitempty"` // '1' means impression must be won-lost as a group; default is '0'.
+	Ext   Extension `json:"ext,omitempty"`
 }
 
 // Validation errors
@@ -25,12 +24,12 @@ var (
 
 // Validate required attributes
 func (sb *SeatBid) Validate() error {
-	if len(sb.Bids) == 0 {
+	if len(sb.Bid) == 0 {
 		return ErrInvalidSeatBidBid
 	}
 
-	for i := range sb.Bids {
-		bid := sb.Bids[i]
+	for i := range sb.Bid {
+		bid := sb.Bid[i]
 		if err := (&bid).Validate(); err != nil {
 			return err
 		}
diff --git a/seatbid_test.go b/seatbid_test.go
index 9f73349..0590c85 100644
--- a/seatbid_test.go
+++ b/seatbid_test.go
@@ -4,12 +4,12 @@ import (
 	"errors"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestSeatBid_Validate(t *testing.T) {
 	subject := &SeatBid{}
-	if exp, got := ErrInvalidSeatBidBid, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidSeatBidBid, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 }
diff --git a/source.go b/source.go
index c72acf3..615ceb8 100644
--- a/source.go
+++ b/source.go
@@ -1,11 +1,9 @@
 package openrtb
 
-import "encoding/json"
-
 // Source object describes the nature and behavior of the entity that is the source of the bid request upstream from the exchange.
 type Source struct {
-	FinalSaleDecision int             `json:"fd,omitempty"`     // Entity responsible for the final impression sale decision, where 0 = exchange, 1 = upstream source.
-	TransactionID     string          `json:"tid,omitempty"`    // Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges).
-	PaymentChain      string          `json:"pchain,omitempty"` // Payment ID chain string containing embedded syntax described in the TAG Payment ID Protocol v1.0.
-	Ext               json.RawMessage `json:"ext,omitempty"`    // Placeholder for exchange-specific extensions to OpenRTB.
+	FinalSaleDecision int       `json:"fd"`               // Entity responsible for the final impression sale decision, where 0 = exchange, 1 = upstream source.
+	TransactionID     string    `json:"tid,omitempty"`    // Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges).
+	PaymentChain      string    `json:"pchain,omitempty"` // Payment ID chain string containing embedded syntax described in the TAG Payment ID Protocol v1.0.
+	Ext               Extension `json:"ext,omitempty"`    // Placeholder for exchange-specific extensions to OpenRTB.
 }
diff --git a/source_test.go b/source_test.go
index 82401ea..d4f9f3f 100644
--- a/source_test.go
+++ b/source_test.go
@@ -1,11 +1,10 @@
 package openrtb_test
 
 import (
-	"encoding/json"
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestSource(t *testing.T) {
@@ -18,7 +17,7 @@ func TestSource(t *testing.T) {
 		FinalSaleDecision: 1,
 		TransactionID:     "transaction-id",
 		PaymentChain:      "payment-chain",
-		Ext:               json.RawMessage("{}"),
+		Ext:               Extension("{}"),
 	}
 	if got := subject; !reflect.DeepEqual(exp, got) {
 		t.Errorf("expected %+v, got %+v", exp, got)
diff --git a/sua.go b/sua.go
index e310d6b..0f0f92d 100644
--- a/sua.go
+++ b/sua.go
@@ -1,20 +1,18 @@
 package openrtb
 
-import "encoding/json"
-
 type UserAgent struct {
-	Browsers     []BrandVersion  `json:"browsers,omitempty"`     // A browser or similar software component
-	Platform     BrandVersion    `json:"platform,omitempty"`     // The user agent’s execution platform / OS
-	Mobile       int             `json:"mobile,omitempty"`       // 1 if the agent prefers a "mobile" version of the content, if available, i.e. optimized for small screens or touch input. 0 if the agent prefers the "desktop". Taken from Sec-CH-UAMobile header
-	Architecture string          `json:"architecture,omitempty"` // Device’s major binary architecture, e.g. "x86" or "arm". Taken from the Sec-CH-UA-Arch header
-	Bitness      string          `json:"bitness,omitempty"`      // Device’s bitness, e.g. "64" for 64-bit architecture. Taken from the Sec-CH-UA-Bitness header
-	Model        string          `json:"model,omitempty"`        // Device model. Taken from the Sec-CH-UAModel header
-	Source       int             `json:"source,omitempty"`       // The source of data used to create this object, List: User-Agent Source in AdCOM 1.0
-	Ext          json.RawMessage `json:"ext,omitempty"`
+	Browsers     []BrandVersion `json:"browsers,omitempty"`     // A browser or similar software component
+	Platform     BrandVersion   `json:"platform,omitempty"`     // The user agent’s execution platform / OS
+	Mobile       int            `json:"mobile,omitempty"`       // 1 if the agent prefers a "mobile" version of the content, if available, i.e. optimized for small screens or touch input. 0 if the agent prefers the "desktop". Taken from Sec-CH-UAMobile header
+	Architecture string         `json:"architecture,omitempty"` // Device’s major binary architecture, e.g. "x86" or "arm". Taken from the Sec-CH-UA-Arch header
+	Bitness      string         `json:"bitness,omitempty"`      // Device’s bitness, e.g. "64" for 64-bit architecture. Taken from the Sec-CH-UA-Bitness header
+	Model        string         `json:"model,omitempty"`        // Device model. Taken from the Sec-CH-UAModel header
+	Source       int            `json:"source,omitempty"`       // The source of data used to create this object, List: User-Agent Source in AdCOM 1.0
+	Ext          Extension      `json:"ext,omitempty"`
 }
 
 type BrandVersion struct {
-	Brand   string          `json:"brand,omitempty"`   // A brand identifier, for example, "Chrome" or "Windows". Taken from the Sec-CH-UA-Full-Version or Sec-CH-UA-Platform header
-	Version []string        `json:"version,omitempty"` // A sequence of version components, in descending hierarchical order (major, minor, patch)
-	Ext     json.RawMessage `json:"ext,omitempty"`
+	Brand   string    `json:"brand,omitempty"`   // A brand identifier, for example, "Chrome" or "Windows". Taken from the Sec-CH-UA-Full-Version or Sec-CH-UA-Platform header
+	Version []string  `json:"version,omitempty"` // A sequence of version components, in descending hierarchical order (major, minor, patch)
+	Ext     Extension `json:"ext,omitempty"`
 }
diff --git a/testdata/bid.loopme.ar.json b/testdata/bid.loopme.ar.json
new file mode 100644
index 0000000..aec4012
--- /dev/null
+++ b/testdata/bid.loopme.ar.json
@@ -0,0 +1,25 @@
+{
+  "id": "5d4b3b3be8250678c813f475",
+  "impid": "1",
+  "price": 29.7,
+  "adid": "2056515",
+  "nurl": "https://us.fake-url.com/tr?et=BID_WIN&a_ecp=29.7&a_price=${AUCTION_PRICE:BF}&name=imp_nurl",
+  "adm": "<img>",
+  "adomain": [
+    "unity.com"
+  ],
+  "iurl": "https://fake-url.net/assets/2690-e3c0-aa99-9efd38cd1fa6.jpg",
+  "cid": "2001626",
+  "crid": "2056515",
+  "attr": [
+    99
+  ],
+  "protocol": 2,
+  "cat": [
+    "IAB19"
+  ],
+  "lurl": "https://fake-url.com?et=BID_LOSS&meta=&a_id=${AUCTION_ID}&a_bid_id=${AUCTION_BID_ID}&a_loss_id=${AUCTION_LOSS}",
+  "h": 320,
+  "w": 480,
+  "crtype": "MRAID AR"
+}
\ No newline at end of file
diff --git a/testdata/content.quoted.json b/testdata/content.quoted.json
index 67733eb..e88e1d8 100644
--- a/testdata/content.quoted.json
+++ b/testdata/content.quoted.json
@@ -9,7 +9,7 @@
   "len": 129,
   "series": "book reading",
   "videoquality": 2,
-  "context": "1",
+  "context": 1,
   "season": "1",
   "sourcerelationship": 0,
   "id": "eb9f13ede5fd225333971523f6042f9d"
diff --git a/testdata/device.json b/testdata/device.json
index 98b7e55..bcba212 100644
--- a/testdata/device.json
+++ b/testdata/device.json
@@ -22,5 +22,6 @@
   "js": 1,
   "connectiontype": 3,
   "mccmnc": "722-341",
-  "devicetype": 1
+  "devicetype": 1,
+  "oaid": "1fe9a970-efbb-29e0-0bdd-f5dbbf751ab5"
 }
diff --git a/testdata/impression.json b/testdata/impression.json
index 3d1bd45..b0db1e8 100644
--- a/testdata/impression.json
+++ b/testdata/impression.json
@@ -1,5 +1,17 @@
 {
   "id": "1",
+  "metric": [
+    {
+      "type": "viewable_percentage_1",
+      "value": 0.99,
+      "vendor": "MOAT"
+    },
+    {
+      "type": "viewable_percentage_2",
+      "value": 0.98,
+      "vendor": "MOAT"
+    }
+  ],
   "banner": {
     "h": 250,
     "w": 300,
@@ -18,5 +30,6 @@
   },
   "qty": {
     "multiplier": 1.0
-  }
+  },
+  "rwdd": 1
 }
diff --git a/testdata/metric.json b/testdata/metric.json
new file mode 100644
index 0000000..3e09dcd
--- /dev/null
+++ b/testdata/metric.json
@@ -0,0 +1,5 @@
+{
+  "type": "viewable_percentage",
+  "value": 0.99,
+  "vendor": "MOAT"
+}
\ No newline at end of file
diff --git a/video.go b/video.go
index f69d0e2..2df18b5 100644
--- a/video.go
+++ b/video.go
@@ -7,55 +7,57 @@ import (
 
 // Validation errors
 var (
-	ErrInvalidVideoNoMIMEs     = errors.New("openrtb: video has no mimes")
-	ErrInvalidVideoNoLinearity = errors.New("openrtb: video linearity missing")
-	ErrInvalidVideoNoProtocols = errors.New("openrtb: video protocols missing")
+	ErrInvalidVideoNoMimes       = errors.New("openrtb: video has no mimes")
+	ErrInvalidVideoNoLinearity   = errors.New("openrtb: video linearity missing")
+	ErrInvalidVideoNoMinDuration = errors.New("openrtb: video min-duration missing")
+	ErrInvalidVideoNoMaxDuration = errors.New("openrtb: video max-duration missing")
+	ErrInvalidVideoNoProtocols   = errors.New("openrtb: video protocols missing")
 )
 
 // Video object must be included directly in the impression object if the impression offered
 // for auction is an in-stream video ad opportunity.
 type Video struct {
-	MIMEs           []string            `json:"mimes,omitempty"`          // Content MIME types supported.
-	MinDuration     int                 `json:"minduration,omitempty"`    // Minimum video ad duration in seconds
-	MaxDuration     int                 `json:"maxduration,omitempty"`    // Maximum video ad duration in seconds
-	Protocols       []Protocol          `json:"protocols,omitempty"`      // Video bid response protocols
-	Protocol        Protocol            `json:"protocol,omitempty"`       // Video bid response protocols DEPRECATED
-	Width           int                 `json:"w"`                        // Width of the player in pixels
-	Height          int                 `json:"h"`                        // Height of the player in pixels
-	StartDelay      StartDelay          `json:"startdelay,omitempty"`     // Indicates the start delay in seconds
-	Linearity       VideoLinearity      `json:"linearity,omitempty"`      // Indicates whether the ad impression is linear or non-linear
-	Skip            int                 `json:"skip,omitempty"`           // Indicates if the player will allow the video to be skipped, where 0 = no, 1 = yes.
-	SkipMin         int                 `json:"skipmin,omitempty"`        // Videos of total duration greater than this number of seconds can be skippable
-	SkipAfter       int                 `json:"skipafter,omitempty"`      // Number of seconds a video must play before skipping is enabled
-	Sequence        int                 `json:"sequence,omitempty"`       // Default: 1
-	BlockedAttrs    []CreativeAttribute `json:"battr,omitempty"`          // Blocked creative attributes
-	MaxExtended     int                 `json:"maxextended,omitempty"`    // Maximum extended video ad duration
-	MinBitrate      int                 `json:"minbitrate,omitempty"`     // Minimum bit rate in Kbps
-	MaxBitrate      int                 `json:"maxbitrate,omitempty"`     // Maximum bit rate in Kbps
-	BoxingAllowed   *int                `json:"boxingallowed,omitempty"`  // If exchange publisher has rules preventing letter boxing
-	PodID           string              `json:"podid,omitempty"`          // Pod id unique identifier for video ad pod
-	PodDuration     int                 `json:"poddur,omitempty"`         // Pod Duration total amount of time in seconds that advertisers may fill for a video ad pod
-	PodSequence     PodSequence         `json:"podseq,omitempty"`         // Pod Sequence position of the video ad pod
-	SlotInPod       SlotPositionInPod   `json:"slotinpod,omitempty"`      // Slot Position in ad
-	RqdDurs         []int64             `json:"rqddurs,omitempty"`        // Precise acceptable durations for video creatives inseconds.
-	MinCPMPerSecond float64             `json:"mincpmpersec,omitempty"`   //   Minimum CPM per second. This is a price floor for the portion of a video ad pod
-	PlaybackMethods []VideoPlayback     `json:"playbackmethod,omitempty"` // List of allowed playback methods
-	Delivery        []ContentDelivery   `json:"delivery,omitempty"`       // List of supported delivery methods
-	Position        AdPosition          `json:"pos,omitempty"`            // Ad Position
-	CompanionAds    []Banner            `json:"companionad,omitempty"`
-	APIs            []APIFramework      `json:"api,omitempty"` // List of supported API frameworks
-	CompanionTypes  []CompanionType     `json:"companiontype,omitempty"`
-	Placement       VideoPlacement      `json:"placement,omitempty"` // Video placement type, DEPRECATED
-	Plcmt           VideoPlcmt          `json:"plcmt,omitempty"`     // Video Plcmt type ad defined in ADCOM1.0
-	Ext             json.RawMessage     `json:"ext,omitempty"`
+	Mimes           []string          `json:"mimes,omitempty"`          // Content MIME types supported.
+	MinDuration     int               `json:"minduration,omitempty"`    // Minimum video ad duration in seconds
+	MaxDuration     int               `json:"maxduration,omitempty"`    // Maximum video ad duration in seconds
+	Protocols       []int             `json:"protocols,omitempty"`      // Video bid response protocols
+	Protocol        int               `json:"protocol,omitempty"`       // Video bid response protocols DEPRECATED
+	W               int               `json:"w,omitempty"`              // Width of the player in pixels
+	H               int               `json:"h,omitempty"`              // Height of the player in pixels
+	StartDelay      int               `json:"startdelay,omitempty"`     // Indicates the start delay in seconds
+	Linearity       int               `json:"linearity,omitempty"`      // Indicates whether the ad impression is linear or non-linear
+	Skip            int               `json:"skip"`                     // Indicates if the player will allow the video to be skipped, where 0 = no, 1 = yes.
+	SkipMin         int               `json:"skipmin,omitempty"`        // Videos of total duration greater than this number of seconds can be skippable
+	SkipAfter       int               `json:"skipafter,omitempty"`      // Number of seconds a video must play before skipping is enabled
+	Sequence        int               `json:"sequence,omitempty"`       // Default: 1
+	BAttr           []int             `json:"battr,omitempty"`          // Blocked creative attributes
+	MaxExtended     int               `json:"maxextended,omitempty"`    // Maximum extended video ad duration
+	MinBitrate      int               `json:"minbitrate,omitempty"`     // Minimum bit rate in Kbps
+	MaxBitrate      int               `json:"maxbitrate,omitempty"`     // Maximum bit rate in Kbps
+	BoxingAllowed   *int              `json:"boxingallowed,omitempty"`  // If exchange publisher has rules preventing letter boxing
+	PodID           string            `json:"podid,omitempty"`          // Pod id unique identifier for video ad pod
+	PodDuration     int               `json:"poddur,omitempty"`         // Pod Duration total amount of time in seconds that advertisers may fill for a video ad pod
+	PodSequence     PodSequence       `json:"podseq,omitempty"`         // Pod Sequence position of the video ad pod
+	SlotInPod       SlotPositionInPod `json:"slotinpod,omitempty"`      // Slot Position in ad
+	RqdDurs         []int64           `json:"rqddurs,omitempty"`        // Precise acceptable durations for video creatives inseconds.
+	MinCPMPerSecond float64           `json:"mincpmpersec,omitempty"`   //   Minimum CPM per second. This is a price floor for the portion of a video ad pod
+	PlaybackMethod  []int             `json:"playbackmethod,omitempty"` // List of allowed playback methods
+	Delivery        []int             `json:"delivery,omitempty"`       // List of supported delivery methods
+	Pos             int               `json:"pos,omitempty"`            // Ad Position
+	CompanionAd     []Banner          `json:"companionad,omitempty"`
+	Api             []int             `json:"api,omitempty"` // List of supported API frameworks
+	CompanionType   []int             `json:"companiontype,omitempty"`
+	Placement       int               `json:"placement,omitempty"` // Video placement type, DEPRECATED
+	Plcmt           VideoPlcmt        `json:"plcmt,omitempty"`     // Video Plcmt type ad defined in ADCOM1.0
+	Ext             Extension         `json:"ext,omitempty"`
 }
 
 type jsonVideo Video
 
 // Validate the object
 func (v *Video) Validate() error {
-	if len(v.MIMEs) == 0 {
-		return ErrInvalidVideoNoMIMEs
+	if len(v.Mimes) == 0 {
+		return ErrInvalidVideoNoMimes
 	} else if v.Linearity == 0 {
 		return ErrInvalidVideoNoLinearity
 	} else if v.Protocol == 0 && len(v.Protocols) == 0 {
diff --git a/video_test.go b/video_test.go
index 4b9cbda..12037df 100644
--- a/video_test.go
+++ b/video_test.go
@@ -5,7 +5,7 @@ import (
 	"reflect"
 	"testing"
 
-	. "github.com/bsm/openrtb/v3"
+	. "github.com/UnityTech/openrtb/v3"
 )
 
 func TestVideo(t *testing.T) {
@@ -16,34 +16,34 @@ func TestVideo(t *testing.T) {
 
 	boxingAllowed := 1
 	exp := &Video{
-		MIMEs: []string{
+		Mimes: []string{
 			"video/x-flv",
 			"video/mp4",
 			"application/x-shockwave-flash",
 			"application/javascript",
 		},
-		MinDuration:     5,
-		MaxDuration:     30,
-		Protocols:       []Protocol{ProtocolVAST2, ProtocolVAST3},
-		Width:           640,
-		Height:          480,
-		Linearity:       VideoLinearityLinear,
-		Sequence:        1,
-		BlockedAttrs:    []CreativeAttribute{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert},
-		MaxExtended:     30,
-		MinBitrate:      300,
-		MaxBitrate:      1500,
-		BoxingAllowed:   &boxingAllowed,
-		PlaybackMethods: []VideoPlayback{VideoPlaybackPageLoadSoundOn, VideoPlaybackClickToPlay},
-		Delivery:        []ContentDelivery{ContentDeliveryProgressive},
-		Position:        AdPositionAboveFold,
-		CompanionAds: []Banner{
-			{Width: 300, Height: 250, ID: "1234567893-1", Position: AdPositionAboveFold, BlockedAttrs: []CreativeAttribute{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}, ExpDirs: []ExpDir{ExpDirRight, ExpDirDown}},
-			{Width: 728, Height: 90, ID: "1234567893-2", Position: AdPositionAboveFold, BlockedAttrs: []CreativeAttribute{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}},
+		MinDuration:    5,
+		MaxDuration:    30,
+		Protocols:      []int{VideoProtoVAST2, VideoProtoVAST3},
+		W:              640,
+		H:              480,
+		Linearity:      VideoLinearityLinear,
+		Sequence:       1,
+		BAttr:          []int{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert},
+		MaxExtended:    30,
+		MinBitrate:     300,
+		MaxBitrate:     1500,
+		BoxingAllowed:  &boxingAllowed,
+		PlaybackMethod: []int{VideoPlaybackAutoSoundOn, VideoPlaybackClickToPlay},
+		Delivery:       []int{ContentDeliveryProgressive},
+		Pos:            AdPosAboveFold,
+		CompanionAd: []Banner{
+			{W: 300, H: 250, ID: "1234567893-1", Pos: AdPosAboveFold, BAttr: []int{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}, ExpDir: []int{ExpDirRight, ExpDirDown}},
+			{W: 728, H: 90, ID: "1234567893-2", Pos: AdPosAboveFold, BAttr: []int{CreativeAttributeUserInitiated, CreativeAttributeWindowsDialogOrAlert}},
 		},
-		Placement:      VideoPlacementInStream,
-		APIs:           []APIFramework{APIFrameworkVPAID1, APIFrameworkVPAID2},
-		CompanionTypes: []CompanionType{CompanionTypeStatic, CompanionTypeHTML},
+		Placement:     VideoPlacementInStream,
+		Api:           []int{APIFrameworkVPAID1, APIFrameworkVPAID2},
+		CompanionType: []int{VASTCompanionStatic, VASTCompanionHTML},
 	}
 	if got := subject; !reflect.DeepEqual(exp, got) {
 		t.Errorf("expected %+v, got %+v", exp, got)
@@ -52,20 +52,20 @@ func TestVideo(t *testing.T) {
 
 func TestVideo_Validate(t *testing.T) {
 	subject := &Video{}
-	if exp, got := ErrInvalidVideoNoMIMEs, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidVideoNoMimes, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
-	subject = &Video{MIMEs: []string{"video/mp4"}}
-	if exp, got := ErrInvalidVideoNoLinearity, subject.Validate(); !errors.Is(exp, got) {
+	subject = &Video{Mimes: []string{"video/mp4"}}
+	if exp, got := ErrInvalidVideoNoLinearity, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 	subject = &Video{
 		MinDuration: 1,
 		MaxDuration: 1,
 		Linearity:   VideoLinearityNonLinear,
-		MIMEs:       []string{"video/mp4"},
+		Mimes:       []string{"video/mp4"},
 	}
-	if exp, got := ErrInvalidVideoNoProtocols, subject.Validate(); !errors.Is(exp, got) {
+	if exp, got := ErrInvalidVideoNoProtocols, subject.Validate(); !errors.Is(got, exp) {
 		t.Fatalf("expected %v, got %v", exp, got)
 	}
 }
