Repository: mailgun/mailgun-go
Pull request #393 (2025-02-23 11:42:34)

DE-1393 Move API Mailgun requests, responses, and helper types into a separate package


Patch:
diff --git a/analytics.go b/analytics.go
index 41e0053..a82825f 100644
--- a/analytics.go
+++ b/analytics.go
@@ -4,20 +4,10 @@ import (
 	"context"
 
 	"github.com/mailgun/errors"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
-type MetricsPagination struct {
-	// Colon-separated value indicating column name and sort direction e.g. 'domain:asc'.
-	Sort string `json:"sort"`
-	// The number of items to skip over when satisfying the request.
-	// To get the first page of data set skip to zero.
-	// Then increment the skip by the limit for subsequent calls.
-	Skip int `json:"skip"`
-	// The maximum number of items returned in the response.
-	Limit int `json:"limit"`
-	// The total number of items in the query result set.
-	Total int `json:"total"`
-}
+type MetricsOptions = mtypes.MetricsRequest
 
 // ListMetrics returns domain/account metrics.
 //
@@ -58,7 +48,7 @@ func (iter *MetricsIterator) Err() error {
 // Next retrieves the next page of items from the api. Returns false when there are
 // no more pages to retrieve or if there was an error.
 // Use `.Err()` to retrieve the error
-func (iter *MetricsIterator) Next(ctx context.Context, resp *MetricsResponse) (more bool) {
+func (iter *MetricsIterator) Next(ctx context.Context, resp *mtypes.MetricsResponse) (more bool) {
 	if iter.err != nil {
 		return false
 	}
@@ -73,7 +63,7 @@ func (iter *MetricsIterator) Next(ctx context.Context, resp *MetricsResponse) (m
 	return len(resp.Items) == iter.opts.Pagination.Limit
 }
 
-func (iter *MetricsIterator) fetch(ctx context.Context, resp *MetricsResponse) error {
+func (iter *MetricsIterator) fetch(ctx context.Context, resp *mtypes.MetricsResponse) error {
 	if resp == nil {
 		return errors.New("resp cannot be nil")
 	}
@@ -86,7 +76,7 @@ func (iter *MetricsIterator) fetch(ctx context.Context, resp *MetricsResponse) e
 	}
 
 	// preallocate
-	resp.Items = make([]MetricsItem, 0, iter.opts.Pagination.Limit)
+	resp.Items = make([]mtypes.MetricsItem, 0, iter.opts.Pagination.Limit)
 
 	err = httpResp.parseFromJSON(resp)
 	if err != nil {
diff --git a/bounces.go b/bounces.go
index cba6713..3fdce1e 100644
--- a/bounces.go
+++ b/bounces.go
@@ -3,31 +3,13 @@ package mailgun
 import (
 	"context"
 	"strconv"
-)
-
-// Bounce aggregates data relating to undeliverable messages to a specific intended recipient,
-// identified by Address.
-type Bounce struct {
-	// The time at which Mailgun detected the bounce.
-	CreatedAt RFC2822Time `json:"created_at"`
-	// Code provides the SMTP error code that caused the bounce
-	Code string `json:"code"`
-	// Address the bounce is for
-	Address string `json:"address"`
-	// human readable reason why
-	Error string `json:"error"`
-}
 
-type Paging struct {
-	First    string `json:"first,omitempty"`
-	Next     string `json:"next,omitempty"`
-	Previous string `json:"previous,omitempty"`
-	Last     string `json:"last,omitempty"`
-}
+	"github.com/mailgun/mailgun-go/v4/mtypes"
+)
 
 type bouncesListResponse struct {
-	Items  []Bounce `json:"items"`
-	Paging Paging   `json:"paging"`
+	Items  []mtypes.Bounce `json:"items"`
+	Paging mtypes.Paging   `json:"paging"`
 }
 
 // ListBounces returns a complete set of bounces logged against the sender's domain, if any.
@@ -46,7 +28,7 @@ func (mg *MailgunImpl) ListBounces(domain string, opts *ListOptions) *BouncesIte
 	url, err := r.generateUrlWithParameters()
 	return &BouncesIterator{
 		mg:                  mg,
-		bouncesListResponse: bouncesListResponse{Paging: Paging{Next: url, First: url}},
+		bouncesListResponse: bouncesListResponse{Paging: mtypes.Paging{Next: url, First: url}},
 		err:                 err,
 	}
 }
@@ -57,7 +39,7 @@ type BouncesIterator struct {
 	err error
 }
 
-// If an error occurred during iteration `Err()` will return non nil
+// Err if an error occurred during iteration `Err()` will return non nil
 func (ci *BouncesIterator) Err() error {
 	return ci.err
 }
@@ -65,7 +47,7 @@ func (ci *BouncesIterator) Err() error {
 // Next retrieves the next page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error
-func (ci *BouncesIterator) Next(ctx context.Context, items *[]Bounce) bool {
+func (ci *BouncesIterator) Next(ctx context.Context, items *[]mtypes.Bounce) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -73,7 +55,7 @@ func (ci *BouncesIterator) Next(ctx context.Context, items *[]Bounce) bool {
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Bounce, len(ci.Items))
+	cpy := make([]mtypes.Bounce, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 
@@ -83,7 +65,7 @@ func (ci *BouncesIterator) Next(ctx context.Context, items *[]Bounce) bool {
 // First retrieves the first page of items from the api. Returns false if there
 // was an error. It also sets the iterator object to the first page.
 // Use `.Err()` to retrieve the error.
-func (ci *BouncesIterator) First(ctx context.Context, items *[]Bounce) bool {
+func (ci *BouncesIterator) First(ctx context.Context, items *[]mtypes.Bounce) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -91,7 +73,7 @@ func (ci *BouncesIterator) First(ctx context.Context, items *[]Bounce) bool {
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Bounce, len(ci.Items))
+	cpy := make([]mtypes.Bounce, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 	return true
@@ -101,7 +83,7 @@ func (ci *BouncesIterator) First(ctx context.Context, items *[]Bounce) bool {
 // Calling Last() is invalid unless you first call First() or Next()
 // Returns false if there was an error. It also sets the iterator object
 // to the last page. Use `.Err()` to retrieve the error.
-func (ci *BouncesIterator) Last(ctx context.Context, items *[]Bounce) bool {
+func (ci *BouncesIterator) Last(ctx context.Context, items *[]mtypes.Bounce) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -109,7 +91,7 @@ func (ci *BouncesIterator) Last(ctx context.Context, items *[]Bounce) bool {
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Bounce, len(ci.Items))
+	cpy := make([]mtypes.Bounce, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 	return true
@@ -118,7 +100,7 @@ func (ci *BouncesIterator) Last(ctx context.Context, items *[]Bounce) bool {
 // Previous retrieves the previous page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error if any
-func (ci *BouncesIterator) Previous(ctx context.Context, items *[]Bounce) bool {
+func (ci *BouncesIterator) Previous(ctx context.Context, items *[]mtypes.Bounce) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -129,7 +111,7 @@ func (ci *BouncesIterator) Previous(ctx context.Context, items *[]Bounce) bool {
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Bounce, len(ci.Items))
+	cpy := make([]mtypes.Bounce, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 
@@ -146,12 +128,12 @@ func (ci *BouncesIterator) fetch(ctx context.Context, url string) error {
 }
 
 // GetBounce retrieves a single bounce record, if any exist, for the given recipient address.
-func (mg *MailgunImpl) GetBounce(ctx context.Context, domain, address string) (Bounce, error) {
+func (mg *MailgunImpl) GetBounce(ctx context.Context, domain, address string) (mtypes.Bounce, error) {
 	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, bouncesEndpoint, domain) + "/" + address)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	var response Bounce
+	var response mtypes.Bounce
 	err := getResponseFromJSON(ctx, r, &response)
 	return response, err
 }
@@ -190,7 +172,7 @@ func (mg *MailgunImpl) AddBounce(ctx context.Context, domain, address, code, bou
 }
 
 // Add Bounces adds a list of bounces to the bounce list
-func (mg *MailgunImpl) AddBounces(ctx context.Context, domain string, bounces []Bounce) error {
+func (mg *MailgunImpl) AddBounces(ctx context.Context, domain string, bounces []mtypes.Bounce) error {
 	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, bouncesEndpoint, domain))
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
diff --git a/credentials.go b/credentials.go
index 945a9c7..2616d95 100644
--- a/credentials.go
+++ b/credentials.go
@@ -2,25 +2,20 @@ package mailgun
 
 import (
 	"context"
-	"fmt"
+	"errors"
 	"strconv"
-)
 
-// A Credential structure describes a principle allowed to send or receive mail at the domain.
-type Credential struct {
-	CreatedAt RFC2822Time `json:"created_at"`
-	Login     string      `json:"login"`
-	Password  string      `json:"password"`
-}
+	"github.com/mailgun/mailgun-go/v4/mtypes"
+)
 
 type credentialsListResponse struct {
 	// is -1 if Next() or First() have not been called
-	TotalCount int          `json:"total_count"`
-	Items      []Credential `json:"items"`
+	TotalCount int                 `json:"total_count"`
+	Items      []mtypes.Credential `json:"items"`
 }
 
-// Returned when a required parameter is missing.
-var ErrEmptyParam = fmt.Errorf("empty or illegal parameter")
+// ErrEmptyParam is returned when a required parameter is missing.
+var ErrEmptyParam = errors.New("empty or illegal parameter")
 
 // ListCredentials returns the (possibly zero-length) list of credentials associated with your domain.
 func (mg *MailgunImpl) ListCredentials(domain string, opts *ListOptions) *CredentialsIterator {
@@ -63,7 +58,7 @@ func (ri *CredentialsIterator) Offset() int {
 // Next retrieves the next page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error
-func (ri *CredentialsIterator) Next(ctx context.Context, items *[]Credential) bool {
+func (ri *CredentialsIterator) Next(ctx context.Context, items *[]mtypes.Credential) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -73,7 +68,7 @@ func (ri *CredentialsIterator) Next(ctx context.Context, items *[]Credential) bo
 		return false
 	}
 
-	cpy := make([]Credential, len(ri.Items))
+	cpy := make([]mtypes.Credential, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	if len(ri.Items) == 0 {
@@ -86,7 +81,7 @@ func (ri *CredentialsIterator) Next(ctx context.Context, items *[]Credential) bo
 // First retrieves the first page of items from the api. Returns false if there
 // was an error. It also sets the iterator object to the first page.
 // Use `.Err()` to retrieve the error.
-func (ri *CredentialsIterator) First(ctx context.Context, items *[]Credential) bool {
+func (ri *CredentialsIterator) First(ctx context.Context, items *[]mtypes.Credential) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -94,7 +89,7 @@ func (ri *CredentialsIterator) First(ctx context.Context, items *[]Credential) b
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Credential, len(ri.Items))
+	cpy := make([]mtypes.Credential, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	ri.offset = len(ri.Items)
@@ -105,7 +100,7 @@ func (ri *CredentialsIterator) First(ctx context.Context, items *[]Credential) b
 // Calling Last() is invalid unless you first call First() or Next()
 // Returns false if there was an error. It also sets the iterator object
 // to the last page. Use `.Err()` to retrieve the error.
-func (ri *CredentialsIterator) Last(ctx context.Context, items *[]Credential) bool {
+func (ri *CredentialsIterator) Last(ctx context.Context, items *[]mtypes.Credential) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -123,7 +118,7 @@ func (ri *CredentialsIterator) Last(ctx context.Context, items *[]Credential) bo
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Credential, len(ri.Items))
+	cpy := make([]mtypes.Credential, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	return true
@@ -132,7 +127,7 @@ func (ri *CredentialsIterator) Last(ctx context.Context, items *[]Credential) bo
 // Previous retrieves the previous page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error if any
-func (ri *CredentialsIterator) Previous(ctx context.Context, items *[]Credential) bool {
+func (ri *CredentialsIterator) Previous(ctx context.Context, items *[]mtypes.Credential) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -150,7 +145,7 @@ func (ri *CredentialsIterator) Previous(ctx context.Context, items *[]Credential
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Credential, len(ri.Items))
+	cpy := make([]mtypes.Credential, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 
diff --git a/domains.go b/domains.go
index 6ed4c4e..142565b 100644
--- a/domains.go
+++ b/domains.go
@@ -4,92 +4,10 @@ import (
 	"context"
 	"strconv"
 	"strings"
-)
 
-// Use these to specify a spam action when creating a new domain.
-const (
-	// SpamActionTag tags the received message with headers providing a measure of its spamness.
-	SpamActionTag = SpamAction("tag")
-	// SpamActionDisabled prevents Mailgun from taking any action on what it perceives to be spam.
-	SpamActionDisabled = SpamAction("disabled")
-	// SpamActionDelete instructs Mailgun to just block or delete the message all-together.
-	SpamActionDelete = SpamAction("delete")
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
-type SpamAction string
-
-// A Domain structure holds information about a domain used when sending mail.
-type Domain struct {
-	CreatedAt                  RFC2822Time `json:"created_at"`
-	ID                         string      `json:"id"`
-	IsDisabled                 bool        `json:"is_disabled"`
-	Name                       string      `json:"name"`
-	RequireTLS                 bool        `json:"require_tls"`
-	SkipVerification           bool        `json:"skip_verification"`
-	SMTPLogin                  string      `json:"smtp_login"`
-	SMTPPassword               string      `json:"smtp_password,omitempty"`
-	SpamAction                 SpamAction  `json:"spam_action"`
-	State                      string      `json:"state"`
-	Type                       string      `json:"type"`
-	TrackingHost               string      `json:"tracking_host,omitempty"`
-	UseAutomaticSenderSecurity bool        `json:"use_automatic_sender_security"`
-	WebPrefix                  string      `json:"web_prefix"`
-	WebScheme                  string      `json:"web_scheme"`
-	Wildcard                   bool        `json:"wildcard"`
-}
-
-// DNSRecord structures describe intended records to properly configure your domain for use with Mailgun.
-// Note that Mailgun does not host DNS records.
-type DNSRecord struct {
-	Active     bool     `json:"is_active"`
-	Cached     []string `json:"cached"`
-	Name       string   `json:"name,omitempty"`
-	Priority   string   `json:"priority,omitempty"`
-	RecordType string   `json:"record_type"`
-	Valid      string   `json:"valid"`
-	Value      string   `json:"value"`
-}
-
-type GetDomainResponse struct {
-	Domain              Domain      `json:"domain"`
-	ReceivingDNSRecords []DNSRecord `json:"receiving_dns_records"`
-	SendingDNSRecords   []DNSRecord `json:"sending_dns_records"`
-}
-
-type domainConnectionResponse struct {
-	Connection DomainConnection `json:"connection"`
-}
-
-type domainsListResponse struct {
-	// is -1 if Next() or First() have not been called
-	TotalCount int      `json:"total_count"`
-	Items      []Domain `json:"items"`
-}
-
-// Specify the domain connection options
-type DomainConnection struct {
-	RequireTLS       bool `json:"require_tls"`
-	SkipVerification bool `json:"skip_verification"`
-}
-
-// Specify the domain tracking options
-type DomainTracking struct {
-	Click       TrackingStatus `json:"click"`
-	Open        TrackingStatus `json:"open"`
-	Unsubscribe TrackingStatus `json:"unsubscribe"`
-}
-
-// The tracking status of a domain
-type TrackingStatus struct {
-	Active     bool   `json:"active"`
-	HTMLFooter string `json:"html_footer"`
-	TextFooter string `json:"text_footer"`
-}
-
-type domainTrackingResponse struct {
-	Tracking DomainTracking `json:"tracking"`
-}
-
 type ListDomainsOptions struct {
 	Limit int
 }
@@ -107,13 +25,13 @@ func (mg *MailgunImpl) ListDomains(opts *ListDomainsOptions) *DomainsIterator {
 	return &DomainsIterator{
 		mg:                  mg,
 		url:                 generateApiUrl(mg, 4, domainsEndpoint),
-		domainsListResponse: domainsListResponse{TotalCount: -1},
+		ListDomainsResponse: mtypes.ListDomainsResponse{TotalCount: -1},
 		limit:               limit,
 	}
 }
 
 type DomainsIterator struct {
-	domainsListResponse
+	mtypes.ListDomainsResponse
 
 	limit  int
 	mg     Mailgun
@@ -135,7 +53,7 @@ func (ri *DomainsIterator) Offset() int {
 // Next retrieves the next page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error
-func (ri *DomainsIterator) Next(ctx context.Context, items *[]Domain) bool {
+func (ri *DomainsIterator) Next(ctx context.Context, items *[]mtypes.Domain) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -145,7 +63,7 @@ func (ri *DomainsIterator) Next(ctx context.Context, items *[]Domain) bool {
 		return false
 	}
 
-	cpy := make([]Domain, len(ri.Items))
+	cpy := make([]mtypes.Domain, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	if len(ri.Items) == 0 {
@@ -158,7 +76,7 @@ func (ri *DomainsIterator) Next(ctx context.Context, items *[]Domain) bool {
 // First retrieves the first page of items from the api. Returns false if there
 // was an error. It also sets the iterator object to the first page.
 // Use `.Err()` to retrieve the error.
-func (ri *DomainsIterator) First(ctx context.Context, items *[]Domain) bool {
+func (ri *DomainsIterator) First(ctx context.Context, items *[]mtypes.Domain) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -166,7 +84,7 @@ func (ri *DomainsIterator) First(ctx context.Context, items *[]Domain) bool {
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Domain, len(ri.Items))
+	cpy := make([]mtypes.Domain, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	ri.offset = len(ri.Items)
@@ -177,7 +95,7 @@ func (ri *DomainsIterator) First(ctx context.Context, items *[]Domain) bool {
 // Calling Last() is invalid unless you first call First() or Next()
 // Returns false if there was an error. It also sets the iterator object
 // to the last page. Use `.Err()` to retrieve the error.
-func (ri *DomainsIterator) Last(ctx context.Context, items *[]Domain) bool {
+func (ri *DomainsIterator) Last(ctx context.Context, items *[]mtypes.Domain) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -195,7 +113,7 @@ func (ri *DomainsIterator) Last(ctx context.Context, items *[]Domain) bool {
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Domain, len(ri.Items))
+	cpy := make([]mtypes.Domain, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	return true
@@ -204,7 +122,7 @@ func (ri *DomainsIterator) Last(ctx context.Context, items *[]Domain) bool {
 // Previous retrieves the previous page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error if any
-func (ri *DomainsIterator) Previous(ctx context.Context, items *[]Domain) bool {
+func (ri *DomainsIterator) Previous(ctx context.Context, items *[]mtypes.Domain) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -222,7 +140,7 @@ func (ri *DomainsIterator) Previous(ctx context.Context, items *[]Domain) bool {
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Domain, len(ri.Items))
+	cpy := make([]mtypes.Domain, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 
@@ -242,28 +160,28 @@ func (ri *DomainsIterator) fetch(ctx context.Context, skip, limit int) error {
 		r.addParameter("limit", strconv.Itoa(limit))
 	}
 
-	return getResponseFromJSON(ctx, r, &ri.domainsListResponse)
+	return getResponseFromJSON(ctx, r, &ri.ListDomainsResponse)
 }
 
 type GetDomainOptions struct{}
 
 // GetDomain retrieves detailed information about the named domain.
-func (mg *MailgunImpl) GetDomain(ctx context.Context, domain string, _ *GetDomainOptions) (GetDomainResponse, error) {
+func (mg *MailgunImpl) GetDomain(ctx context.Context, domain string, _ *GetDomainOptions) (mtypes.GetDomainResponse, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 4, domainsEndpoint) + "/" + domain)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
-	var resp GetDomainResponse
+	var resp mtypes.GetDomainResponse
 	err := getResponseFromJSON(ctx, r, &resp)
 	return resp, err
 }
 
-func (mg *MailgunImpl) VerifyDomain(ctx context.Context, domain string) (GetDomainResponse, error) {
+func (mg *MailgunImpl) VerifyDomain(ctx context.Context, domain string) (mtypes.GetDomainResponse, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 4, domainsEndpoint) + "/" + domain + "/verify")
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
 	payload := newUrlEncodedPayload()
-	var resp GetDomainResponse
+	var resp mtypes.GetDomainResponse
 	err := putResponseFromJSON(ctx, r, payload, &resp)
 	return resp, err
 }
@@ -273,7 +191,7 @@ func (mg *MailgunImpl) VerifyDomain(ctx context.Context, domain string) (GetDoma
 // TODO(vtopc): support all fields
 type CreateDomainOptions struct {
 	Password           string
-	SpamAction         SpamAction
+	SpamAction         mtypes.SpamAction
 	Wildcard           bool
 	ForceDKIMAuthority bool
 	DKIMKeySize        int
@@ -287,7 +205,7 @@ type CreateDomainOptions struct {
 // The spamAction domain must be one of Delete, Tag, or Disabled.
 // The wildcard parameter instructs Mailgun to treat all subdomains of this domain uniformly if true,
 // and as different domains if false.
-func (mg *MailgunImpl) CreateDomain(ctx context.Context, name string, opts *CreateDomainOptions) (GetDomainResponse, error) {
+func (mg *MailgunImpl) CreateDomain(ctx context.Context, name string, opts *CreateDomainOptions) (mtypes.GetDomainResponse, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 4, domainsEndpoint))
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
@@ -318,7 +236,7 @@ func (mg *MailgunImpl) CreateDomain(ctx context.Context, name string, opts *Crea
 			payload.addValue("web_scheme", opts.WebScheme)
 		}
 	}
-	var resp GetDomainResponse
+	var resp mtypes.GetDomainResponse
 	err := postResponseFromJSON(ctx, r, payload, &resp)
 	return resp, err
 }
@@ -360,10 +278,3 @@ func (mg *MailgunImpl) UpdateDomain(ctx context.Context, name string, opts *Upda
 
 	return err
 }
-
-func boolToString(b bool) string {
-	if b {
-		return "true"
-	}
-	return "false"
-}
diff --git a/domains_connection.go b/domains_connection.go
index 6ecbb53..efca21c 100644
--- a/domains_connection.go
+++ b/domains_connection.go
@@ -2,20 +2,22 @@ package mailgun
 
 import (
 	"context"
+
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 // GetDomainConnection returns delivery connection settings for the defined domain
-func (mg *MailgunImpl) GetDomainConnection(ctx context.Context, domain string) (DomainConnection, error) {
+func (mg *MailgunImpl) GetDomainConnection(ctx context.Context, domain string) (mtypes.DomainConnection, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/connection")
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
-	var resp domainConnectionResponse
+	var resp mtypes.DomainConnectionResponse
 	err := getResponseFromJSON(ctx, r, &resp)
 	return resp.Connection, err
 }
 
 // UpdateDomainConnection updates the specified delivery connection settings for the defined domain
-func (mg *MailgunImpl) UpdateDomainConnection(ctx context.Context, domain string, settings DomainConnection) error {
+func (mg *MailgunImpl) UpdateDomainConnection(ctx context.Context, domain string, settings mtypes.DomainConnection) error {
 	r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/connection")
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
diff --git a/domains_tracking.go b/domains_tracking.go
index a0a029d..3be52c8 100644
--- a/domains_tracking.go
+++ b/domains_tracking.go
@@ -2,14 +2,16 @@ package mailgun
 
 import (
 	"context"
+
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 // GetDomainTracking returns tracking settings for a domain
-func (mg *MailgunImpl) GetDomainTracking(ctx context.Context, domain string) (DomainTracking, error) {
+func (mg *MailgunImpl) GetDomainTracking(ctx context.Context, domain string) (mtypes.DomainTracking, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/tracking")
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
-	var resp domainTrackingResponse
+	var resp mtypes.DomainTrackingResponse
 	err := getResponseFromJSON(ctx, r, &resp)
 	return resp.Tracking, err
 }
diff --git a/events.go b/events.go
index ee311d8..2ec47cd 100644
--- a/events.go
+++ b/events.go
@@ -279,8 +279,3 @@ func (ep *EventPoller) Poll(ctx context.Context, ee *[]Event) bool {
 		}
 	}
 }
-
-// Given time.Time{} return a float64 as given in mailgun event timestamps
-func TimeToFloat(t time.Time) float64 {
-	return float64(t.Unix()) + (float64(t.Nanosecond()/int(time.Microsecond)) / float64(1000000))
-}
diff --git a/examples/examples.go b/examples/examples.go
index 6e1814b..79ba1da 100644
--- a/examples/examples.go
+++ b/examples/examples.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/mailgun/mailgun-go/v4"
 	"github.com/mailgun/mailgun-go/v4/events"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func AddBounce(domain, apiKey string) error {
@@ -28,7 +29,7 @@ func CreateComplaint(domain, apiKey string) error {
 	return mg.CreateComplaint(ctx, domain, "bob@example.com")
 }
 
-func AddDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) {
+func AddDomain(domain, apiKey string) (mtypes.GetDomainResponse, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -36,7 +37,7 @@ func AddDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) {
 
 	return mg.CreateDomain(ctx, domain, &mailgun.CreateDomainOptions{
 		Password:   "super_secret",
-		SpamAction: mailgun.SpamActionTag,
+		SpamAction: mtypes.SpamActionTag,
 		Wildcard:   false,
 	})
 }
@@ -53,10 +54,10 @@ func AddDomainIPS(domain, apiKey string) error {
 func AddListMember(apiKey string) error {
 	mg := mailgun.NewMailgun(apiKey)
 
-	memberJoe := mailgun.Member{
+	memberJoe := mtypes.Member{
 		Address:    "joe@example.com",
 		Name:       "Joe Example",
-		Subscribed: mailgun.Subscribed,
+		Subscribed: mtypes.Subscribed,
 	}
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -72,17 +73,17 @@ func AddListMembers(apiKey string) error {
 	defer cancel()
 
 	return mg.CreateMemberList(ctx, nil, "mailgunList@example.com", []any{
-		mailgun.Member{
+		mtypes.Member{
 			Address:    "alice@example.com",
 			Name:       "Alice's debugging account",
-			Subscribed: mailgun.Unsubscribed,
+			Subscribed: mtypes.Unsubscribed,
 		},
-		mailgun.Member{
+		mtypes.Member{
 			Address:    "Bob Cool <bob@example.com>",
 			Name:       "Bob's Cool Account",
-			Subscribed: mailgun.Subscribed,
+			Subscribed: mtypes.Subscribed,
 		},
-		mailgun.Member{
+		mtypes.Member{
 			Address: "joe.hamradio@example.com",
 			// Charlette is a ham radio packet BBS user.
 			// We attach her packet BBS e-mail address as an arbitrary var here.
@@ -108,7 +109,7 @@ func CreateUnsubscribes(domain, apiKey string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	unsubscribes := []mailgun.Unsubscribe{
+	unsubscribes := []mtypes.Unsubscribe{
 		{Address: "alice@example.com"},
 		{Address: "bob@example.com", Tags: []string{"tag1"}},
 	}
@@ -152,7 +153,7 @@ func CreateCredential(domain, apiKey string) error {
 	return mg.CreateCredential(ctx, domain, "alice@example.com", "secret")
 }
 
-func CreateDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) {
+func CreateDomain(domain, apiKey string) (mtypes.GetDomainResponse, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -160,7 +161,7 @@ func CreateDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) {
 
 	return mg.CreateDomain(ctx, domain, &mailgun.CreateDomainOptions{
 		Password:   "super_secret",
-		SpamAction: mailgun.SpamActionTag,
+		SpamAction: mtypes.SpamActionTag,
 		Wildcard:   false,
 	})
 }
@@ -174,27 +175,27 @@ func CreateExport(apiKey string) error {
 	return mg.CreateExport(ctx, "/v3/domains")
 }
 
-func CreateMailingList(apiKey string) (mailgun.MailingList, error) {
+func CreateMailingList(apiKey string) (mtypes.MailingList, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	return mg.CreateMailingList(ctx, mailgun.MailingList{
+	return mg.CreateMailingList(ctx, mtypes.MailingList{
 		Address:     "list@example.com",
 		Name:        "dev",
 		Description: "Mailgun developers list.",
-		AccessLevel: mailgun.AccessLevelMembers,
+		AccessLevel: mtypes.AccessLevelMembers,
 	})
 }
 
-func CreateRoute(domain, apiKey string) (mailgun.Route, error) {
+func CreateRoute(domain, apiKey string) (mtypes.Route, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	return mg.CreateRoute(ctx, mailgun.Route{
+	return mg.CreateRoute(ctx, mtypes.Route{
 		Priority:    1,
 		Description: "Sample Route",
 		Expression:  "match_recipient(\".*@YOUR_DOMAIN_NAME\")",
@@ -336,7 +337,7 @@ func PrintEvents(domain, apiKey string) error {
 	return nil
 }
 
-func GetBounce(domain, apiKey string) (mailgun.Bounce, error) {
+func GetBounce(domain, apiKey string) (mtypes.Bounce, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -345,14 +346,14 @@ func GetBounce(domain, apiKey string) (mailgun.Bounce, error) {
 	return mg.GetBounce(ctx, domain, "foo@bar.com")
 }
 
-func ListBounces(domain, apiKey string) ([]mailgun.Bounce, error) {
+func ListBounces(domain, apiKey string) ([]mtypes.Bounce, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListBounces(domain, nil)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.Bounce
+	var page, result []mtypes.Bounce
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
@@ -363,7 +364,7 @@ func ListBounces(domain, apiKey string) ([]mailgun.Bounce, error) {
 	return result, nil
 }
 
-func GetComplaints(domain, apiKey string) (mailgun.Complaint, error) {
+func GetComplaints(domain, apiKey string) (mtypes.Complaint, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -372,14 +373,14 @@ func GetComplaints(domain, apiKey string) (mailgun.Complaint, error) {
 	return mg.GetComplaint(ctx, domain, "baz@example.com")
 }
 
-func ListComplaints(domain, apiKey string) ([]mailgun.Complaint, error) {
+func ListComplaints(domain, apiKey string) ([]mtypes.Complaint, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListComplaints(domain, nil)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.Complaint
+	var page, result []mtypes.Complaint
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
@@ -390,7 +391,7 @@ func ListComplaints(domain, apiKey string) ([]mailgun.Complaint, error) {
 	return result, nil
 }
 
-func GetDomainConnection(domain, apiKey string) (mailgun.DomainConnection, error) {
+func GetDomainConnection(domain, apiKey string) (mtypes.DomainConnection, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -399,14 +400,14 @@ func GetDomainConnection(domain, apiKey string) (mailgun.DomainConnection, error
 	return mg.GetDomainConnection(ctx, domain)
 }
 
-func ListCredentials(domain, apiKey string) ([]mailgun.Credential, error) {
+func ListCredentials(domain, apiKey string) ([]mtypes.Credential, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListCredentials(domain, nil)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.Credential
+	var page, result []mtypes.Credential
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
@@ -417,7 +418,7 @@ func ListCredentials(domain, apiKey string) ([]mailgun.Credential, error) {
 	return result, nil
 }
 
-func GetDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) {
+func GetDomain(domain, apiKey string) (mtypes.GetDomainResponse, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -426,7 +427,7 @@ func GetDomain(domain, apiKey string) (mailgun.GetDomainResponse, error) {
 	return mg.GetDomain(ctx, domain, nil)
 }
 
-func ListDomainIPS(domain, apiKey string) ([]mailgun.IPAddress, error) {
+func ListDomainIPS(domain, apiKey string) ([]mtypes.IPAddress, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -435,7 +436,7 @@ func ListDomainIPS(domain, apiKey string) ([]mailgun.IPAddress, error) {
 	return mg.ListDomainIPS(ctx, domain)
 }
 
-func GetDomainTracking(domain, apiKey string) (mailgun.DomainTracking, error) {
+func GetDomainTracking(domain, apiKey string) (mtypes.DomainTracking, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -444,14 +445,14 @@ func GetDomainTracking(domain, apiKey string) (mailgun.DomainTracking, error) {
 	return mg.GetDomainTracking(ctx, domain)
 }
 
-func ListDomains(domain, apiKey string) ([]mailgun.Domain, error) {
+func ListDomains(domain, apiKey string) ([]mtypes.Domain, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListDomains(nil)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.Domain
+	var page, result []mtypes.Domain
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
@@ -462,7 +463,7 @@ func ListDomains(domain, apiKey string) ([]mailgun.Domain, error) {
 	return result, nil
 }
 
-func GetExport(domain, apiKey string) (mailgun.Export, error) {
+func GetExport(domain, apiKey string) (mtypes.Export, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -471,7 +472,7 @@ func GetExport(domain, apiKey string) (mailgun.Export, error) {
 	return mg.GetExport(ctx, "EXPORT_ID")
 }
 
-func GetIP(domain, apiKey string) (mailgun.IPAddress, error) {
+func GetIP(domain, apiKey string) (mtypes.IPAddress, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -480,7 +481,7 @@ func GetIP(domain, apiKey string) (mailgun.IPAddress, error) {
 	return mg.GetIP(ctx, "127.0.0.1")
 }
 
-func ListIPS(domain, apiKey string) ([]mailgun.IPAddress, error) {
+func ListIPS(domain, apiKey string) ([]mtypes.IPAddress, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -490,7 +491,7 @@ func ListIPS(domain, apiKey string) ([]mailgun.IPAddress, error) {
 	return mg.ListIPS(ctx, true)
 }
 
-func GetTagLimits(domain, apiKey string) (mailgun.TagLimits, error) {
+func GetTagLimits(domain, apiKey string) (mtypes.TagLimits, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -499,7 +500,7 @@ func GetTagLimits(domain, apiKey string) (mailgun.TagLimits, error) {
 	return mg.GetTagLimits(ctx, domain)
 }
 
-func ListExports(apiKey string) ([]mailgun.Export, error) {
+func ListExports(apiKey string) ([]mtypes.Export, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -509,14 +510,14 @@ func ListExports(apiKey string) ([]mailgun.Export, error) {
 	return mg.ListExports(ctx, "")
 }
 
-func GetMembers(apiKey string) ([]mailgun.Member, error) {
+func GetMembers(apiKey string) ([]mtypes.Member, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListMembers("list@example.com", nil)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.Member
+	var page, result []mtypes.Member
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
@@ -527,14 +528,14 @@ func GetMembers(apiKey string) ([]mailgun.Member, error) {
 	return result, nil
 }
 
-func ListMailingLists(apiKey string) ([]mailgun.MailingList, error) {
+func ListMailingLists(apiKey string) ([]mtypes.MailingList, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListMailingLists(nil)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.MailingList
+	var page, result []mtypes.MailingList
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
@@ -545,7 +546,7 @@ func ListMailingLists(apiKey string) ([]mailgun.MailingList, error) {
 	return result, nil
 }
 
-func GetRoute(domain, apiKey string) (mailgun.Route, error) {
+func GetRoute(domain, apiKey string) (mtypes.Route, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -554,14 +555,14 @@ func GetRoute(domain, apiKey string) (mailgun.Route, error) {
 	return mg.GetRoute(ctx, "route_id")
 }
 
-func ListRoutes(domain, apiKey string) ([]mailgun.Route, error) {
+func ListRoutes(domain, apiKey string) ([]mtypes.Route, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListRoutes(nil)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.Route
+	var page, result []mtypes.Route
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
@@ -572,14 +573,14 @@ func ListRoutes(domain, apiKey string) ([]mailgun.Route, error) {
 	return result, nil
 }
 
-func ListTags(domain, apiKey string) ([]mailgun.Tag, error) {
+func ListTags(domain, apiKey string) ([]mtypes.Tag, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListTags(domain, nil)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.Tag
+	var page, result []mtypes.Tag
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
@@ -590,14 +591,14 @@ func ListTags(domain, apiKey string) ([]mailgun.Tag, error) {
 	return result, nil
 }
 
-func ListUnsubscribes(domain, apiKey string) ([]mailgun.Unsubscribe, error) {
+func ListUnsubscribes(domain, apiKey string) ([]mtypes.Unsubscribe, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListUnsubscribes(domain, nil)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.Unsubscribe
+	var page, result []mtypes.Unsubscribe
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
@@ -608,7 +609,7 @@ func ListUnsubscribes(domain, apiKey string) ([]mailgun.Unsubscribe, error) {
 	return result, nil
 }
 
-func ValidateEmail(apiKey string) (mailgun.ValidateEmailResponse, error) {
+func ValidateEmail(apiKey string) (mtypes.ValidateEmailResponse, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -860,7 +861,7 @@ func UpdateDomainConnection(domain, apiKey string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	return mg.UpdateDomainConnection(ctx, domain, mailgun.DomainConnection{
+	return mg.UpdateDomainConnection(ctx, domain, mtypes.DomainConnection{
 		RequireTLS:       true,
 		SkipVerification: true,
 	})
@@ -872,9 +873,9 @@ func UpdateMember(apiKey string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	_, err := mg.UpdateMember(ctx, "bar@example.com", "list@example.com", mailgun.Member{
+	_, err := mg.UpdateMember(ctx, "bar@example.com", "list@example.com", mtypes.Member{
 		Name:       "Foo Bar",
-		Subscribed: mailgun.Unsubscribed,
+		Subscribed: mtypes.Unsubscribed,
 	})
 	return err
 }
@@ -892,7 +893,7 @@ func VerifyWebhookSignature(apiKey, webhookSigningKey, timestamp, token, signatu
 	mg := mailgun.NewMailgun(apiKey)
 	mg.SetWebhookSigningKey(webhookSigningKey)
 
-	return mg.VerifyWebhookSignature(mailgun.Signature{
+	return mg.VerifyWebhookSignature(mtypes.Signature{
 		TimeStamp: timestamp,
 		Token:     token,
 		Signature: signature,
@@ -907,11 +908,11 @@ func SendMessageWithTemplate(domain, apiKey string) error {
 	defer cancel()
 
 	// Create a new template
-	err = mg.CreateTemplate(ctx, domain, &mailgun.Template{
+	err = mg.CreateTemplate(ctx, domain, &mtypes.Template{
 		Name: "my-template",
-		Version: mailgun.TemplateVersion{
+		Version: mtypes.TemplateVersion{
 			Template: `'<div class="entry"> <h1>{{.title}}</h1> <div class="body"> {{.body}} </div> </div>'`,
-			Engine:   mailgun.TemplateEngineGo,
+			Engine:   mtypes.TemplateEngineGo,
 			Tag:      "v1",
 		},
 	})
@@ -945,11 +946,11 @@ func CreateTemplate(domain, apiKey string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	return mg.CreateTemplate(ctx, domain, &mailgun.Template{
+	return mg.CreateTemplate(ctx, domain, &mtypes.Template{
 		Name: "my-template",
-		Version: mailgun.TemplateVersion{
+		Version: mtypes.TemplateVersion{
 			Template: `'<div class="entry"> <h1>{{.title}}</h1> <div class="body"> {{.body}} </div> </div>'`,
-			Engine:   mailgun.TemplateEngineGo,
+			Engine:   mtypes.TemplateEngineGo,
 			Tag:      "v1",
 		},
 	})
@@ -970,13 +971,13 @@ func UpdateTemplate(domain, apiKey string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	return mg.UpdateTemplate(ctx, domain, &mailgun.Template{
+	return mg.UpdateTemplate(ctx, domain, &mtypes.Template{
 		Name:        "my-template",
 		Description: "Add a description to the template",
 	})
 }
 
-func GetTemplate(domain, apiKey string) (mailgun.Template, error) {
+func GetTemplate(domain, apiKey string) (mtypes.Template, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -985,14 +986,14 @@ func GetTemplate(domain, apiKey string) (mailgun.Template, error) {
 	return mg.GetTemplate(ctx, domain, "my-template")
 }
 
-func ListActiveTemplates(domain, apiKey string) ([]mailgun.Template, error) {
+func ListActiveTemplates(domain, apiKey string) ([]mtypes.Template, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListTemplates(domain, &mailgun.ListTemplateOptions{Active: true})
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.Template
+	var page, result []mtypes.Template
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
@@ -1003,14 +1004,14 @@ func ListActiveTemplates(domain, apiKey string) ([]mailgun.Template, error) {
 	return result, nil
 }
 
-func ListTemplates(domain, apiKey string) ([]mailgun.Template, error) {
+func ListTemplates(domain, apiKey string) ([]mtypes.Template, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListTemplates(domain, nil)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.Template
+	var page, result []mtypes.Template
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
@@ -1027,9 +1028,9 @@ func AddTemplateVersion(domain, apiKey string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	return mg.AddTemplateVersion(ctx, domain, "my-template", &mailgun.TemplateVersion{
+	return mg.AddTemplateVersion(ctx, domain, "my-template", &mtypes.TemplateVersion{
 		Template: `'<div class="entry"> <h1>{{.title}}</h1> <div class="body"> {{.body}} </div> </div>'`,
-		Engine:   mailgun.TemplateEngineGo,
+		Engine:   mtypes.TemplateEngineGo,
 		Tag:      "v2",
 		Active:   true,
 	})
@@ -1045,7 +1046,7 @@ func DeleteTemplateVersion(domain, apiKey string) error {
 	return mg.DeleteTemplateVersion(ctx, domain, "my-template", "v2")
 }
 
-func GetTemplateVersion(domain, apiKey string) (mailgun.TemplateVersion, error) {
+func GetTemplateVersion(domain, apiKey string) (mtypes.TemplateVersion, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -1061,21 +1062,21 @@ func UpdateTemplateVersion(domain, apiKey string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	return mg.UpdateTemplateVersion(ctx, domain, "my-template", &mailgun.TemplateVersion{
+	return mg.UpdateTemplateVersion(ctx, domain, "my-template", &mtypes.TemplateVersion{
 		Comment: "Add a comment to the template and make it 'active'",
 		Tag:     "v2",
 		Active:  true,
 	})
 }
 
-func ListTemplateVersions(domain, apiKey string) ([]mailgun.TemplateVersion, error) {
+func ListTemplateVersions(domain, apiKey string) ([]mtypes.TemplateVersion, error) {
 	mg := mailgun.NewMailgun(apiKey)
 	it := mg.ListTemplateVersions(domain, "my-template", nil)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	var page, result []mailgun.TemplateVersion
+	var page, result []mtypes.TemplateVersion
 	for it.Next(ctx, &page) {
 		result = append(result, page...)
 	}
diff --git a/exports.go b/exports.go
index c222374..88d3e78 100644
--- a/exports.go
+++ b/exports.go
@@ -5,19 +5,11 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
-)
-
-type ExportList struct {
-	Items []Export `json:"items"`
-}
 
-type Export struct {
-	ID     string `json:"id"`
-	Status string `json:"status"`
-	URL    string `json:"url"`
-}
+	"github.com/mailgun/mailgun-go/v4/mtypes"
+)
 
-// Create an export based on the URL given
+// CreateExport creates an export based on the URL given
 func (mg *MailgunImpl) CreateExport(ctx context.Context, url string) error {
 	r := newHTTPRequest(generateApiUrl(mg, 3, exportsEndpoint))
 	r.setClient(mg.HTTPClient())
@@ -29,8 +21,8 @@ func (mg *MailgunImpl) CreateExport(ctx context.Context, url string) error {
 	return err
 }
 
-// List all exports created within the past 24 hours
-func (mg *MailgunImpl) ListExports(ctx context.Context, url string) ([]Export, error) {
+// ListExports lists all exports created within the past 24 hours
+func (mg *MailgunImpl) ListExports(ctx context.Context, url string) ([]mtypes.Export, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, exportsEndpoint))
 	r.setClient(mg.HTTPClient())
 	if url != "" {
@@ -38,24 +30,24 @@ func (mg *MailgunImpl) ListExports(ctx context.Context, url string) ([]Export, e
 	}
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	var resp ExportList
+	var resp mtypes.ExportList
 	if err := getResponseFromJSON(ctx, r, &resp); err != nil {
 		return nil, err
 	}
 
-	var result []Export
+	var result []mtypes.Export
 	for _, item := range resp.Items {
-		result = append(result, Export(item))
+		result = append(result, mtypes.Export(item))
 	}
 	return result, nil
 }
 
 // GetExport gets an export by id
-func (mg *MailgunImpl) GetExport(ctx context.Context, id string) (Export, error) {
+func (mg *MailgunImpl) GetExport(ctx context.Context, id string) (mtypes.Export, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, exportsEndpoint) + "/" + id)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
-	var resp Export
+	var resp mtypes.Export
 	err := getResponseFromJSON(ctx, r, &resp)
 	return resp, err
 }
diff --git a/ips.go b/ips.go
index 7a94457..901f6e8 100644
--- a/ips.go
+++ b/ips.go
@@ -1,25 +1,13 @@
 package mailgun
 
-import "context"
+import (
+	"context"
 
-type ipAddressListResponse struct {
-	TotalCount int      `json:"total_count"`
-	Items      []string `json:"items"`
-}
-
-type IPAddress struct {
-	IP        string `json:"ip"`
-	RDNS      string `json:"rdns"`
-	Dedicated bool   `json:"dedicated"`
-}
-
-type okResp struct {
-	ID      string `json:"id,omitempty"`
-	Message string `json:"message"`
-}
+	"github.com/mailgun/mailgun-go/v4/mtypes"
+)
 
 // ListIPS returns a list of IPs assigned to your account
-func (mg *MailgunImpl) ListIPS(ctx context.Context, dedicated bool) ([]IPAddress, error) {
+func (mg *MailgunImpl) ListIPS(ctx context.Context, dedicated bool) ([]mtypes.IPAddress, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, ipsEndpoint))
 	r.setClient(mg.HTTPClient())
 	if dedicated {
@@ -27,40 +15,40 @@ func (mg *MailgunImpl) ListIPS(ctx context.Context, dedicated bool) ([]IPAddress
 	}
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	var resp ipAddressListResponse
+	var resp mtypes.IPAddressListResponse
 	if err := getResponseFromJSON(ctx, r, &resp); err != nil {
 		return nil, err
 	}
-	var result []IPAddress
+	var result []mtypes.IPAddress
 	for _, ip := range resp.Items {
-		result = append(result, IPAddress{IP: ip})
+		result = append(result, mtypes.IPAddress{IP: ip})
 	}
 	return result, nil
 }
 
 // GetIP returns information about the specified IP
-func (mg *MailgunImpl) GetIP(ctx context.Context, ip string) (IPAddress, error) {
+func (mg *MailgunImpl) GetIP(ctx context.Context, ip string) (mtypes.IPAddress, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, ipsEndpoint) + "/" + ip)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
-	var resp IPAddress
+	var resp mtypes.IPAddress
 	err := getResponseFromJSON(ctx, r, &resp)
 	return resp, err
 }
 
 // ListDomainIPS returns a list of IPs currently assigned to the specified domain.
-func (mg *MailgunImpl) ListDomainIPS(ctx context.Context, domain string) ([]IPAddress, error) {
+func (mg *MailgunImpl) ListDomainIPS(ctx context.Context, domain string) ([]mtypes.IPAddress, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/ips")
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	var resp ipAddressListResponse
+	var resp mtypes.IPAddressListResponse
 	if err := getResponseFromJSON(ctx, r, &resp); err != nil {
 		return nil, err
 	}
-	var result []IPAddress
+	var result []mtypes.IPAddress
 	for _, ip := range resp.Items {
-		result = append(result, IPAddress{IP: ip})
+		result = append(result, mtypes.IPAddress{IP: ip})
 	}
 	return result, nil
 }
diff --git a/limits.go b/limits.go
index 2e73ead..bcb30c7 100644
--- a/limits.go
+++ b/limits.go
@@ -1,18 +1,17 @@
 package mailgun
 
-import "context"
+import (
+	"context"
 
-type TagLimits struct {
-	Limit int `json:"limit"`
-	Count int `json:"count"`
-}
+	"github.com/mailgun/mailgun-go/v4/mtypes"
+)
 
 // GetTagLimits returns tracking settings for a domain
-func (mg *MailgunImpl) GetTagLimits(ctx context.Context, domain string) (TagLimits, error) {
+func (mg *MailgunImpl) GetTagLimits(ctx context.Context, domain string) (mtypes.TagLimits, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/limits/tag")
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
-	var resp TagLimits
+	var resp mtypes.TagLimits
 	err := getResponseFromJSON(ctx, r, &resp)
 	return resp, err
 }
diff --git a/mailgun.go b/mailgun.go
index edb3985..d851851 100644
--- a/mailgun.go
+++ b/mailgun.go
@@ -80,7 +80,10 @@ import (
 	"fmt"
 	"net/http"
 	"os"
+	"strconv"
 	"time"
+
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 // Debug set true to write the HTTP requests in curl for to stdout
@@ -134,35 +137,35 @@ type Mailgun interface {
 	ReSend(ctx context.Context, id string, recipients ...string) (string, string, error)
 
 	ListBounces(domain string, opts *ListOptions) *BouncesIterator
-	GetBounce(ctx context.Context, domain, address string) (Bounce, error)
+	GetBounce(ctx context.Context, domain, address string) (mtypes.Bounce, error)
 	AddBounce(ctx context.Context, domain, address, code, err string) error
 	DeleteBounce(ctx context.Context, domain, address string) error
 	DeleteBounceList(ctx context.Context, domain string) error
 
 	ListMetrics(opts MetricsOptions) (*MetricsIterator, error)
 
-	GetTag(ctx context.Context, domain, tag string) (Tag, error)
+	GetTag(ctx context.Context, domain, tag string) (mtypes.Tag, error)
 	DeleteTag(ctx context.Context, domain, tag string) error
 	ListTags(domain string, opts *ListTagOptions) *TagIterator
 
 	ListDomains(opts *ListDomainsOptions) *DomainsIterator
-	GetDomain(ctx context.Context, domain string, opts *GetDomainOptions) (GetDomainResponse, error)
-	CreateDomain(ctx context.Context, name string, opts *CreateDomainOptions) (GetDomainResponse, error)
+	GetDomain(ctx context.Context, domain string, opts *GetDomainOptions) (mtypes.GetDomainResponse, error)
+	CreateDomain(ctx context.Context, name string, opts *CreateDomainOptions) (mtypes.GetDomainResponse, error)
 	DeleteDomain(ctx context.Context, name string) error
-	VerifyDomain(ctx context.Context, name string) (GetDomainResponse, error)
+	VerifyDomain(ctx context.Context, name string) (mtypes.GetDomainResponse, error)
 
-	UpdateDomainConnection(ctx context.Context, domain string, dc DomainConnection) error
-	GetDomainConnection(ctx context.Context, domain string) (DomainConnection, error)
+	UpdateDomainConnection(ctx context.Context, domain string, dc mtypes.DomainConnection) error
+	GetDomainConnection(ctx context.Context, domain string) (mtypes.DomainConnection, error)
 
-	GetDomainTracking(ctx context.Context, domain string) (DomainTracking, error)
+	GetDomainTracking(ctx context.Context, domain string) (mtypes.DomainTracking, error)
 	UpdateClickTracking(ctx context.Context, domain, active string) error
 	UpdateUnsubscribeTracking(ctx context.Context, domain, active, htmlFooter, textFooter string) error
 	UpdateOpenTracking(ctx context.Context, domain, active string) error
 
 	UpdateDomainDkimSelector(ctx context.Context, domain, dkimSelector string) error
 
-	GetStoredMessage(ctx context.Context, url string) (StoredMessage, error)
-	GetStoredMessageRaw(ctx context.Context, id string) (StoredMessageRaw, error)
+	GetStoredMessage(ctx context.Context, url string) (mtypes.StoredMessage, error)
+	GetStoredMessageRaw(ctx context.Context, id string) (mtypes.StoredMessageRaw, error)
 	GetStoredAttachment(ctx context.Context, url string) ([]byte, error)
 
 	ListCredentials(domain string, opts *ListOptions) *CredentialsIterator
@@ -171,81 +174,81 @@ type Mailgun interface {
 	DeleteCredential(ctx context.Context, domain, login string) error
 
 	ListUnsubscribes(domain string, opts *ListOptions) *UnsubscribesIterator
-	GetUnsubscribe(ctx context.Context, domain, address string) (Unsubscribe, error)
+	GetUnsubscribe(ctx context.Context, domain, address string) (mtypes.Unsubscribe, error)
 	CreateUnsubscribe(ctx context.Context, domain, address, tag string) error
-	CreateUnsubscribes(ctx context.Context, domain string, unsubscribes []Unsubscribe) error
+	CreateUnsubscribes(ctx context.Context, domain string, unsubscribes []mtypes.Unsubscribe) error
 	DeleteUnsubscribe(ctx context.Context, domain, address string) error
 	DeleteUnsubscribeWithTag(ctx context.Context, domain, a, t string) error
 
 	ListComplaints(domain string, opts *ListOptions) *ComplaintsIterator
-	GetComplaint(ctx context.Context, domain, address string) (Complaint, error)
+	GetComplaint(ctx context.Context, domain, address string) (mtypes.Complaint, error)
 	CreateComplaint(ctx context.Context, domain, address string) error
 	CreateComplaints(ctx context.Context, domain string, addresses []string) error
 	DeleteComplaint(ctx context.Context, domain, address string) error
 
 	ListRoutes(opts *ListOptions) *RoutesIterator
-	GetRoute(ctx context.Context, address string) (Route, error)
-	CreateRoute(ctx context.Context, address Route) (Route, error)
+	GetRoute(ctx context.Context, address string) (mtypes.Route, error)
+	CreateRoute(ctx context.Context, address mtypes.Route) (mtypes.Route, error)
 	DeleteRoute(ctx context.Context, address string) error
-	UpdateRoute(ctx context.Context, address string, r Route) (Route, error)
+	UpdateRoute(ctx context.Context, address string, r mtypes.Route) (mtypes.Route, error)
 
 	ListWebhooks(ctx context.Context, domain string) (map[string][]string, error)
 	CreateWebhook(ctx context.Context, domain, kind string, url []string) error
 	DeleteWebhook(ctx context.Context, domain, kind string) error
 	GetWebhook(ctx context.Context, domain, kind string) ([]string, error)
 	UpdateWebhook(ctx context.Context, domain, kind string, url []string) error
-	VerifyWebhookSignature(sig Signature) (verified bool, err error)
+	VerifyWebhookSignature(sig mtypes.Signature) (verified bool, err error)
 
 	ListMailingLists(opts *ListOptions) *ListsIterator
-	CreateMailingList(ctx context.Context, address MailingList) (MailingList, error)
+	CreateMailingList(ctx context.Context, address mtypes.MailingList) (mtypes.MailingList, error)
 	DeleteMailingList(ctx context.Context, address string) error
-	GetMailingList(ctx context.Context, address string) (MailingList, error)
-	UpdateMailingList(ctx context.Context, address string, ml MailingList) (MailingList, error)
+	GetMailingList(ctx context.Context, address string) (mtypes.MailingList, error)
+	UpdateMailingList(ctx context.Context, address string, ml mtypes.MailingList) (mtypes.MailingList, error)
 
 	ListMembers(address string, opts *ListOptions) *MemberListIterator
-	GetMember(ctx context.Context, MemberAddr, listAddr string) (Member, error)
-	CreateMember(ctx context.Context, merge bool, addr string, prototype Member) error
+	GetMember(ctx context.Context, MemberAddr, listAddr string) (mtypes.Member, error)
+	CreateMember(ctx context.Context, merge bool, addr string, prototype mtypes.Member) error
 	CreateMemberList(ctx context.Context, subscribed *bool, addr string, newMembers []any) error
-	UpdateMember(ctx context.Context, Member, list string, prototype Member) (Member, error)
+	UpdateMember(ctx context.Context, Member, list string, prototype mtypes.Member) (mtypes.Member, error)
 	DeleteMember(ctx context.Context, Member, list string) error
 
 	ListEvents(domain string, opts *ListEventOptions) *EventIterator
 	PollEvents(domain string, opts *ListEventOptions) *EventPoller
 
-	ListIPS(ctx context.Context, dedicated bool) ([]IPAddress, error)
-	GetIP(ctx context.Context, ip string) (IPAddress, error)
-	ListDomainIPS(ctx context.Context, domain string) ([]IPAddress, error)
+	ListIPS(ctx context.Context, dedicated bool) ([]mtypes.IPAddress, error)
+	GetIP(ctx context.Context, ip string) (mtypes.IPAddress, error)
+	ListDomainIPS(ctx context.Context, domain string) ([]mtypes.IPAddress, error)
 	AddDomainIP(ctx context.Context, domain, ip string) error
 	DeleteDomainIP(ctx context.Context, domain, ip string) error
 
-	ListExports(ctx context.Context, url string) ([]Export, error)
-	GetExport(ctx context.Context, id string) (Export, error)
+	ListExports(ctx context.Context, url string) ([]mtypes.Export, error)
+	GetExport(ctx context.Context, id string) (mtypes.Export, error)
 	GetExportLink(ctx context.Context, id string) (string, error)
 	CreateExport(ctx context.Context, url string) error
 
-	GetTagLimits(ctx context.Context, domain string) (TagLimits, error)
+	GetTagLimits(ctx context.Context, domain string) (mtypes.TagLimits, error)
 
-	CreateTemplate(ctx context.Context, domain string, template *Template) error
-	GetTemplate(ctx context.Context, domain, name string) (Template, error)
-	UpdateTemplate(ctx context.Context, domain string, template *Template) error
+	CreateTemplate(ctx context.Context, domain string, template *mtypes.Template) error
+	GetTemplate(ctx context.Context, domain, name string) (mtypes.Template, error)
+	UpdateTemplate(ctx context.Context, domain string, template *mtypes.Template) error
 	DeleteTemplate(ctx context.Context, domain, name string) error
 	ListTemplates(domain string, opts *ListTemplateOptions) *TemplatesIterator
 
-	AddTemplateVersion(ctx context.Context, domain, templateName string, version *TemplateVersion) error
-	GetTemplateVersion(ctx context.Context, domain, templateName, tag string) (TemplateVersion, error)
-	UpdateTemplateVersion(ctx context.Context, domain, templateName string, version *TemplateVersion) error
+	AddTemplateVersion(ctx context.Context, domain, templateName string, version *mtypes.TemplateVersion) error
+	GetTemplateVersion(ctx context.Context, domain, templateName, tag string) (mtypes.TemplateVersion, error)
+	UpdateTemplateVersion(ctx context.Context, domain, templateName string, version *mtypes.TemplateVersion) error
 	DeleteTemplateVersion(ctx context.Context, domain, templateName, tag string) error
 	ListTemplateVersions(domain, templateName string, opts *ListOptions) *TemplateVersionsIterator
 
-	ValidateEmail(ctx context.Context, email string, mailBoxVerify bool) (ValidateEmailResponse, error)
+	ValidateEmail(ctx context.Context, email string, mailBoxVerify bool) (mtypes.ValidateEmailResponse, error)
 
 	ListSubaccounts(opts *ListSubaccountsOptions) *SubaccountsIterator
-	CreateSubaccount(ctx context.Context, subaccountName string) (SubaccountResponse, error)
-	GetSubaccount(ctx context.Context, subaccountId string) (SubaccountResponse, error)
-	EnableSubaccount(ctx context.Context, subaccountId string) (SubaccountResponse, error)
-	DisableSubaccount(ctx context.Context, subaccountId string) (SubaccountResponse, error)
+	CreateSubaccount(ctx context.Context, subaccountName string) (mtypes.SubaccountResponse, error)
+	GetSubaccount(ctx context.Context, subaccountID string) (mtypes.SubaccountResponse, error)
+	EnableSubaccount(ctx context.Context, subaccountID string) (mtypes.SubaccountResponse, error)
+	DisableSubaccount(ctx context.Context, subaccountID string) (mtypes.SubaccountResponse, error)
 
-	SetOnBehalfOfSubaccount(subaccountId string)
+	SetOnBehalfOfSubaccount(subaccountID string)
 	RemoveOnBehalfOfSubaccount()
 }
 
@@ -369,6 +372,11 @@ type ListOptions struct {
 	Limit int
 }
 
+// TimeToFloat given time.Time{} return a float64 as given in mailgun event timestamps
+func TimeToFloat(t time.Time) float64 {
+	return float64(t.Unix()) + (float64(t.Nanosecond()/int(time.Microsecond)) / float64(1000000))
+}
+
 // TODO(vtopc): sort all these generate URL functions(some are generateApi...Url, other are generate...ApiUrl)
 
 func generateApiUrlWithDomain(m Mailgun, version int, endpoint, domain string) string {
@@ -427,3 +435,8 @@ func formatMailgunTime(t time.Time) string {
 func ptr[T any](v T) *T {
 	return &v
 }
+
+// TODO(vtopc): remove boolToString and use strconv.FormatBool() directly.
+func boolToString(b bool) string {
+	return strconv.FormatBool(b)
+}
diff --git a/mailing_lists.go b/mailing_lists.go
index a1f3613..10a7aee 100644
--- a/mailing_lists.go
+++ b/mailing_lists.go
@@ -3,60 +3,12 @@ package mailgun
 import (
 	"context"
 	"strconv"
-)
-
-// A mailing list may have one of three membership modes.
-const (
-	// ReadOnly specifies that nobody, including Members, may send messages to
-	// the mailing list.  Messages distributed on such lists come from list
-	// administrator accounts only.
-	AccessLevelReadOnly = "readonly"
-	// Members specifies that only those who subscribe to the mailing list may
-	// send messages.
-	AccessLevelMembers = "members"
-	// Everyone specifies that anyone and everyone may both read and submit
-	// messages to the mailing list, including non-subscribers.
-	AccessLevelEveryone = "everyone"
-)
 
-// Specify the access of a mailing list member
-type AccessLevel string
-
-// Replies to a mailing list should go to one of two preferred destinations.
-const (
-	// List specifies that replies should be sent to the mailing list address.
-	ReplyPreferenceList = "list"
-	// Sender specifies that replies should be sent to the sender (FROM) address.
-	ReplyPreferenceSender = "sender"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
-// Set where replies should go
-type ReplyPreference string
-
-// A List structure provides information for a mailing list.
-//
-// AccessLevel may be one of ReadOnly, Members, or Everyone.
-type MailingList struct {
-	Address         string          `json:"address,omitempty"`
-	Name            string          `json:"name,omitempty"`
-	Description     string          `json:"description,omitempty"`
-	AccessLevel     AccessLevel     `json:"access_level,omitempty"`
-	ReplyPreference ReplyPreference `json:"reply_preference,omitempty"`
-	CreatedAt       RFC2822Time     `json:"created_at,omitempty"`
-	MembersCount    int             `json:"members_count,omitempty"`
-}
-
-type listsResponse struct {
-	Items  []MailingList `json:"items"`
-	Paging Paging        `json:"paging"`
-}
-
-type mailingListResponse struct {
-	MailingList MailingList `json:"list"`
-}
-
 type ListsIterator struct {
-	listsResponse
+	mtypes.ListMailingListsResponse
 	mg  Mailgun
 	err error
 }
@@ -73,9 +25,9 @@ func (mg *MailgunImpl) ListMailingLists(opts *ListOptions) *ListsIterator {
 	}
 	url, err := r.generateUrlWithParameters()
 	return &ListsIterator{
-		mg:            mg,
-		listsResponse: listsResponse{Paging: Paging{Next: url, First: url}},
-		err:           err,
+		mg:                       mg,
+		ListMailingListsResponse: mtypes.ListMailingListsResponse{Paging: mtypes.Paging{Next: url, First: url}},
+		err:                      err,
 	}
 }
 
@@ -87,7 +39,7 @@ func (li *ListsIterator) Err() error {
 // Next retrieves the next page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error
-func (li *ListsIterator) Next(ctx context.Context, items *[]MailingList) bool {
+func (li *ListsIterator) Next(ctx context.Context, items *[]mtypes.MailingList) bool {
 	if li.err != nil {
 		return false
 	}
@@ -95,7 +47,7 @@ func (li *ListsIterator) Next(ctx context.Context, items *[]MailingList) bool {
 	if li.err != nil {
 		return false
 	}
-	cpy := make([]MailingList, len(li.Items))
+	cpy := make([]mtypes.MailingList, len(li.Items))
 	copy(cpy, li.Items)
 	*items = cpy
 
@@ -105,7 +57,7 @@ func (li *ListsIterator) Next(ctx context.Context, items *[]MailingList) bool {
 // First retrieves the first page of items from the api. Returns false if there
 // was an error. It also sets the iterator object to the first page.
 // Use `.Err()` to retrieve the error.
-func (li *ListsIterator) First(ctx context.Context, items *[]MailingList) bool {
+func (li *ListsIterator) First(ctx context.Context, items *[]mtypes.MailingList) bool {
 	if li.err != nil {
 		return false
 	}
@@ -113,7 +65,7 @@ func (li *ListsIterator) First(ctx context.Context, items *[]MailingList) bool {
 	if li.err != nil {
 		return false
 	}
-	cpy := make([]MailingList, len(li.Items))
+	cpy := make([]mtypes.MailingList, len(li.Items))
 	copy(cpy, li.Items)
 	*items = cpy
 	return true
@@ -123,7 +75,7 @@ func (li *ListsIterator) First(ctx context.Context, items *[]MailingList) bool {
 // Calling Last() is invalid unless you first call First() or Next()
 // Returns false if there was an error. It also sets the iterator object
 // to the last page. Use `.Err()` to retrieve the error.
-func (li *ListsIterator) Last(ctx context.Context, items *[]MailingList) bool {
+func (li *ListsIterator) Last(ctx context.Context, items *[]mtypes.MailingList) bool {
 	if li.err != nil {
 		return false
 	}
@@ -131,7 +83,7 @@ func (li *ListsIterator) Last(ctx context.Context, items *[]MailingList) bool {
 	if li.err != nil {
 		return false
 	}
-	cpy := make([]MailingList, len(li.Items))
+	cpy := make([]mtypes.MailingList, len(li.Items))
 	copy(cpy, li.Items)
 	*items = cpy
 	return true
@@ -140,7 +92,7 @@ func (li *ListsIterator) Last(ctx context.Context, items *[]MailingList) bool {
 // Previous retrieves the previous page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error if any
-func (li *ListsIterator) Previous(ctx context.Context, items *[]MailingList) bool {
+func (li *ListsIterator) Previous(ctx context.Context, items *[]mtypes.MailingList) bool {
 	if li.err != nil {
 		return false
 	}
@@ -151,7 +103,7 @@ func (li *ListsIterator) Previous(ctx context.Context, items *[]MailingList) boo
 	if li.err != nil {
 		return false
 	}
-	cpy := make([]MailingList, len(li.Items))
+	cpy := make([]mtypes.MailingList, len(li.Items))
 	copy(cpy, li.Items)
 	*items = cpy
 
@@ -164,7 +116,7 @@ func (li *ListsIterator) fetch(ctx context.Context, url string) error {
 	r.setClient(li.mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, li.mg.APIKey())
 
-	return getResponseFromJSON(ctx, r, &li.listsResponse)
+	return getResponseFromJSON(ctx, r, &li.ListMailingListsResponse)
 }
 
 // CreateMailingList creates a new mailing list under your Mailgun account.
@@ -173,7 +125,7 @@ func (li *ListsIterator) fetch(ctx context.Context, url string) error {
 // If unspecified, Description remains blank,
 // while AccessLevel defaults to Everyone
 // and ReplyPreference defaults to List.
-func (mg *MailgunImpl) CreateMailingList(ctx context.Context, prototype MailingList) (MailingList, error) {
+func (mg *MailgunImpl) CreateMailingList(ctx context.Context, prototype mtypes.MailingList) (mtypes.MailingList, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, listsEndpoint))
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
@@ -195,9 +147,9 @@ func (mg *MailgunImpl) CreateMailingList(ctx context.Context, prototype MailingL
 	}
 	response, err := makePostRequest(ctx, r, p)
 	if err != nil {
-		return MailingList{}, err
+		return mtypes.MailingList{}, err
 	}
-	var l MailingList
+	var l mtypes.MailingList
 	err = response.parseFromJSON(&l)
 	return l, err
 }
@@ -214,16 +166,16 @@ func (mg *MailgunImpl) DeleteMailingList(ctx context.Context, addr string) error
 
 // GetMailingList allows your application to recover the complete List structure
 // representing a mailing list, so long as you have its e-mail address.
-func (mg *MailgunImpl) GetMailingList(ctx context.Context, addr string) (MailingList, error) {
+func (mg *MailgunImpl) GetMailingList(ctx context.Context, addr string) (mtypes.MailingList, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, listsEndpoint) + "/" + addr)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 	response, err := makeGetRequest(ctx, r)
 	if err != nil {
-		return MailingList{}, err
+		return mtypes.MailingList{}, err
 	}
 
-	var resp mailingListResponse
+	var resp mtypes.GetMailingListResponse
 	err = response.parseFromJSON(&resp)
 	return resp.MailingList, err
 }
@@ -235,7 +187,7 @@ func (mg *MailgunImpl) GetMailingList(ctx context.Context, addr string) (Mailing
 // Be careful!  If changing the address of a mailing list,
 // e-mail sent to the old address will not succeed.
 // Make sure you account for the change accordingly.
-func (mg *MailgunImpl) UpdateMailingList(ctx context.Context, addr string, prototype MailingList) (MailingList, error) {
+func (mg *MailgunImpl) UpdateMailingList(ctx context.Context, addr string, prototype mtypes.MailingList) (mtypes.MailingList, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, listsEndpoint) + "/" + addr)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
@@ -255,7 +207,7 @@ func (mg *MailgunImpl) UpdateMailingList(ctx context.Context, addr string, proto
 	if prototype.ReplyPreference != "" {
 		p.addValue("reply_preference", string(prototype.ReplyPreference))
 	}
-	var l MailingList
+	var l mtypes.MailingList
 	response, err := makePutRequest(ctx, r, p)
 	if err != nil {
 		return l, err
diff --git a/members.go b/members.go
index 2b48354..747fb41 100644
--- a/members.go
+++ b/members.go
@@ -4,48 +4,12 @@ import (
 	"context"
 	"encoding/json"
 	"strconv"
-)
-
-// yes and no are variables which provide us the ability to take their addresses.
-// Subscribed and Unsubscribed are pointers to these booleans.
-//
-// We use a pointer to boolean as a kind of trinary data type:
-// if nil, the relevant data type remains unspecified.
-// Otherwise, its value is either true or false.
-var (
-	yes = true
-	no  = false
-)
 
-// Mailing list members have an attribute that determines if they've subscribed to the mailing list or not.
-// This attribute may be used to filter the results returned by GetSubscribers().
-// All, Subscribed, and Unsubscribed provides a convenient and readable syntax for specifying the scope of the search.
-var (
-	All          *bool
-	Subscribed   = &yes
-	Unsubscribed = &no
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
-// A Member structure represents a member of the mailing list.
-// The Vars field can represent any JSON-encodable data.
-type Member struct {
-	Address    string         `json:"address,omitempty"`
-	Name       string         `json:"name,omitempty"`
-	Subscribed *bool          `json:"subscribed,omitempty"`
-	Vars       map[string]any `json:"vars,omitempty"`
-}
-
-type memberListResponse struct {
-	Lists  []Member `json:"items"`
-	Paging Paging   `json:"paging"`
-}
-
-type memberResponse struct {
-	Member Member `json:"member"`
-}
-
 type MemberListIterator struct {
-	memberListResponse
+	mtypes.MemberListResponse
 	mg  Mailgun
 	err error
 }
@@ -62,7 +26,7 @@ func (mg *MailgunImpl) ListMembers(address string, opts *ListOptions) *MemberLis
 	url, err := r.generateUrlWithParameters()
 	return &MemberListIterator{
 		mg:                 mg,
-		memberListResponse: memberListResponse{Paging: Paging{Next: url, First: url}},
+		MemberListResponse: mtypes.MemberListResponse{Paging: mtypes.Paging{Next: url, First: url}},
 		err:                err,
 	}
 }
@@ -75,7 +39,7 @@ func (li *MemberListIterator) Err() error {
 // Next retrieves the next page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error
-func (li *MemberListIterator) Next(ctx context.Context, items *[]Member) bool {
+func (li *MemberListIterator) Next(ctx context.Context, items *[]mtypes.Member) bool {
 	if li.err != nil {
 		return false
 	}
@@ -91,7 +55,7 @@ func (li *MemberListIterator) Next(ctx context.Context, items *[]Member) bool {
 // First retrieves the first page of items from the api. Returns false if there
 // was an error. It also sets the iterator object to the first page.
 // Use `.Err()` to retrieve the error.
-func (li *MemberListIterator) First(ctx context.Context, items *[]Member) bool {
+func (li *MemberListIterator) First(ctx context.Context, items *[]mtypes.Member) bool {
 	if li.err != nil {
 		return false
 	}
@@ -107,7 +71,7 @@ func (li *MemberListIterator) First(ctx context.Context, items *[]Member) bool {
 // Calling Last() is invalid unless you first call First() or Next()
 // Returns false if there was an error. It also sets the iterator object
 // to the last page. Use `.Err()` to retrieve the error.
-func (li *MemberListIterator) Last(ctx context.Context, items *[]Member) bool {
+func (li *MemberListIterator) Last(ctx context.Context, items *[]mtypes.Member) bool {
 	if li.err != nil {
 		return false
 	}
@@ -122,7 +86,7 @@ func (li *MemberListIterator) Last(ctx context.Context, items *[]Member) bool {
 // Previous retrieves the previous page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error if any
-func (li *MemberListIterator) Previous(ctx context.Context, items *[]Member) bool {
+func (li *MemberListIterator) Previous(ctx context.Context, items *[]mtypes.Member) bool {
 	if li.err != nil {
 		return false
 	}
@@ -144,20 +108,20 @@ func (li *MemberListIterator) fetch(ctx context.Context, url string) error {
 	r.setClient(li.mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, li.mg.APIKey())
 
-	return getResponseFromJSON(ctx, r, &li.memberListResponse)
+	return getResponseFromJSON(ctx, r, &li.MemberListResponse)
 }
 
 // GetMember returns a complete Member structure for a member of a mailing list,
 // given only their subscription e-mail address.
-func (mg *MailgunImpl) GetMember(ctx context.Context, s, l string) (Member, error) {
+func (mg *MailgunImpl) GetMember(ctx context.Context, s, l string) (mtypes.Member, error) {
 	r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, l) + "/" + s)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 	response, err := makeGetRequest(ctx, r)
 	if err != nil {
-		return Member{}, err
+		return mtypes.Member{}, err
 	}
-	var resp memberResponse
+	var resp mtypes.MemberResponse
 	err = response.parseFromJSON(&resp)
 	return resp.Member, err
 }
@@ -165,7 +129,7 @@ func (mg *MailgunImpl) GetMember(ctx context.Context, s, l string) (Member, erro
 // CreateMember registers a new member of the indicated mailing list.
 // If merge is set to true, then the registration may update an existing Member's settings.
 // Otherwise, an error will occur if you attempt to add a member with a duplicate e-mail address.
-func (mg *MailgunImpl) CreateMember(ctx context.Context, merge bool, addr string, prototype Member) error {
+func (mg *MailgunImpl) CreateMember(ctx context.Context, merge bool, addr string, prototype mtypes.Member) error {
 	vs, err := json.Marshal(prototype.Vars)
 	if err != nil {
 		return err
@@ -188,7 +152,7 @@ func (mg *MailgunImpl) CreateMember(ctx context.Context, merge bool, addr string
 
 // UpdateMember lets you change certain details about the indicated mailing list member.
 // Address, Name, Vars, and Subscribed fields may be changed.
-func (mg *MailgunImpl) UpdateMember(ctx context.Context, s, l string, prototype Member) (Member, error) {
+func (mg *MailgunImpl) UpdateMember(ctx context.Context, s, l string, prototype mtypes.Member) (mtypes.Member, error) {
 	r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, l) + "/" + s)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
@@ -202,7 +166,7 @@ func (mg *MailgunImpl) UpdateMember(ctx context.Context, s, l string, prototype
 	if prototype.Vars != nil {
 		vs, err := json.Marshal(prototype.Vars)
 		if err != nil {
-			return Member{}, err
+			return mtypes.Member{}, err
 		}
 		p.addValue("vars", string(vs))
 	}
@@ -211,10 +175,10 @@ func (mg *MailgunImpl) UpdateMember(ctx context.Context, s, l string, prototype
 	}
 	response, err := makePutRequest(ctx, r, p)
 	if err != nil {
-		return Member{}, err
+		return mtypes.Member{}, err
 	}
 	var envelope struct {
-		Member Member `json:"member"`
+		Member mtypes.Member `json:"member"`
 	}
 	err = response.parseFromJSON(&envelope)
 	return envelope.Member, err
diff --git a/messages.go b/messages.go
index a6ace90..8efbb0e 100644
--- a/messages.go
+++ b/messages.go
@@ -114,9 +114,10 @@ type MimeMessage struct {
 }
 
 // TODO(v5): return from Send()
+// TODO(v5): move to mtypes?
 type sendMessageResponse struct {
 	Message string `json:"message"`
-	Id      string `json:"id"`
+	ID      string `json:"id"`
 }
 
 // TrackingOptions contains fields relevant to tracking.
@@ -696,7 +697,7 @@ func (mg *MailgunImpl) Send(ctx context.Context, m SendableMessage) (mes, id str
 	err = postResponseFromJSON(ctx, r, payload, &response)
 	if err == nil {
 		mes = response.Message
-		id = response.Id
+		id = response.ID
 	}
 
 	return mes, id, err
diff --git a/messages_types_v5.go b/messages_types_v5.go
deleted file mode 100644
index 9213908..0000000
--- a/messages_types_v5.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package mailgun
-
-// This file contains a draft for new v5 messages.
-// TODO(v5): remove this file
-
-import (
-	"io"
-	"time"
-)
-
-// CommonMessage structures contain both the message text and the envelope for an e-mail message.
-// TODO(v5): rename to CommonMessage
-type commonMessageV5 struct {
-	domain             string
-	to                 []string
-	tags               []string
-	dkim               *bool
-	deliveryTime       time.Time
-	stoPeriod          string
-	attachments        []string
-	readerAttachments  []ReaderAttachment
-	inlines            []string
-	readerInlines      []ReaderAttachment
-	bufferAttachments  []BufferAttachment
-	nativeSend         bool
-	testMode           bool
-	tracking           *bool
-	trackingClicks     *string
-	trackingOpens      *bool
-	headers            map[string]string
-	variables          map[string]string
-	templateVariables  map[string]any
-	recipientVariables map[string]map[string]any
-	templateVersionTag string
-	templateRenderText bool
-	requireTLS         bool
-	skipVerification   bool
-}
-
-// PlainMessage contains fields relevant to plain API-synthesized messages.
-// You're expected to use various setters to set most of these attributes,
-// although from, subject, and text are set when the message is created with
-// NewMessage.
-// TODO(v5): rename to PlainMessage
-type plainMessageV5 struct {
-	commonMessageV5
-
-	from     string
-	cc       []string
-	bcc      []string
-	subject  string
-	text     string
-	html     string
-	ampHtml  string
-	template string
-}
-
-// MimeMessage contains fields relevant to pre-packaged MIME messages.
-// TODO(v5): rename to MimeMessage
-type mimeMessageV5 struct {
-	commonMessageV5
-
-	body io.ReadCloser
-}
diff --git a/messages_v5.go b/messages_v5.go
deleted file mode 100644
index 4a7c313..0000000
--- a/messages_v5.go
+++ /dev/null
@@ -1,488 +0,0 @@
-package mailgun
-
-// This file contains methods for new v5 messages.
-// TODO(v5): remove this file
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"io"
-	"regexp"
-	"strconv"
-	"strings"
-	"time"
-)
-
-// NewMessage returns a new e-mail message with the simplest envelop needed to send.
-//
-// Supports arbitrary-sized recipient lists by
-// automatically sending mail in batches of up to MaxNumberOfRecipients.
-//
-// To support batch sending, do not provide `to` at this point.
-// You can do this explicitly, or implicitly, as follows:
-//
-//	// Note absence of `to` parameter(s)!
-//	m := NewMessage("me@example.com", "Help save our planet", "Hello world!")
-//
-// Note that you'll need to invoke the AddRecipientAndVariables or AddRecipient method
-// before sending, though.
-// TODO(v5): rename to NewMessage
-func newMessageV5(domain, from, subject, text string, to ...string) *plainMessageV5 {
-	return &plainMessageV5{
-		commonMessageV5: commonMessageV5{
-			domain: domain,
-			to:     to,
-		},
-
-		from:    from,
-		subject: subject,
-		text:    text,
-	}
-}
-
-// NewMIMEMessage creates a new MIME message. These messages are largely canned;
-// you do not need to invoke setters to set message-related headers.
-// However, you do still need to call setters for Mailgun-specific settings.
-//
-// Supports arbitrary-sized recipient lists by
-// automatically sending mail in batches of up to MaxNumberOfRecipients.
-//
-// To support batch sending, do not provide `to` at this point.
-// You can do this explicitly, or implicitly, as follows:
-//
-//	// Note absence of `to` parameter(s)!
-//	m := NewMIMEMessage(body)
-//
-// Note that you'll need to invoke the AddRecipientAndVariables or AddRecipient method
-// before sending, though.
-// TODO(v5): rename to NewMIMEMessage
-func newMIMEMessage(domain string, body io.ReadCloser, to ...string) *mimeMessageV5 {
-	return &mimeMessageV5{
-		commonMessageV5: commonMessageV5{
-			domain: domain,
-			to:     to,
-		},
-		body: body,
-	}
-}
-
-// AddReaderAttachment arranges to send a file along with the e-mail message.
-// File contents are read from a io.ReadCloser.
-// The filename parameter is the resulting filename of the attachment.
-// The readCloser parameter is the io.ReadCloser which reads the actual bytes to be used
-// as the contents of the attached file.
-func (m *commonMessageV5) AddReaderAttachment(filename string, readCloser io.ReadCloser) {
-	ra := ReaderAttachment{Filename: filename, ReadCloser: readCloser}
-	m.readerAttachments = append(m.readerAttachments, ra)
-}
-
-// AddBufferAttachment arranges to send a file along with the e-mail message.
-// File contents are read from the []byte array provided
-// The filename parameter is the resulting filename of the attachment.
-// The buffer parameter is the []byte array which contains the actual bytes to be used
-// as the contents of the attached file.
-func (m *commonMessageV5) AddBufferAttachment(filename string, buffer []byte) {
-	ba := BufferAttachment{Filename: filename, Buffer: buffer}
-	m.bufferAttachments = append(m.bufferAttachments, ba)
-}
-
-// AddAttachment arranges to send a file from the filesystem along with the e-mail message.
-// The attachment parameter is a filename, which must refer to a file which actually resides
-// in the local filesystem.
-func (m *commonMessageV5) AddAttachment(attachment string) {
-	m.attachments = append(m.attachments, attachment)
-}
-
-// AddReaderInline arranges to send a file along with the e-mail message.
-// File contents are read from a io.ReadCloser.
-// The filename parameter is the resulting filename of the attachment.
-// The readCloser parameter is the io.ReadCloser which reads the actual bytes to be used
-// as the contents of the attached file.
-func (m *commonMessageV5) AddReaderInline(filename string, readCloser io.ReadCloser) {
-	ra := ReaderAttachment{Filename: filename, ReadCloser: readCloser}
-	m.readerInlines = append(m.readerInlines, ra)
-}
-
-// AddInline arranges to send a file along with the e-mail message, but does so
-// in a way that its data remains "inline" with the rest of the message.  This
-// can be used to send image or font data along with an HTML-encoded message body.
-// The attachment parameter is a filename, which must refer to a file which actually resides
-// in the local filesystem.
-func (m *commonMessageV5) AddInline(inline string) {
-	m.inlines = append(m.inlines, inline)
-}
-
-// SetDKIM arranges to send the o:dkim header with the message, and sets its value accordingly.
-// Refer to the Mailgun documentation for more information.
-func (m *commonMessageV5) SetDKIM(dkim bool) {
-	m.dkim = &dkim
-}
-
-// EnableNativeSend allows the return path to match the address in the CommonMessage.Headers.From:
-// field when sending from Mailgun rather than the usual bounce+ address in the return path.
-func (m *commonMessageV5) EnableNativeSend() {
-	m.nativeSend = true
-}
-
-// EnableTestMode allows submittal of a message, such that it will be discarded by Mailgun.
-// This facilitates testing client-side software without actually consuming e-mail resources.
-func (m *commonMessageV5) EnableTestMode() {
-	m.testMode = true
-}
-
-// SetDeliveryTime schedules the message for transmission at the indicated time.
-// Pass nil to remove any installed schedule.
-// Refer to the Mailgun documentation for more information.
-func (m *commonMessageV5) SetDeliveryTime(dt time.Time) {
-	m.deliveryTime = dt
-}
-
-// SetSTOPeriod toggles Send Time Optimization (STO) on a per-message basis.
-// String should be set to the number of hours in [0-9]+h format,
-// with the minimum being 24h and the maximum being 72h.
-// Refer to the Mailgun documentation for more information.
-func (m *commonMessageV5) SetSTOPeriod(stoPeriod string) error {
-	validPattern := `^([2-6][4-9]|[3-6][0-9]|7[0-2])h$`
-	// TODO(vtopc): regexp.Compile, which is called by regexp.MatchString, is a heave operation, move into global variable
-	// or just parse using time.ParseDuration().
-	match, err := regexp.MatchString(validPattern, stoPeriod)
-	if err != nil {
-		return err
-	}
-
-	if !match {
-		return errors.New("STO period is invalid. Valid range is 24h to 72h")
-	}
-
-	m.stoPeriod = stoPeriod
-	return nil
-}
-
-// SetTracking sets the o:tracking message parameter to adjust, on a message-by-message basis,
-// whether or not Mailgun will rewrite URLs to facilitate event tracking.
-// Events tracked includes opens, clicks, unsubscribes, etc.
-// Note: simply calling this method ensures that the o:tracking header is passed in with the message.
-// Its yes/no setting is determined by the call's parameter.
-// Note that this header is not passed on to the final recipient(s).
-// Refer to the Mailgun documentation for more information.
-func (m *commonMessageV5) SetTracking(tracking bool) {
-	m.tracking = &tracking
-}
-
-// SetTrackingClicks information is found in the Mailgun documentation.
-func (m *commonMessageV5) SetTrackingClicks(trackingClicks bool) {
-	m.trackingClicks = ptr(yesNo(trackingClicks))
-}
-
-// SetTrackingOptions sets the o:tracking, o:tracking-clicks and o:tracking-opens at once.
-func (m *commonMessageV5) SetTrackingOptions(options *TrackingOptions) {
-	m.tracking = &options.Tracking
-
-	m.trackingClicks = &options.TrackingClicks
-
-	m.trackingOpens = &options.TrackingOpens
-}
-
-// SetRequireTLS information is found in the Mailgun documentation.
-func (m *commonMessageV5) SetRequireTLS(b bool) {
-	m.requireTLS = b
-}
-
-// SetSkipVerification information is found in the Mailgun documentation.
-func (m *commonMessageV5) SetSkipVerification(b bool) {
-	m.skipVerification = b
-}
-
-// SetTrackingOpens information is found in the Mailgun documentation.
-func (m *commonMessageV5) SetTrackingOpens(trackingOpens bool) {
-	m.trackingOpens = &trackingOpens
-}
-
-// SetTemplateVersion information is found in the Mailgun documentation.
-func (m *commonMessageV5) SetTemplateVersion(tag string) {
-	m.templateVersionTag = tag
-}
-
-// SetTemplateRenderText information is found in the Mailgun documentation.
-func (m *commonMessageV5) SetTemplateRenderText(render bool) {
-	m.templateRenderText = render
-}
-
-// AddHeader allows you to send custom MIME headers with the message.
-func (m *commonMessageV5) AddHeader(header, value string) {
-	if m.headers == nil {
-		m.headers = make(map[string]string)
-	}
-	m.headers[header] = value
-}
-
-// AddVariable lets you associate a set of variables with messages you send,
-// which Mailgun can use to, in essence, complete form-mail.
-// Refer to the Mailgun documentation for more information.
-func (m *commonMessageV5) AddVariable(variable string, value any) error {
-	if m.variables == nil {
-		m.variables = make(map[string]string)
-	}
-
-	j, err := json.Marshal(value)
-	if err != nil {
-		return err
-	}
-
-	encoded := string(j)
-	v, err := strconv.Unquote(encoded)
-	if err != nil {
-		v = encoded
-	}
-
-	m.variables[variable] = v
-	return nil
-}
-
-// AddTemplateVariable adds a template variable to the map of template variables, replacing the variable if it is already there.
-// This is used for server-side message templates and can nest arbitrary values. At send time, the resulting map will be converted into
-// a JSON string and sent as a header in the X-Mailgun-Variables header.
-func (m *commonMessageV5) AddTemplateVariable(variable string, value any) error {
-	if m.templateVariables == nil {
-		m.templateVariables = make(map[string]any)
-	}
-	m.templateVariables[variable] = value
-	return nil
-}
-
-// AddDomain allows you to use a separate domain for the type of messages you are sending.
-func (m *commonMessageV5) AddDomain(domain string) {
-	m.domain = domain
-}
-
-// GetHeaders retrieves the http headers associated with this message
-func (m *commonMessageV5) GetHeaders() map[string]string {
-	return m.headers
-}
-
-// specific message methods
-
-func (m *plainMessageV5) AddRecipient(recipient string) error {
-	return m.AddRecipientAndVariables(recipient, nil)
-}
-
-// AddRecipientAndVariables appends a receiver to the To: header of a message,
-// and as well attaches a set of variables relevant for this recipient.
-// It will return an error if the limit of recipients have been exceeded for this message
-func (m *plainMessageV5) AddRecipientAndVariables(r string, vars map[string]any) error {
-	if m.RecipientCount() >= MaxNumberOfRecipients {
-		return fmt.Errorf("recipient limit exceeded (max %d)", MaxNumberOfRecipients)
-	}
-	m.to = append(m.to, r)
-	if vars != nil {
-		if m.recipientVariables == nil {
-			m.recipientVariables = make(map[string]map[string]any)
-		}
-		m.recipientVariables[r] = vars
-	}
-
-	return nil
-}
-
-func (m *mimeMessageV5) AddRecipient(recipient string) error {
-	if m.RecipientCount() >= MaxNumberOfRecipients {
-		return fmt.Errorf("recipient limit exceeded (max %d)", MaxNumberOfRecipients)
-	}
-	m.to = append(m.to, recipient)
-
-	return nil
-}
-
-func (m *plainMessageV5) RecipientCount() int {
-	return len(m.to) + len(m.bcc) + len(m.cc)
-}
-
-func (m *mimeMessageV5) RecipientCount() int {
-	return len(m.to)
-}
-
-// SetReplyTo sets the receiver who should receive replies
-func (m *commonMessageV5) SetReplyTo(recipient string) {
-	m.AddHeader("Reply-To", recipient)
-}
-
-func (m *plainMessageV5) AddCC(r string) {
-	m.cc = append(m.cc, r)
-}
-
-func (*mimeMessageV5) AddCC(_ string) {}
-
-func (m *plainMessageV5) AddBCC(r string) {
-	m.bcc = append(m.bcc, r)
-}
-
-func (*mimeMessageV5) AddBCC(_ string) {}
-
-func (m *plainMessageV5) SetHTML(h string) {
-	m.html = h
-}
-
-func (*mimeMessageV5) SetHTML(_ string) {}
-
-func (m *plainMessageV5) SetAmpHTML(h string) {
-	m.ampHtml = h
-}
-
-func (*mimeMessageV5) SetAmpHTML(_ string) {}
-
-// AddTag attaches tags to the message.  Tags are useful for metrics gathering and event tracking purposes.
-// Refer to the Mailgun documentation for further details.
-func (m *commonMessageV5) AddTag(tag ...string) error {
-	if len(m.tags) >= MaxNumberOfTags {
-		return fmt.Errorf("cannot add any new tags. Message tag limit (%d) reached", MaxNumberOfTags)
-	}
-
-	m.tags = append(m.tags, tag...)
-	return nil
-}
-
-func (m *plainMessageV5) SetTemplate(t string) {
-	m.template = t
-}
-
-func (*mimeMessageV5) SetTemplate(_ string) {}
-
-func (m *plainMessageV5) AddValues(p *FormDataPayload) {
-	p.addValue("from", m.from)
-	p.addValue("subject", m.subject)
-	p.addValue("text", m.text)
-	for _, cc := range m.cc {
-		p.addValue("cc", cc)
-	}
-	for _, bcc := range m.bcc {
-		p.addValue("bcc", bcc)
-	}
-	if m.html != "" {
-		p.addValue("html", m.html)
-	}
-	if m.template != "" {
-		p.addValue("template", m.template)
-	}
-	if m.ampHtml != "" {
-		p.addValue("amp-html", m.ampHtml)
-	}
-}
-
-func (m *mimeMessageV5) AddValues(p *FormDataPayload) {
-	p.addReadCloser("message", "message.mime", m.body)
-}
-
-func (*plainMessageV5) Endpoint() string {
-	return messagesEndpoint
-}
-
-func (*mimeMessageV5) Endpoint() string {
-	return mimeMessagesEndpoint
-}
-
-// Send attempts to queue a message (see CommonMessage, NewMessage, and its methods) for delivery.
-// It returns the Mailgun server response, which consists of two components:
-//   - A human-readable status message, typically "Queued. Thank you."
-//   - A Message ID, which is the id used to track the queued message. The message id is useful
-//     when contacting support to report an issue with a specific message or to relate a
-//     delivered, accepted or failed event back to specific message.
-//
-// The status and message ID are only returned if no error occurred.
-//
-// Error returns can be of type `error.Error` which wrap internal and standard
-// golang errors like `url.Error`. The error can also be of type
-// mailgun.UnexpectedResponseError which contains the error returned by the mailgun API.
-//
-//	mailgun.UnexpectedResponseError {
-//	  URL:      "https://api.mailgun.com/v3/messages",
-//	  Expected: 200,
-//	  Actual:   400,
-//	  Data:     "Domain not found: example.com",
-//	}
-//
-//	See the public mailgun documentation for all possible return codes and error messages
-func (mg *MailgunImpl) sendV5(ctx context.Context, m SendableMessage) (mes, id string, err error) {
-	// TODO(vtopc): move domain checks into NewMessage and NewMIMEMessage?
-	if m.Domain() == "" {
-		err = errors.New("you must provide a valid domain before calling Send()")
-		return "", "", err
-	}
-
-	invalidChars := ":&'@(),!?#;%+=<>"
-	if i := strings.ContainsAny(m.Domain(), invalidChars); i {
-		err = fmt.Errorf("you called Send() with a domain that contains invalid characters")
-		return "", "", err
-	}
-
-	if mg.apiKey == "" {
-		err = errors.New("you must provide a valid api-key before calling Send()")
-		return "", "", err
-	}
-
-	if !isValid(m) {
-		err = ErrInvalidMessage
-		return "", "", err
-	}
-
-	if m.STOPeriod() != "" && m.RecipientCount() > 1 {
-		err = errors.New("STO can only be used on a per-message basis")
-		return "", "", err
-	}
-	payload := NewFormDataPayload()
-
-	m.AddValues(payload)
-
-	// TODO: make (CommonMessage).AddValues():
-	err = addMessageValues(payload, m)
-	if err != nil {
-		return "", "", err
-	}
-
-	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, m.Endpoint(), m.Domain()))
-	r.setClient(mg.HTTPClient())
-	r.setBasicAuth(basicAuthUser, mg.APIKey())
-	// Override any HTTP headers if provided
-	for k, v := range mg.overrideHeaders {
-		r.addHeader(k, v)
-	}
-
-	var response sendMessageResponse
-	err = postResponseFromJSON(ctx, r, payload, &response)
-	if err == nil {
-		mes = response.Message
-		id = response.Id
-	}
-
-	return mes, id, err
-}
-
-func (m *plainMessageV5) isValid() bool {
-	if !validateStringList(m.cc, false) {
-		return false
-	}
-
-	if !validateStringList(m.bcc, false) {
-		return false
-	}
-
-	if m.from == "" {
-		return false
-	}
-
-	if m.template != "" {
-		// m.text or m.html not needed if template is supplied
-		return true
-	}
-
-	if m.text == "" && m.html == "" {
-		return false
-	}
-
-	return true
-}
-
-func (m *mimeMessageV5) isValid() bool {
-	return m.body != nil
-}
diff --git a/mock.go b/mock.go
index e992085..4b5f413 100644
--- a/mock.go
+++ b/mock.go
@@ -13,6 +13,7 @@ import (
 	"sync"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 type MockServer interface {
@@ -20,13 +21,13 @@ type MockServer interface {
 	URL() string
 	DomainIPS() []string
 	DomainList() []DomainContainer
-	ExportList() []Export
+	ExportList() []mtypes.Export
 	MailingList() []MailingListContainer
-	RouteList() []Route
+	RouteList() []mtypes.Route
 	Events() []Event
-	Webhooks() WebHooksListResponse
-	Templates() []Template
-	SubaccountList() []Subaccount
+	Webhooks() mtypes.WebHooksListResponse
+	Templates() []mtypes.Template
+	SubaccountList() []mtypes.Subaccount
 }
 
 // A mailgun api mock suitable for testing
@@ -35,19 +36,19 @@ type mockServer struct {
 
 	domainIPS        []string
 	domainList       []DomainContainer
-	exportList       []Export
+	exportList       []mtypes.Export
 	mailingList      []MailingListContainer
-	routeList        []Route
+	routeList        []mtypes.Route
 	events           []Event
-	templates        []Template
-	templateVersions map[string][]TemplateVersion
-	unsubscribes     []Unsubscribe
-	complaints       []Complaint
-	bounces          []Bounce
-	credentials      []Credential
-	tags             []Tag
-	subaccountList   []Subaccount
-	webhooks         WebHooksListResponse
+	templates        []mtypes.Template
+	templateVersions map[string][]mtypes.TemplateVersion
+	unsubscribes     []mtypes.Unsubscribe
+	complaints       []mtypes.Complaint
+	bounces          []mtypes.Bounce
+	credentials      []mtypes.Credential
+	tags             []mtypes.Tag
+	subaccountList   []mtypes.Subaccount
+	webhooks         mtypes.WebHooksListResponse
 	mutex            sync.Mutex
 }
 
@@ -63,7 +64,7 @@ func (ms *mockServer) DomainList() []DomainContainer {
 	return ms.domainList
 }
 
-func (ms *mockServer) ExportList() []Export {
+func (ms *mockServer) ExportList() []mtypes.Export {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 	return ms.exportList
@@ -75,7 +76,7 @@ func (ms *mockServer) MailingList() []MailingListContainer {
 	return ms.mailingList
 }
 
-func (ms *mockServer) RouteList() []Route {
+func (ms *mockServer) RouteList() []mtypes.Route {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 	return ms.routeList
@@ -87,25 +88,25 @@ func (ms *mockServer) Events() []Event {
 	return ms.events
 }
 
-func (ms *mockServer) Webhooks() WebHooksListResponse {
+func (ms *mockServer) Webhooks() mtypes.WebHooksListResponse {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 	return ms.webhooks
 }
 
-func (ms *mockServer) Templates() []Template {
+func (ms *mockServer) Templates() []mtypes.Template {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 	return ms.templates
 }
 
-func (ms *mockServer) Unsubscribes() []Unsubscribe {
+func (ms *mockServer) Unsubscribes() []mtypes.Unsubscribe {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 	return ms.unsubscribes
 }
 
-func (ms *mockServer) SubaccountList() []Subaccount {
+func (ms *mockServer) SubaccountList() []mtypes.Subaccount {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 	return ms.subaccountList
@@ -286,3 +287,8 @@ func randomString(n int, prefix string) string {
 func randomEmail(prefix, domain string) string {
 	return strings.ToLower(fmt.Sprintf("%s@%s", randomString(20, prefix), domain))
 }
+
+type okResp struct {
+	ID      string `json:"id,omitempty"`
+	Message string `json:"message"`
+}
diff --git a/mock_analytics.go b/mock_analytics.go
index 868bc5b..bf51a0a 100644
--- a/mock_analytics.go
+++ b/mock_analytics.go
@@ -4,6 +4,7 @@ import (
 	"net/http"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addAnalyticsRoutes(r chi.Router) {
@@ -11,23 +12,23 @@ func (ms *mockServer) addAnalyticsRoutes(r chi.Router) {
 }
 
 func (ms *mockServer) listMetrics(w http.ResponseWriter, _ *http.Request) {
-	start, _ := NewRFC2822Time("Tue, 24 Sep 2024 00:00:00 +0000")
-	end, _ := NewRFC2822Time("Tue, 24 Oct 2024 00:00:00 +0000")
+	start, _ := mtypes.NewRFC2822Time("Tue, 24 Sep 2024 00:00:00 +0000")
+	end, _ := mtypes.NewRFC2822Time("Tue, 24 Oct 2024 00:00:00 +0000")
 
-	resp := MetricsResponse{
+	resp := mtypes.MetricsResponse{
 		Start:      start,
 		End:        end,
 		Resolution: "day",
 		Duration:   "30d",
 		Dimensions: []string{"time"},
-		Items: []MetricsItem{
+		Items: []mtypes.MetricsItem{
 			{
-				Dimensions: []MetricsDimension{{
+				Dimensions: []mtypes.MetricsDimension{{
 					Dimension:    "time",
 					Value:        "Tue, 24 Sep 2024 00:00:00 +0000",
 					DisplayValue: "Tue, 24 Sep 2024 00:00:00 +0000",
 				}},
-				Metrics: Metrics{
+				Metrics: mtypes.Metrics{
 					SentCount:      ptr(uint64(4)),
 					DeliveredCount: ptr(uint64(3)),
 					OpenedCount:    ptr(uint64(2)),
@@ -35,7 +36,7 @@ func (ms *mockServer) listMetrics(w http.ResponseWriter, _ *http.Request) {
 				},
 			},
 		},
-		Pagination: MetricsPagination{
+		Pagination: mtypes.MetricsPagination{
 			Sort:  "",
 			Skip:  0,
 			Limit: 10,
diff --git a/mock_bounces.go b/mock_bounces.go
index ecea4cb..54d1c4d 100644
--- a/mock_bounces.go
+++ b/mock_bounces.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addBouncesRoutes(r chi.Router) {
@@ -18,15 +19,15 @@ func (ms *mockServer) addBouncesRoutes(r chi.Router) {
 	r.Delete("/{domain}/bounces", ms.deleteBouncesList)
 	r.Post("/{domain}/bounces", ms.createBounce)
 
-	ms.bounces = append(ms.bounces, Bounce{
-		CreatedAt: RFC2822Time(time.Now()),
+	ms.bounces = append(ms.bounces, mtypes.Bounce{
+		CreatedAt: mtypes.RFC2822Time(time.Now()),
 		Error:     "invalid address",
 		Code:      "INVALID",
 		Address:   "foo@mailgun.test",
 	})
 
-	ms.bounces = append(ms.bounces, Bounce{
-		CreatedAt: RFC2822Time(time.Now()),
+	ms.bounces = append(ms.bounces, mtypes.Bounce{
+		CreatedAt: mtypes.RFC2822Time(time.Now()),
 		Error:     "non existing address",
 		Code:      "NOT_EXIST",
 		Address:   "alice@example.com",
@@ -59,20 +60,20 @@ func (ms *mockServer) listBounces(w http.ResponseWriter, r *http.Request) {
 	}
 	start, end := pageOffsets(idx, page, pivot, limit)
 	var nextAddress, prevAddress string
-	var results []Bounce
+	var results []mtypes.Bounce
 
 	if start != end {
 		results = ms.bounces[start:end]
 		nextAddress = results[len(results)-1].Address
 		prevAddress = results[0].Address
 	} else {
-		results = []Bounce{}
+		results = []mtypes.Bounce{}
 		nextAddress = pivot
 		prevAddress = pivot
 	}
 
 	toJSON(w, bouncesListResponse{
-		Paging: Paging{
+		Paging: mtypes.Paging{
 			First: getPageURL(r, url.Values{
 				"page": []string{"first"},
 			}),
@@ -110,7 +111,7 @@ func (ms *mockServer) createBounce(w http.ResponseWriter, r *http.Request) {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 
-	var bounces []Bounce
+	var bounces []mtypes.Bounce
 	if r.Header.Get("Content-Type") == "application/json" {
 		body, err := ioutil.ReadAll(r.Body)
 		if err != nil {
@@ -142,7 +143,7 @@ func (ms *mockServer) createBounce(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
-		bounces = append(bounces, Bounce{Address: address, Code: code, Error: bounceError})
+		bounces = append(bounces, mtypes.Bounce{Address: address, Code: code, Error: bounceError})
 	}
 
 	for _, bounce := range bounces {
@@ -188,7 +189,7 @@ func (ms *mockServer) deleteBouncesList(w http.ResponseWriter, r *http.Request)
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 
-	ms.bounces = []Bounce{}
+	ms.bounces = []mtypes.Bounce{}
 
 	toJSON(w, map[string]any{
 		"message": "All bounces has been deleted",
diff --git a/mock_complaints.go b/mock_complaints.go
index fdc9f98..2920dcd 100644
--- a/mock_complaints.go
+++ b/mock_complaints.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addComplaintsRoutes(r chi.Router) {
@@ -17,13 +18,13 @@ func (ms *mockServer) addComplaintsRoutes(r chi.Router) {
 	r.Delete("/{domain}/complaints/{address}", ms.deleteComplaint)
 	r.Post("/{domain}/complaints", ms.createComplaint)
 
-	ms.complaints = append(ms.complaints, Complaint{
-		CreatedAt: RFC2822Time(time.Now()),
+	ms.complaints = append(ms.complaints, mtypes.Complaint{
+		CreatedAt: mtypes.RFC2822Time(time.Now()),
 		Address:   "foo@mailgun.test",
 	})
 
-	ms.complaints = append(ms.complaints, Complaint{
-		CreatedAt: RFC2822Time(time.Now()),
+	ms.complaints = append(ms.complaints, mtypes.Complaint{
+		CreatedAt: mtypes.RFC2822Time(time.Now()),
 		Address:   "alice@example.com",
 	})
 }
@@ -54,20 +55,20 @@ func (ms *mockServer) listComplaints(w http.ResponseWriter, r *http.Request) {
 	}
 	start, end := pageOffsets(idx, page, pivot, limit)
 	var nextAddress, prevAddress string
-	var results []Complaint
+	var results []mtypes.Complaint
 
 	if start != end {
 		results = ms.complaints[start:end]
 		nextAddress = results[len(results)-1].Address
 		prevAddress = results[0].Address
 	} else {
-		results = []Complaint{}
+		results = []mtypes.Complaint{}
 		nextAddress = pivot
 		prevAddress = pivot
 	}
 
-	toJSON(w, complaintsResponse{
-		Paging: Paging{
+	toJSON(w, mtypes.ComplaintsResponse{
+		Paging: mtypes.Paging{
 			First: getPageURL(r, url.Values{
 				"page": []string{"first"},
 			}),
@@ -105,7 +106,7 @@ func (ms *mockServer) createComplaint(w http.ResponseWriter, r *http.Request) {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 
-	var complaints []Complaint
+	var complaints []mtypes.Complaint
 	if r.Header.Get("Content-Type") == "application/json" {
 		body, err := ioutil.ReadAll(r.Body)
 		if err != nil {
@@ -134,7 +135,7 @@ func (ms *mockServer) createComplaint(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
-		complaints = append(complaints, Complaint{Address: address, CreatedAt: RFC2822Time(time.Now())})
+		complaints = append(complaints, mtypes.Complaint{Address: address, CreatedAt: mtypes.RFC2822Time(time.Now())})
 	}
 
 	for _, complaint := range complaints {
@@ -146,7 +147,7 @@ func (ms *mockServer) createComplaint(w http.ResponseWriter, r *http.Request) {
 		}
 
 		if !addressExist {
-			complaint.CreatedAt = RFC2822Time(time.Now())
+			complaint.CreatedAt = mtypes.RFC2822Time(time.Now())
 			ms.complaints = append(ms.complaints, complaint)
 		}
 	}
diff --git a/mock_credentials.go b/mock_credentials.go
index 0e9b6ff..4fdb459 100644
--- a/mock_credentials.go
+++ b/mock_credentials.go
@@ -5,6 +5,7 @@ import (
 	"time"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addCredentialsRoutes(r chi.Router) {
@@ -13,14 +14,14 @@ func (ms *mockServer) addCredentialsRoutes(r chi.Router) {
 	r.Delete("/domains/{domain}/credentials/{login}", ms.deleteCredential)
 	r.Post("/domains/{domain}/credentials", ms.createCredential)
 
-	ms.credentials = append(ms.credentials, Credential{
-		CreatedAt: RFC2822Time(time.Now()),
+	ms.credentials = append(ms.credentials, mtypes.Credential{
+		CreatedAt: mtypes.RFC2822Time(time.Now()),
 		Login:     "alice",
 		Password:  "alices_password",
 	})
 
-	ms.credentials = append(ms.credentials, Credential{
-		CreatedAt: RFC2822Time(time.Now()),
+	ms.credentials = append(ms.credentials, mtypes.Credential{
+		CreatedAt: mtypes.RFC2822Time(time.Now()),
 		Login:     "bob",
 		Password:  "bobs_password",
 	})
@@ -37,7 +38,7 @@ func (ms *mockServer) listCredentials(w http.ResponseWriter, r *http.Request) {
 
 	skip := stringToInt(r.FormValue("skip"))
 
-	var results []Credential
+	var results []mtypes.Credential
 
 	if skip > 0 {
 		if len(ms.credentials[skip:]) < limit {
@@ -92,7 +93,7 @@ func (ms *mockServer) createCredential(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	ms.credentials = append(ms.credentials, Credential{Login: login, Password: password, CreatedAt: RFC2822Time(time.Now())})
+	ms.credentials = append(ms.credentials, mtypes.Credential{Login: login, Password: password, CreatedAt: mtypes.RFC2822Time(time.Now())})
 
 	toJSON(w, map[string]any{
 		"message": "Credentials created",
diff --git a/mock_domains.go b/mock_domains.go
index 74599e0..7065c2d 100644
--- a/mock_domains.go
+++ b/mock_domains.go
@@ -5,47 +5,48 @@ import (
 	"time"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 type DomainContainer struct {
-	Domain              Domain            `json:"domain"`
-	ReceivingDNSRecords []DNSRecord       `json:"receiving_dns_records"`
-	SendingDNSRecords   []DNSRecord       `json:"sending_dns_records"`
-	Connection          *DomainConnection `json:"connection,omitempty"`
-	Tracking            *DomainTracking   `json:"tracking,omitempty"`
-	TagLimits           *TagLimits        `json:"limits,omitempty"`
+	Domain              mtypes.Domain            `json:"domain"`
+	ReceivingDNSRecords []mtypes.DNSRecord       `json:"receiving_dns_records"`
+	SendingDNSRecords   []mtypes.DNSRecord       `json:"sending_dns_records"`
+	Connection          *mtypes.DomainConnection `json:"connection,omitempty"`
+	Tracking            *mtypes.DomainTracking   `json:"tracking,omitempty"`
+	TagLimits           *mtypes.TagLimits        `json:"limits,omitempty"`
 }
 
 func (ms *mockServer) addDomainRoutes(r chi.Router) {
 	ms.domainList = append(ms.domainList, DomainContainer{
-		Domain: Domain{
-			CreatedAt:    RFC2822Time(time.Now().UTC()),
+		Domain: mtypes.Domain{
+			CreatedAt:    mtypes.RFC2822Time(time.Now().UTC()),
 			Name:         "mailgun.test",
 			SMTPLogin:    "postmaster@mailgun.test",
 			SMTPPassword: "4rtqo4p6rrx9",
 			Wildcard:     true,
-			SpamAction:   SpamActionDisabled,
+			SpamAction:   mtypes.SpamActionDisabled,
 			State:        "active",
 			WebScheme:    "http",
 		},
-		Connection: &DomainConnection{
+		Connection: &mtypes.DomainConnection{
 			RequireTLS:       true,
 			SkipVerification: true,
 		},
-		TagLimits: &TagLimits{
+		TagLimits: &mtypes.TagLimits{
 			Limit: 50000,
 			Count: 5000,
 		},
-		Tracking: &DomainTracking{
-			Click: TrackingStatus{Active: true},
-			Open:  TrackingStatus{Active: true},
-			Unsubscribe: TrackingStatus{
+		Tracking: &mtypes.DomainTracking{
+			Click: mtypes.TrackingStatus{Active: true},
+			Open:  mtypes.TrackingStatus{Active: true},
+			Unsubscribe: mtypes.TrackingStatus{
 				Active:     false,
 				HTMLFooter: "\n<br>\n<p><a href=\"%unsubscribe_url%\">unsubscribe</a></p>\n",
 				TextFooter: "\n\nTo unsubscribe click: <%unsubscribe_url%>\n\n",
 			},
 		},
-		ReceivingDNSRecords: []DNSRecord{
+		ReceivingDNSRecords: []mtypes.DNSRecord{
 			{
 				Priority:   "10",
 				RecordType: "MX",
@@ -59,7 +60,7 @@ func (ms *mockServer) addDomainRoutes(r chi.Router) {
 				Value:      "mxb.mailgun.org",
 			},
 		},
-		SendingDNSRecords: []DNSRecord{
+		SendingDNSRecords: []mtypes.DNSRecord{
 			{
 				RecordType: "TXT",
 				Valid:      "valid",
@@ -104,7 +105,7 @@ func (ms *mockServer) listDomains(w http.ResponseWriter, r *http.Request) {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 
-	var list []Domain
+	var list []mtypes.Domain
 	for _, domain := range ms.domainList {
 		list = append(list, domain.Domain)
 	}
@@ -126,14 +127,14 @@ func (ms *mockServer) listDomains(w http.ResponseWriter, r *http.Request) {
 
 	// If we are at the end of the list
 	if skip == end {
-		toJSON(w, domainsListResponse{
+		toJSON(w, mtypes.ListDomainsResponse{
 			TotalCount: len(list),
-			Items:      []Domain{},
+			Items:      []mtypes.Domain{},
 		})
 		return
 	}
 
-	toJSON(w, domainsListResponse{
+	toJSON(w, mtypes.ListDomainsResponse{
 		TotalCount: len(list),
 		Items:      list[skip:end],
 	})
@@ -159,13 +160,13 @@ func (ms *mockServer) createDomain(w http.ResponseWriter, r *http.Request) {
 	ms.mutex.Lock()
 
 	ms.domainList = append(ms.domainList, DomainContainer{
-		Domain: Domain{
-			CreatedAt:    RFC2822Time(time.Now()),
+		Domain: mtypes.Domain{
+			CreatedAt:    mtypes.RFC2822Time(time.Now()),
 			Name:         r.FormValue("name"),
 			SMTPLogin:    r.FormValue("smtp_login"),
 			SMTPPassword: r.FormValue("smtp_password"),
 			Wildcard:     stringToBool(r.FormValue("wildcard")),
-			SpamAction:   SpamAction(r.FormValue("spam_action")),
+			SpamAction:   mtypes.SpamAction(r.FormValue("spam_action")),
 			State:        "active",
 			WebScheme:    "http",
 		},
@@ -214,7 +215,7 @@ func (ms *mockServer) getConnection(w http.ResponseWriter, r *http.Request) {
 
 	for _, d := range ms.domainList {
 		if d.Domain.Name == chi.URLParam(r, "domain") {
-			resp := domainConnectionResponse{
+			resp := mtypes.DomainConnectionResponse{
 				Connection: *d.Connection,
 			}
 			toJSON(w, resp)
@@ -231,7 +232,7 @@ func (ms *mockServer) updateConnection(w http.ResponseWriter, r *http.Request) {
 
 	for i, d := range ms.domainList {
 		if d.Domain.Name == chi.URLParam(r, "domain") {
-			ms.domainList[i].Connection = &DomainConnection{
+			ms.domainList[i].Connection = &mtypes.DomainConnection{
 				RequireTLS:       stringToBool(r.FormValue("require_tls")),
 				SkipVerification: stringToBool(r.FormValue("skip_verification")),
 			}
@@ -249,7 +250,7 @@ func (ms *mockServer) getTracking(w http.ResponseWriter, r *http.Request) {
 
 	for _, d := range ms.domainList {
 		if d.Domain.Name == chi.URLParam(r, "domain") {
-			resp := domainTrackingResponse{
+			resp := mtypes.DomainTrackingResponse{
 				Tracking: *d.Tracking,
 			}
 			toJSON(w, resp)
diff --git a/mock_events.go b/mock_events.go
index c0a88a1..f0a91bc 100644
--- a/mock_events.go
+++ b/mock_events.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/go-chi/chi/v5"
 	"github.com/mailgun/mailgun-go/v4/events"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addEventRoutes(r chi.Router) {
@@ -190,8 +191,8 @@ func (ms *mockServer) addEventRoutes(r chi.Router) {
 }
 
 type eventsResponse struct {
-	Items  []Event `json:"items"`
-	Paging Paging  `json:"paging"`
+	Items  []Event       `json:"items"`
+	Paging mtypes.Paging `json:"paging"`
 }
 
 func (ms *mockServer) listEvents(w http.ResponseWriter, r *http.Request) {
@@ -223,7 +224,7 @@ func (ms *mockServer) listEvents(w http.ResponseWriter, r *http.Request) {
 	}
 
 	resp := eventsResponse{
-		Paging: Paging{
+		Paging: mtypes.Paging{
 			First: getPageURL(r, url.Values{
 				"page": []string{"first"},
 			}),
diff --git a/mock_exports.go b/mock_exports.go
index 737d5cf..9865246 100644
--- a/mock_exports.go
+++ b/mock_exports.go
@@ -5,6 +5,7 @@ import (
 	"strconv"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addExportRoutes(r chi.Router) {
@@ -17,7 +18,7 @@ func (ms *mockServer) addExportRoutes(r chi.Router) {
 func (ms *mockServer) postExports(w http.ResponseWriter, r *http.Request) {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
-	e := Export{
+	e := mtypes.Export{
 		ID:     strconv.Itoa(len(ms.exportList)),
 		URL:    r.FormValue("url"),
 		Status: "complete",
@@ -30,7 +31,7 @@ func (ms *mockServer) postExports(w http.ResponseWriter, r *http.Request) {
 func (ms *mockServer) listExports(w http.ResponseWriter, _ *http.Request) {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
-	toJSON(w, ExportList{
+	toJSON(w, mtypes.ExportList{
 		Items: ms.exportList,
 	})
 }
diff --git a/mock_ips.go b/mock_ips.go
index 12fbd19..6a31d41 100644
--- a/mock_ips.go
+++ b/mock_ips.go
@@ -4,6 +4,7 @@ import (
 	"net/http"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addIPRoutes(r chi.Router) {
@@ -18,14 +19,14 @@ func (ms *mockServer) addIPRoutes(r chi.Router) {
 }
 
 func (ms *mockServer) listIPS(w http.ResponseWriter, _ *http.Request) {
-	toJSON(w, ipAddressListResponse{
+	toJSON(w, mtypes.IPAddressListResponse{
 		TotalCount: 2,
 		Items:      []string{"172.0.0.1", "192.168.1.1"},
 	})
 }
 
 func (ms *mockServer) getIPAddress(w http.ResponseWriter, r *http.Request) {
-	toJSON(w, IPAddress{
+	toJSON(w, mtypes.IPAddress{
 		IP:        chi.URLParam(r, "ip"),
 		RDNS:      "luna.mailgun.net",
 		Dedicated: true,
@@ -35,7 +36,7 @@ func (ms *mockServer) getIPAddress(w http.ResponseWriter, r *http.Request) {
 func (ms *mockServer) listDomainIPS(w http.ResponseWriter, _ *http.Request) {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
-	toJSON(w, ipAddressListResponse{
+	toJSON(w, mtypes.IPAddressListResponse{
 		TotalCount: 2,
 		Items:      ms.domainIPS,
 	})
diff --git a/mock_mailing_list.go b/mock_mailing_list.go
index 5482264..1a33c64 100644
--- a/mock_mailing_list.go
+++ b/mock_mailing_list.go
@@ -7,11 +7,12 @@ import (
 	"time"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 type MailingListContainer struct {
-	MailingList MailingList
-	Members     []Member
+	MailingList mtypes.MailingList
+	Members     []mtypes.Member
 }
 
 func (ms *mockServer) addMailingListRoutes(r chi.Router) {
@@ -29,16 +30,16 @@ func (ms *mockServer) addMailingListRoutes(r chi.Router) {
 	r.Post("/lists/{address}/members.json", ms.bulkCreate)
 
 	ms.mailingList = append(ms.mailingList, MailingListContainer{
-		MailingList: MailingList{
+		MailingList: mtypes.MailingList{
 			ReplyPreference: "list",
 			AccessLevel:     "everyone",
 			Address:         "foo@mailgun.test",
-			CreatedAt:       RFC2822Time(time.Now().UTC()),
+			CreatedAt:       mtypes.RFC2822Time(time.Now().UTC()),
 			Description:     "Mailgun developers list",
 			MembersCount:    1,
 			Name:            "",
 		},
-		Members: []Member{
+		Members: []mtypes.Member{
 			{
 				Address: "dev@samples.mailgun.org",
 				Name:    "Developer",
@@ -51,7 +52,7 @@ func (ms *mockServer) listMailingLists(w http.ResponseWriter, r *http.Request) {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 
-	var list []MailingList
+	var list []mtypes.MailingList
 	var idx []string
 
 	for _, ml := range ms.mailingList {
@@ -67,12 +68,12 @@ func (ms *mockServer) listMailingLists(w http.ResponseWriter, r *http.Request) {
 	results := list[start:end]
 
 	if len(results) == 0 {
-		toJSON(w, listsResponse{})
+		toJSON(w, mtypes.ListMailingListsResponse{})
 		return
 	}
 
-	resp := listsResponse{
-		Paging: Paging{
+	resp := mtypes.ListMailingListsResponse{
+		Paging: mtypes.Paging{
 			First: getPageURL(r, url.Values{
 				"page": []string{"first"},
 			}),
@@ -99,7 +100,7 @@ func (ms *mockServer) getMailingList(w http.ResponseWriter, r *http.Request) {
 
 	for _, ml := range ms.mailingList {
 		if ml.MailingList.Address == chi.URLParam(r, "address") {
-			toJSON(w, mailingListResponse{MailingList: ml.MailingList})
+			toJSON(w, mtypes.GetMailingListResponse{MailingList: ml.MailingList})
 			return
 		}
 	}
@@ -145,10 +146,10 @@ func (ms *mockServer) updateMailingList(w http.ResponseWriter, r *http.Request)
 				ms.mailingList[i].MailingList.Description = r.FormValue("description")
 			}
 			if r.FormValue("access_level") != "" {
-				ms.mailingList[i].MailingList.AccessLevel = AccessLevel(r.FormValue("access_level"))
+				ms.mailingList[i].MailingList.AccessLevel = mtypes.AccessLevel(r.FormValue("access_level"))
 			}
 			if r.FormValue("reply_preference") != "" {
-				ms.mailingList[i].MailingList.ReplyPreference = ReplyPreference(r.FormValue("reply_preference"))
+				ms.mailingList[i].MailingList.ReplyPreference = mtypes.ReplyPreference(r.FormValue("reply_preference"))
 			}
 			toJSON(w, okResp{Message: "Mailing list member has been updated"})
 			return
@@ -163,13 +164,13 @@ func (ms *mockServer) createMailingList(w http.ResponseWriter, r *http.Request)
 	ms.mutex.Lock()
 
 	ms.mailingList = append(ms.mailingList, MailingListContainer{
-		MailingList: MailingList{
-			CreatedAt:       RFC2822Time(time.Now().UTC()),
+		MailingList: mtypes.MailingList{
+			CreatedAt:       mtypes.RFC2822Time(time.Now().UTC()),
 			Name:            r.FormValue("name"),
 			Address:         r.FormValue("address"),
 			Description:     r.FormValue("description"),
-			AccessLevel:     AccessLevel(r.FormValue("access_level")),
-			ReplyPreference: ReplyPreference(r.FormValue("reply_preference")),
+			AccessLevel:     mtypes.AccessLevel(r.FormValue("access_level")),
+			ReplyPreference: mtypes.ReplyPreference(r.FormValue("reply_preference")),
 		},
 	})
 	toJSON(w, okResp{Message: "Mailing list has been created"})
@@ -179,7 +180,7 @@ func (ms *mockServer) listMembers(w http.ResponseWriter, r *http.Request) {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 
-	var list []Member
+	var list []mtypes.Member
 	var idx []string
 	var found bool
 
@@ -207,12 +208,12 @@ func (ms *mockServer) listMembers(w http.ResponseWriter, r *http.Request) {
 	results := list[start:end]
 
 	if len(results) == 0 {
-		toJSON(w, memberListResponse{})
+		toJSON(w, mtypes.MemberListResponse{})
 		return
 	}
 
-	resp := memberListResponse{
-		Paging: Paging{
+	resp := mtypes.MemberListResponse{
+		Paging: mtypes.Paging{
 			First: getPageURL(r, url.Values{
 				"page": []string{"first"},
 			}),
@@ -243,7 +244,7 @@ func (ms *mockServer) getMember(w http.ResponseWriter, r *http.Request) {
 			found = true
 			for _, member := range ml.Members {
 				if member.Address == chi.URLParam(r, "member") {
-					toJSON(w, memberResponse{Member: member})
+					toJSON(w, mtypes.MemberResponse{Member: member})
 					return
 				}
 			}
@@ -372,7 +373,7 @@ func (ms *mockServer) createMember(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	ms.mailingList[idx].Members = append(ms.mailingList[idx].Members, Member{
+	ms.mailingList[idx].Members = append(ms.mailingList[idx].Members, mtypes.Member{
 		Name:       r.FormValue("name"),
 		Address:    parseAddress(r.FormValue("address")),
 		Vars:       stringToMap(r.FormValue("vars")),
@@ -398,7 +399,7 @@ func (ms *mockServer) bulkCreate(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	var bulkList []Member
+	var bulkList []mtypes.Member
 	if err := json.Unmarshal([]byte(r.FormValue("members")), &bulkList); err != nil {
 		w.WriteHeader(http.StatusInternalServerError)
 		toJSON(w, okResp{Message: "while un-marshalling 'members' param - " + err.Error()})
diff --git a/mock_messages.go b/mock_messages.go
index 539a73a..26b35b1 100644
--- a/mock_messages.go
+++ b/mock_messages.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/go-chi/chi/v5"
 	"github.com/mailgun/mailgun-go/v4/events"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addMessagesRoutes(r chi.Router) {
@@ -94,7 +95,7 @@ func (ms *mockServer) createMessages(w http.ResponseWriter, r *http.Request) {
 		}
 
 		if !tagExists {
-			ms.tags = append(ms.tags, Tag{Value: newTag})
+			ms.tags = append(ms.tags, mtypes.Tag{Value: newTag})
 		}
 	}
 
@@ -119,7 +120,7 @@ func (ms *mockServer) getStoredMessages(w http.ResponseWriter, r *http.Request)
 		toJSON(w, okResp{Message: "not found"})
 	}
 
-	toJSON(w, StoredMessage{
+	toJSON(w, mtypes.StoredMessage{
 		Recipients: strings.Join(stored.Message.Recipients, ","),
 		Sender:     stored.Message.Headers.From,
 		Subject:    stored.Message.Headers.Subject,
diff --git a/mock_routes.go b/mock_routes.go
index ada658c..1a59f95 100644
--- a/mock_routes.go
+++ b/mock_routes.go
@@ -6,10 +6,11 @@ import (
 	"time"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 type routeResponse struct {
-	Route Route `json:"route"`
+	Route mtypes.Route `json:"route"`
 }
 
 func (ms *mockServer) addRoutes(r chi.Router) {
@@ -20,7 +21,7 @@ func (ms *mockServer) addRoutes(r chi.Router) {
 	r.Delete("/routes/{id}", ms.deleteRoute)
 
 	for i := 0; i < 10; i++ {
-		ms.routeList = append(ms.routeList, Route{
+		ms.routeList = append(ms.routeList, mtypes.Route{
 			Id:          randomString(10, "ID-"),
 			Priority:    0,
 			Description: fmt.Sprintf("Sample Route %d", i),
@@ -51,14 +52,14 @@ func (ms *mockServer) listRoutes(w http.ResponseWriter, r *http.Request) {
 
 	// If we are at the end of the list
 	if skip == end {
-		toJSON(w, routesListResponse{
+		toJSON(w, mtypes.RoutesListResponse{
 			TotalCount: len(ms.routeList),
-			Items:      []Route{},
+			Items:      []mtypes.Route{},
 		})
 		return
 	}
 
-	toJSON(w, routesListResponse{
+	toJSON(w, mtypes.RoutesListResponse{
 		TotalCount: len(ms.routeList),
 		Items:      ms.routeList[skip:end],
 	})
@@ -85,15 +86,15 @@ func (ms *mockServer) createRoute(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	ms.routeList = append(ms.routeList, Route{
-		CreatedAt:   RFC2822Time(time.Now().UTC()),
+	ms.routeList = append(ms.routeList, mtypes.Route{
+		CreatedAt:   mtypes.RFC2822Time(time.Now().UTC()),
 		Id:          randomString(10, "ID-"),
 		Priority:    stringToInt(r.FormValue("priority")),
 		Description: r.FormValue("description"),
 		Expression:  r.FormValue("expression"),
 		Actions:     r.Form["action"],
 	})
-	toJSON(w, createRouteResp{
+	toJSON(w, mtypes.CreateRouteResp{
 		Message: "Route has been created",
 		Route:   ms.routeList[len(ms.routeList)-1],
 	})
diff --git a/mock_subaccounts.go b/mock_subaccounts.go
index c6141b2..357ce96 100644
--- a/mock_subaccounts.go
+++ b/mock_subaccounts.go
@@ -4,15 +4,16 @@ import (
 	"net/http"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addSubaccountRoutes(r chi.Router) {
-	ms.subaccountList = append(ms.subaccountList, Subaccount{
-		Id:     "enabled.subaccount",
+	ms.subaccountList = append(ms.subaccountList, mtypes.Subaccount{
+		ID:     "enabled.subaccount",
 		Name:   "mailgun.test",
 		Status: "enabled",
-	}, Subaccount{
-		Id:     "disabled.subaccount",
+	}, mtypes.Subaccount{
+		ID:     "disabled.subaccount",
 		Name:   "mailgun.test",
 		Status: "disabled",
 	})
@@ -29,7 +30,7 @@ func (ms *mockServer) listSubaccounts(w http.ResponseWriter, r *http.Request) {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 
-	var list subaccountsListResponse
+	var list mtypes.ListSubaccountsResponse
 	for _, subaccount := range ms.subaccountList {
 		list.Items = append(list.Items, subaccount)
 	}
@@ -51,14 +52,14 @@ func (ms *mockServer) listSubaccounts(w http.ResponseWriter, r *http.Request) {
 
 	// If we are at the end of the list
 	if skip == end {
-		toJSON(w, subaccountsListResponse{
+		toJSON(w, mtypes.ListSubaccountsResponse{
 			Total: len(list.Items),
-			Items: []Subaccount{},
+			Items: []mtypes.Subaccount{},
 		})
 		return
 	}
 
-	toJSON(w, subaccountsListResponse{
+	toJSON(w, mtypes.ListSubaccountsResponse{
 		Total: len(list.Items),
 		Items: list.Items[skip:end],
 	})
@@ -69,8 +70,8 @@ func (ms *mockServer) getSubaccount(w http.ResponseWriter, r *http.Request) {
 	ms.mutex.Lock()
 
 	for _, s := range ms.subaccountList {
-		if s.Id == chi.URLParam(r, "subaccountID") {
-			toJSON(w, SubaccountResponse{Item: s})
+		if s.ID == chi.URLParam(r, "subaccountID") {
+			toJSON(w, mtypes.SubaccountResponse{Item: s})
 			return
 		}
 	}
@@ -82,8 +83,8 @@ func (ms *mockServer) createSubaccount(w http.ResponseWriter, r *http.Request) {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 
-	ms.subaccountList = append(ms.subaccountList, Subaccount{
-		Id:     "test",
+	ms.subaccountList = append(ms.subaccountList, mtypes.Subaccount{
+		ID:     "test",
 		Name:   r.FormValue("name"),
 		Status: "active",
 	})
@@ -95,12 +96,12 @@ func (ms *mockServer) enableSubaccount(w http.ResponseWriter, r *http.Request) {
 	ms.mutex.Lock()
 
 	for _, subaccount := range ms.subaccountList {
-		if subaccount.Id == chi.URLParam(r, "subaccountID") && subaccount.Status == "disabled" {
+		if subaccount.ID == chi.URLParam(r, "subaccountID") && subaccount.Status == "disabled" {
 			subaccount.Status = "enabled"
-			toJSON(w, SubaccountResponse{Item: subaccount})
+			toJSON(w, mtypes.SubaccountResponse{Item: subaccount})
 			return
 		}
-		if subaccount.Id == chi.URLParam(r, "subaccountID") && subaccount.Status == "enabled" {
+		if subaccount.ID == chi.URLParam(r, "subaccountID") && subaccount.Status == "enabled" {
 			toJSON(w, okResp{Message: "subaccount is already enabled"})
 			return
 		}
@@ -113,12 +114,12 @@ func (ms *mockServer) disableSubaccount(w http.ResponseWriter, r *http.Request)
 	ms.mutex.Lock()
 
 	for _, subaccount := range ms.subaccountList {
-		if subaccount.Id == chi.URLParam(r, "subaccountID") && subaccount.Status == "enabled" {
+		if subaccount.ID == chi.URLParam(r, "subaccountID") && subaccount.Status == "enabled" {
 			subaccount.Status = "disabled"
-			toJSON(w, SubaccountResponse{Item: subaccount})
+			toJSON(w, mtypes.SubaccountResponse{Item: subaccount})
 			return
 		}
-		if subaccount.Id == chi.URLParam(r, "subaccountID") && subaccount.Status == "disabled" {
+		if subaccount.ID == chi.URLParam(r, "subaccountID") && subaccount.Status == "disabled" {
 			toJSON(w, okResp{Message: "subaccount is already disabled"})
 			return
 		}
diff --git a/mock_tags.go b/mock_tags.go
index 09e0d85..5082506 100644
--- a/mock_tags.go
+++ b/mock_tags.go
@@ -6,6 +6,7 @@ import (
 	"time"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addTagsRoutes(r chi.Router) {
@@ -16,14 +17,14 @@ func (ms *mockServer) addTagsRoutes(r chi.Router) {
 
 	tenMinutesBefore := time.Now().Add(-10 * time.Minute)
 	now := time.Now()
-	ms.tags = append(ms.tags, Tag{
+	ms.tags = append(ms.tags, mtypes.Tag{
 		Value:       "test",
 		Description: "test description",
 		FirstSeen:   &tenMinutesBefore,
 		LastSeen:    &now,
 	})
 
-	ms.tags = append(ms.tags, Tag{
+	ms.tags = append(ms.tags, mtypes.Tag{
 		Value:       "test2",
 		Description: "test2 description",
 		FirstSeen:   &tenMinutesBefore,
@@ -57,20 +58,20 @@ func (ms *mockServer) listTags(w http.ResponseWriter, r *http.Request) {
 	}
 	start, end := pageOffsets(idx, page, pivot, limit)
 	var nextAddress, prevAddress string
-	var results []Tag
+	var results []mtypes.Tag
 
 	if start != end {
 		results = ms.tags[start:end]
 		nextAddress = results[len(results)-1].Value
 		prevAddress = results[0].Value
 	} else {
-		results = []Tag{}
+		results = []mtypes.Tag{}
 		nextAddress = pivot
 		prevAddress = pivot
 	}
 
-	toJSON(w, tagsResponse{
-		Paging: Paging{
+	toJSON(w, mtypes.TagsResponse{
+		Paging: mtypes.Paging{
 			First: getPageURL(r, url.Values{
 				"page": []string{"first"},
 			}),
@@ -125,7 +126,7 @@ func (ms *mockServer) createUpdateTags(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if !tagExists {
-		ms.tags = append(ms.tags, Tag{Value: tag, Description: description})
+		ms.tags = append(ms.tags, mtypes.Tag{Value: tag, Description: description})
 	}
 
 	toJSON(w, map[string]any{
diff --git a/mock_template_versions.go b/mock_template_versions.go
index f388100..757e9f1 100644
--- a/mock_template_versions.go
+++ b/mock_template_versions.go
@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addTemplateVersionRoutes(r chi.Router) {
@@ -54,20 +55,20 @@ func (ms *mockServer) listTemplateVersions(w http.ResponseWriter, r *http.Reques
 	}
 	start, end := pageOffsets(idx, page, pivot, limit)
 	var nextAddress, prevAddress string
-	var results []TemplateVersion
+	var results []mtypes.TemplateVersion
 
 	if start != end {
 		results = ms.templateVersions[templateName][start:end]
 		nextAddress = results[len(results)-1].Tag
 		prevAddress = results[0].Tag
 	} else {
-		results = []TemplateVersion{}
+		results = []mtypes.TemplateVersion{}
 		nextAddress = pivot
 		prevAddress = pivot
 	}
 
-	toJSON(w, templateVersionListResp{
-		Paging: Paging{
+	toJSON(w, mtypes.TemplateVersionListResp{
+		Paging: mtypes.Paging{
 			First: getPageURL(r, url.Values{
 				"page": []string{"first"},
 			}),
@@ -84,8 +85,8 @@ func (ms *mockServer) listTemplateVersions(w http.ResponseWriter, r *http.Reques
 			}),
 		},
 		Template: struct {
-			Template
-			Versions []TemplateVersion `json:"versions,omitempty"`
+			mtypes.Template
+			Versions []mtypes.TemplateVersion `json:"versions,omitempty"`
 		}{
 			Template: template,
 			Versions: results,
@@ -118,7 +119,7 @@ func (ms *mockServer) getTemplateVersion(w http.ResponseWriter, r *http.Request)
 
 	template.Version = templateVersion
 
-	toJSON(w, &templateResp{
+	toJSON(w, &mtypes.TemplateResp{
 		Item: template,
 	})
 }
@@ -172,12 +173,12 @@ func (ms *mockServer) createTemplateVersion(w http.ResponseWriter, r *http.Reque
 		engine = "handlebars"
 	}
 
-	newTemplateVersion := TemplateVersion{
+	newTemplateVersion := mtypes.TemplateVersion{
 		Template:  templateContent,
 		Comment:   comment,
 		Tag:       tagName,
-		Engine:    TemplateEngine(engine),
-		CreatedAt: RFC2822Time(time.Now()),
+		Engine:    mtypes.TemplateEngine(engine),
+		CreatedAt: mtypes.RFC2822Time(time.Now()),
 	}
 
 	if active == "yes" {
@@ -212,7 +213,7 @@ func (ms *mockServer) updateTemplateVersion(w http.ResponseWriter, r *http.Reque
 	}
 
 	var templateVersionFound bool
-	var templateVersion TemplateVersion
+	var templateVersion mtypes.TemplateVersion
 	var templateVersionIndex int
 	for i, tmplVersion := range ms.templateVersions[templateName] {
 		if tmplVersion.Tag == templateVersionName {
@@ -308,7 +309,7 @@ func (ms *mockServer) deleteTemplateVersion(w http.ResponseWriter, r *http.Reque
 	})
 }
 
-func (ms *mockServer) fetchTemplate(name string) (template Template, found bool) {
+func (ms *mockServer) fetchTemplate(name string) (template mtypes.Template, found bool) {
 	for _, existingTemplate := range ms.templates {
 		if existingTemplate.Name == name {
 			template = existingTemplate
@@ -316,15 +317,15 @@ func (ms *mockServer) fetchTemplate(name string) (template Template, found bool)
 		}
 	}
 
-	return Template{}, false
+	return mtypes.Template{}, false
 }
 
-func (ms *mockServer) fetchTemplateVersion(templateName string, templateVersionTag string) (TemplateVersion, bool) {
+func (ms *mockServer) fetchTemplateVersion(templateName string, templateVersionTag string) (mtypes.TemplateVersion, bool) {
 	for _, existingTemplate := range ms.templateVersions[templateName] {
 		if existingTemplate.Tag == templateVersionTag {
 			return existingTemplate, true
 		}
 	}
 
-	return TemplateVersion{}, false
+	return mtypes.TemplateVersion{}, false
 }
diff --git a/mock_templates.go b/mock_templates.go
index fffea13..357378d 100644
--- a/mock_templates.go
+++ b/mock_templates.go
@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addTemplateRoutes(r chi.Router) {
@@ -19,53 +20,53 @@ func (ms *mockServer) addTemplateRoutes(r chi.Router) {
 	r.Delete("/{domain}/templates/{name}", ms.deleteTemplate)
 	r.Delete("/{domain}/templates/{name}", ms.deleteAllTemplates)
 
-	ms.templates = append(ms.templates, Template{
+	ms.templates = append(ms.templates, mtypes.Template{
 		Name:        "template1",
 		Description: "template1 description",
-		CreatedAt:   RFC2822Time(time.Now()),
+		CreatedAt:   mtypes.RFC2822Time(time.Now()),
 	})
 
-	ms.templateVersions = make(map[string][]TemplateVersion)
-	ms.templateVersions["template1"] = []TemplateVersion{
+	ms.templateVersions = make(map[string][]mtypes.TemplateVersion)
+	ms.templateVersions["template1"] = []mtypes.TemplateVersion{
 		{
 			Tag:       "test",
 			Template:  "template1 content",
 			Engine:    "go",
-			CreatedAt: RFC2822Time(time.Now()),
+			CreatedAt: mtypes.RFC2822Time(time.Now()),
 			Comment:   "template1 comment",
 			Active:    true,
 		},
 	}
 
-	ms.templates = append(ms.templates, Template{
+	ms.templates = append(ms.templates, mtypes.Template{
 		Name:        "template2",
 		Description: "template2 description",
-		CreatedAt:   RFC2822Time(time.Now()),
+		CreatedAt:   mtypes.RFC2822Time(time.Now()),
 	})
 
-	ms.templateVersions["template2"] = []TemplateVersion{
+	ms.templateVersions["template2"] = []mtypes.TemplateVersion{
 		{
 			Tag:       "test",
 			Template:  "template2 content",
 			Engine:    "go",
-			CreatedAt: RFC2822Time(time.Now()),
+			CreatedAt: mtypes.RFC2822Time(time.Now()),
 			Comment:   "template2 comment",
 			Active:    false,
 		},
 	}
 
-	ms.templates = append(ms.templates, Template{
+	ms.templates = append(ms.templates, mtypes.Template{
 		Name:        "template3",
 		Description: "template3 description",
-		CreatedAt:   RFC2822Time(time.Now()),
+		CreatedAt:   mtypes.RFC2822Time(time.Now()),
 	})
 
-	ms.templateVersions["template3"] = []TemplateVersion{
+	ms.templateVersions["template3"] = []mtypes.TemplateVersion{
 		{
 			Tag:       "test",
 			Template:  "template3 content",
 			Engine:    "go",
-			CreatedAt: RFC2822Time(time.Now()),
+			CreatedAt: mtypes.RFC2822Time(time.Now()),
 			Comment:   "template3 comment",
 			Active:    false,
 		},
@@ -98,20 +99,20 @@ func (ms *mockServer) listTemplates(w http.ResponseWriter, r *http.Request) {
 	}
 	start, end := pageOffsets(idx, page, pivot, limit)
 	var nextAddress, prevAddress string
-	var results []Template
+	var results []mtypes.Template
 
 	if start != end {
 		results = ms.templates[start:end]
 		nextAddress = results[len(results)-1].Name
 		prevAddress = results[0].Name
 	} else {
-		results = []Template{}
+		results = []mtypes.Template{}
 		nextAddress = pivot
 		prevAddress = pivot
 	}
 
-	toJSON(w, templateListResp{
-		Paging: Paging{
+	toJSON(w, mtypes.ListTemplateResp{
+		Paging: mtypes.Paging{
 			First: getPageURL(r, url.Values{
 				"page": []string{"first"},
 			}),
@@ -154,7 +155,7 @@ func (ms *mockServer) getTemplate(w http.ResponseWriter, r *http.Request) {
 					template.Version = version
 				}
 			}
-			toJSON(w, &templateResp{
+			toJSON(w, &mtypes.TemplateResp{
 				Item: template,
 			})
 			return
@@ -187,8 +188,8 @@ func (ms *mockServer) createTemplate(w http.ResponseWriter, r *http.Request) {
 
 	name = strings.ToLower(name)
 
-	template := Template{Name: name}
-	template.CreatedAt = RFC2822Time(time.Now())
+	template := mtypes.Template{Name: name}
+	template.CreatedAt = mtypes.RFC2822Time(time.Now())
 
 	description := r.FormValue("description")
 	if len(description) > 0 {
@@ -197,7 +198,7 @@ func (ms *mockServer) createTemplate(w http.ResponseWriter, r *http.Request) {
 
 	templateContent := r.FormValue("template")
 	if len(templateContent) > 0 {
-		templateVersion := TemplateVersion{Template: templateContent}
+		templateVersion := mtypes.TemplateVersion{Template: templateContent}
 		tag := r.FormValue("tag")
 		if len(tag) > 0 {
 			templateVersion.Tag = tag
@@ -206,7 +207,7 @@ func (ms *mockServer) createTemplate(w http.ResponseWriter, r *http.Request) {
 		}
 
 		templateVersion.Comment = r.FormValue("comment")
-		templateVersion.CreatedAt = RFC2822Time(time.Now())
+		templateVersion.CreatedAt = mtypes.RFC2822Time(time.Now())
 		templateVersion.Active = true
 
 		engine := r.FormValue("engine")
@@ -216,7 +217,7 @@ func (ms *mockServer) createTemplate(w http.ResponseWriter, r *http.Request) {
 				w.Write([]byte(fmt.Sprintf("{\"message\": \"Invalid parameter: engine %s is not supported\"}", engine)))
 				return
 			}
-			templateVersion.Engine = TemplateEngine(engine)
+			templateVersion.Engine = mtypes.TemplateEngine(engine)
 		}
 
 		template.Version = templateVersion
@@ -296,18 +297,18 @@ func (ms *mockServer) deleteAllTemplates(w http.ResponseWriter, r *http.Request)
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 
-	ms.templates = []Template{}
-	ms.templateVersions = map[string][]TemplateVersion{}
+	ms.templates = []mtypes.Template{}
+	ms.templateVersions = map[string][]mtypes.TemplateVersion{}
 
 	toJSON(w, map[string]string{"message": "templates have been deleted"})
 }
 
-func (ms *mockServer) getActiveTemplateVersion(templateName string) TemplateVersion {
+func (ms *mockServer) getActiveTemplateVersion(templateName string) mtypes.TemplateVersion {
 	for _, templateVersion := range ms.templateVersions[templateName] {
 		if templateVersion.Active {
 			return templateVersion
 		}
 	}
 
-	return TemplateVersion{}
+	return mtypes.TemplateVersion{}
 }
diff --git a/mock_unsubscribes.go b/mock_unsubscribes.go
index 8091763..7827825 100644
--- a/mock_unsubscribes.go
+++ b/mock_unsubscribes.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addUnsubscribesRoutes(r chi.Router) {
@@ -17,15 +18,15 @@ func (ms *mockServer) addUnsubscribesRoutes(r chi.Router) {
 	r.Delete("/{domain}/unsubscribes/{address}", ms.deleteUnsubscribe)
 	r.Post("/{domain}/unsubscribes", ms.createUnsubscribe)
 
-	ms.unsubscribes = append(ms.unsubscribes, Unsubscribe{
-		CreatedAt: RFC2822Time(time.Now()),
+	ms.unsubscribes = append(ms.unsubscribes, mtypes.Unsubscribe{
+		CreatedAt: mtypes.RFC2822Time(time.Now()),
 		Tags:      []string{"*"},
 		ID:        "1",
 		Address:   "foo@mailgun.test",
 	})
 
-	ms.unsubscribes = append(ms.unsubscribes, Unsubscribe{
-		CreatedAt: RFC2822Time(time.Now()),
+	ms.unsubscribes = append(ms.unsubscribes, mtypes.Unsubscribe{
+		CreatedAt: mtypes.RFC2822Time(time.Now()),
 		Tags:      []string{"some", "tag"},
 		ID:        "2",
 		Address:   "alice@example.com",
@@ -58,20 +59,20 @@ func (ms *mockServer) listUnsubscribes(w http.ResponseWriter, r *http.Request) {
 	}
 	start, end := pageOffsets(idx, page, pivot, limit)
 	var nextAddress, prevAddress string
-	var results []Unsubscribe
+	var results []mtypes.Unsubscribe
 
 	if start != end {
 		results = ms.unsubscribes[start:end]
 		nextAddress = results[len(results)-1].Address
 		prevAddress = results[0].Address
 	} else {
-		results = []Unsubscribe{}
+		results = []mtypes.Unsubscribe{}
 		nextAddress = pivot
 		prevAddress = pivot
 	}
 
-	toJSON(w, unsubscribesResponse{
-		Paging: Paging{
+	toJSON(w, mtypes.ListUnsubscribesResponse{
+		Paging: mtypes.Paging{
 			First: getPageURL(r, url.Values{
 				"page": []string{"first"},
 			}),
@@ -109,7 +110,7 @@ func (ms *mockServer) createUnsubscribe(w http.ResponseWriter, r *http.Request)
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 
-	var unsubscribes []Unsubscribe
+	var unsubscribes []mtypes.Unsubscribe
 	if r.Header.Get("Content-Type") == "application/json" {
 		body, err := ioutil.ReadAll(r.Body)
 		if err != nil {
@@ -140,7 +141,7 @@ func (ms *mockServer) createUnsubscribe(w http.ResponseWriter, r *http.Request)
 			return
 		}
 
-		unsubscribes = append(unsubscribes, Unsubscribe{Address: address, Tags: []string{tag}})
+		unsubscribes = append(unsubscribes, mtypes.Unsubscribe{Address: address, Tags: []string{tag}})
 	}
 
 	for _, unsubscribe := range unsubscribes {
diff --git a/mock_validation.go b/mock_validation.go
index 163180b..9a0a13c 100644
--- a/mock_validation.go
+++ b/mock_validation.go
@@ -5,6 +5,7 @@ import (
 	"net/mail"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addValidationRoutes(r chi.Router) {
@@ -18,7 +19,7 @@ func (ms *mockServer) validateEmailV4(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	var results ValidateEmailResponse
+	var results mtypes.ValidateEmailResponse
 	results.Risk = "unknown"
 	_, err := mail.ParseAddress(r.FormValue("address"))
 	if err == nil {
@@ -26,7 +27,7 @@ func (ms *mockServer) validateEmailV4(w http.ResponseWriter, r *http.Request) {
 	}
 	results.Reason = []string{"no-reason"}
 	results.Result = "deliverable"
-	results.Engagement = &EngagementData{
+	results.Engagement = &mtypes.EngagementData{
 		Engaging: false,
 		Behavior: "disengaged",
 		IsBot:    false,
diff --git a/mock_webhooks.go b/mock_webhooks.go
index 163870b..d6a5742 100644
--- a/mock_webhooks.go
+++ b/mock_webhooks.go
@@ -4,6 +4,7 @@ import (
 	"net/http"
 
 	"github.com/go-chi/chi/v5"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func (ms *mockServer) addWebhookRoutes(r chi.Router) {
@@ -15,8 +16,8 @@ func (ms *mockServer) addWebhookRoutes(r chi.Router) {
 		sr.Delete("/{webhook}", ms.deleteWebHook)
 	})
 
-	ms.webhooks = WebHooksListResponse{
-		Webhooks: map[string]UrlOrUrls{
+	ms.webhooks = mtypes.WebHooksListResponse{
+		Webhooks: map[string]mtypes.UrlOrUrls{
 			"new-webhook": {
 				Urls: []string{"http://example.com/new"},
 			},
@@ -38,8 +39,8 @@ func (ms *mockServer) getWebHook(w http.ResponseWriter, r *http.Request) {
 	defer ms.mutex.Unlock()
 	ms.mutex.Lock()
 
-	resp := WebHookResponse{
-		Webhook: UrlOrUrls{
+	resp := mtypes.WebHookResponse{
+		Webhook: mtypes.UrlOrUrls{
 			Urls: ms.webhooks.Webhooks[chi.URLParam(r, "webhook")].Urls,
 		},
 	}
@@ -60,7 +61,7 @@ func (ms *mockServer) postWebHook(w http.ResponseWriter, r *http.Request) {
 	for _, url := range r.Form["url"] {
 		urls = append(urls, url)
 	}
-	ms.webhooks.Webhooks[r.FormValue("id")] = UrlOrUrls{Urls: urls}
+	ms.webhooks.Webhooks[r.FormValue("id")] = mtypes.UrlOrUrls{Urls: urls}
 
 	toJSON(w, okResp{Message: "success"})
 }
@@ -79,7 +80,7 @@ func (ms *mockServer) putWebHook(w http.ResponseWriter, r *http.Request) {
 	for _, url := range r.Form["url"] {
 		urls = append(urls, url)
 	}
-	ms.webhooks.Webhooks[chi.URLParam(r, "webhook")] = UrlOrUrls{Urls: urls}
+	ms.webhooks.Webhooks[chi.URLParam(r, "webhook")] = mtypes.UrlOrUrls{Urls: urls}
 
 	toJSON(w, okResp{Message: "success"})
 }
diff --git a/mtypes/analytics.go b/mtypes/analytics.go
new file mode 100644
index 0000000..f2ac46d
--- /dev/null
+++ b/mtypes/analytics.go
@@ -0,0 +1,14 @@
+package mtypes
+
+type MetricsPagination struct {
+	// Colon-separated value indicating column name and sort direction e.g. 'domain:asc'.
+	Sort string `json:"sort"`
+	// The number of items to skip over when satisfying the request.
+	// To get the first page of data set skip to zero.
+	// Then increment the skip by the limit for subsequent calls.
+	Skip int `json:"skip"`
+	// The maximum number of items returned in the response.
+	Limit int `json:"limit"`
+	// The total number of items in the query result set.
+	Total int `json:"total"`
+}
diff --git a/analytics_request.go b/mtypes/analytics_requests.go
similarity index 97%
rename from analytics_request.go
rename to mtypes/analytics_requests.go
index 470f30b..650d216 100644
--- a/analytics_request.go
+++ b/mtypes/analytics_requests.go
@@ -1,6 +1,6 @@
-package mailgun
+package mtypes
 
-type MetricsOptions struct {
+type MetricsRequest struct {
 	// A start date (default: 7 days before current time).
 	Start RFC2822Time `json:"start,omitempty"`
 	// An end date (default: current time).
diff --git a/analytics_response.go b/mtypes/analytics_responses.go
similarity index 99%
rename from analytics_response.go
rename to mtypes/analytics_responses.go
index dab0800..20b9817 100644
--- a/analytics_response.go
+++ b/mtypes/analytics_responses.go
@@ -1,4 +1,4 @@
-package mailgun
+package mtypes
 
 type MetricsResponse struct {
 	Start      RFC2822Time       `json:"start"`
diff --git a/mtypes/bounces.go b/mtypes/bounces.go
new file mode 100644
index 0000000..1f20178
--- /dev/null
+++ b/mtypes/bounces.go
@@ -0,0 +1,14 @@
+package mtypes
+
+// Bounce aggregates data relating to undeliverable messages to a specific intended recipient,
+// identified by Address.
+type Bounce struct {
+	// The time at which Mailgun detected the bounce.
+	CreatedAt RFC2822Time `json:"created_at"`
+	// Code provides the SMTP error code that caused the bounce
+	Code string `json:"code"`
+	// Address the bounce is for
+	Address string `json:"address"`
+	// human readable reason why
+	Error string `json:"error"`
+}
diff --git a/mtypes/credentials.go b/mtypes/credentials.go
new file mode 100644
index 0000000..552915f
--- /dev/null
+++ b/mtypes/credentials.go
@@ -0,0 +1,8 @@
+package mtypes
+
+// A Credential structure describes a principle allowed to send or receive mail at the domain.
+type Credential struct {
+	CreatedAt RFC2822Time `json:"created_at"`
+	Login     string      `json:"login"`
+	Password  string      `json:"password"`
+}
diff --git a/mtypes/doc.go b/mtypes/doc.go
new file mode 100644
index 0000000..141da02
--- /dev/null
+++ b/mtypes/doc.go
@@ -0,0 +1,2 @@
+// Package mtypes contains API Mailgun requests, responses, and helper types.
+package mtypes
diff --git a/mtypes/domains.go b/mtypes/domains.go
new file mode 100644
index 0000000..21557df
--- /dev/null
+++ b/mtypes/domains.go
@@ -0,0 +1,51 @@
+package mtypes
+
+// Use these to specify a spam action when creating a new domain.
+const (
+	// SpamActionTag tags the received message with headers providing a measure of its spamness.
+	SpamActionTag = SpamAction("tag")
+	// SpamActionDisabled prevents Mailgun from taking any action on what it perceives to be spam.
+	SpamActionDisabled = SpamAction("disabled")
+	// SpamActionDelete instructs Mailgun to just block or delete the message all-together.
+	SpamActionDelete = SpamAction("delete")
+)
+
+type SpamAction string
+
+// A Domain structure holds information about a domain used when sending mail.
+type Domain struct {
+	CreatedAt                  RFC2822Time `json:"created_at"`
+	ID                         string      `json:"id"`
+	IsDisabled                 bool        `json:"is_disabled"`
+	Name                       string      `json:"name"`
+	RequireTLS                 bool        `json:"require_tls"`
+	SkipVerification           bool        `json:"skip_verification"`
+	SMTPLogin                  string      `json:"smtp_login"`
+	SMTPPassword               string      `json:"smtp_password,omitempty"`
+	SpamAction                 SpamAction  `json:"spam_action"`
+	State                      string      `json:"state"`
+	Type                       string      `json:"type"`
+	TrackingHost               string      `json:"tracking_host,omitempty"`
+	UseAutomaticSenderSecurity bool        `json:"use_automatic_sender_security"`
+	WebPrefix                  string      `json:"web_prefix"`
+	WebScheme                  string      `json:"web_scheme"`
+	Wildcard                   bool        `json:"wildcard"`
+}
+
+// DNSRecord structures describe intended records to properly configure your domain for use with Mailgun.
+// Note that Mailgun does not host DNS records.
+type DNSRecord struct {
+	Active     bool     `json:"is_active"`
+	Cached     []string `json:"cached"`
+	Name       string   `json:"name,omitempty"`
+	Priority   string   `json:"priority,omitempty"`
+	RecordType string   `json:"record_type"`
+	Valid      string   `json:"valid"`
+	Value      string   `json:"value"`
+}
+
+type GetDomainResponse struct {
+	Domain              Domain      `json:"domain"`
+	ReceivingDNSRecords []DNSRecord `json:"receiving_dns_records"`
+	SendingDNSRecords   []DNSRecord `json:"sending_dns_records"`
+}
diff --git a/mtypes/domains_connection.go b/mtypes/domains_connection.go
new file mode 100644
index 0000000..e12ba8e
--- /dev/null
+++ b/mtypes/domains_connection.go
@@ -0,0 +1,17 @@
+package mtypes
+
+type DomainConnectionResponse struct {
+	Connection DomainConnection `json:"connection"`
+}
+
+type ListDomainsResponse struct {
+	// is -1 if Next() or First() have not been called
+	TotalCount int      `json:"total_count"`
+	Items      []Domain `json:"items"`
+}
+
+// Specify the domain connection options
+type DomainConnection struct {
+	RequireTLS       bool `json:"require_tls"`
+	SkipVerification bool `json:"skip_verification"`
+}
diff --git a/mtypes/domains_tracking.go b/mtypes/domains_tracking.go
new file mode 100644
index 0000000..d3258a0
--- /dev/null
+++ b/mtypes/domains_tracking.go
@@ -0,0 +1,19 @@
+package mtypes
+
+type DomainTrackingResponse struct {
+	Tracking DomainTracking `json:"tracking"`
+}
+
+// Specify the domain tracking options
+type DomainTracking struct {
+	Click       TrackingStatus `json:"click"`
+	Open        TrackingStatus `json:"open"`
+	Unsubscribe TrackingStatus `json:"unsubscribe"`
+}
+
+// TrackingStatus is the tracking status of a domain
+type TrackingStatus struct {
+	Active     bool   `json:"active"`
+	HTMLFooter string `json:"html_footer"`
+	TextFooter string `json:"text_footer"`
+}
diff --git a/mtypes/exports.go b/mtypes/exports.go
new file mode 100644
index 0000000..2227d8c
--- /dev/null
+++ b/mtypes/exports.go
@@ -0,0 +1,11 @@
+package mtypes
+
+type ExportList struct {
+	Items []Export `json:"items"`
+}
+
+type Export struct {
+	ID     string `json:"id"`
+	Status string `json:"status"`
+	URL    string `json:"url"`
+}
diff --git a/mtypes/ips.go b/mtypes/ips.go
new file mode 100644
index 0000000..04f8f3b
--- /dev/null
+++ b/mtypes/ips.go
@@ -0,0 +1,12 @@
+package mtypes
+
+type IPAddressListResponse struct {
+	TotalCount int      `json:"total_count"`
+	Items      []string `json:"items"`
+}
+
+type IPAddress struct {
+	IP        string `json:"ip"`
+	RDNS      string `json:"rdns"`
+	Dedicated bool   `json:"dedicated"`
+}
diff --git a/mtypes/limits.go b/mtypes/limits.go
new file mode 100644
index 0000000..f2ec911
--- /dev/null
+++ b/mtypes/limits.go
@@ -0,0 +1,6 @@
+package mtypes
+
+type TagLimits struct {
+	Limit int `json:"limit"`
+	Count int `json:"count"`
+}
diff --git a/mtypes/mailgun.go b/mtypes/mailgun.go
new file mode 100644
index 0000000..21029f2
--- /dev/null
+++ b/mtypes/mailgun.go
@@ -0,0 +1,5 @@
+package mtypes
+
+func ptr[T any](v T) *T {
+	return &v
+}
diff --git a/mtypes/mailing_lists.go b/mtypes/mailing_lists.go
new file mode 100644
index 0000000..5081fd1
--- /dev/null
+++ b/mtypes/mailing_lists.go
@@ -0,0 +1,51 @@
+package mtypes
+
+// Specify the access of a mailing list member
+type AccessLevel string
+
+// A mailing list may have one of three membership modes.
+const (
+	// ReadOnly specifies that nobody, including Members, may send messages to
+	// the mailing list.  Messages distributed on such lists come from list
+	// administrator accounts only.
+	AccessLevelReadOnly = "readonly"
+	// Members specifies that only those who subscribe to the mailing list may
+	// send messages.
+	AccessLevelMembers = "members"
+	// Everyone specifies that anyone and everyone may both read and submit
+	// messages to the mailing list, including non-subscribers.
+	AccessLevelEveryone = "everyone"
+)
+
+// Set where replies should go
+type ReplyPreference string
+
+// Replies to a mailing list should go to one of two preferred destinations.
+const (
+	// List specifies that replies should be sent to the mailing list address.
+	ReplyPreferenceList = "list"
+	// Sender specifies that replies should be sent to the sender (FROM) address.
+	ReplyPreferenceSender = "sender"
+)
+
+// A List structure provides information for a mailing list.
+//
+// AccessLevel may be one of ReadOnly, Members, or Everyone.
+type MailingList struct {
+	Address         string          `json:"address,omitempty"`
+	Name            string          `json:"name,omitempty"`
+	Description     string          `json:"description,omitempty"`
+	AccessLevel     AccessLevel     `json:"access_level,omitempty"`
+	ReplyPreference ReplyPreference `json:"reply_preference,omitempty"`
+	CreatedAt       RFC2822Time     `json:"created_at,omitempty"`
+	MembersCount    int             `json:"members_count,omitempty"`
+}
+
+type ListMailingListsResponse struct {
+	Items  []MailingList `json:"items"`
+	Paging Paging        `json:"paging"`
+}
+
+type GetMailingListResponse struct {
+	MailingList MailingList `json:"list"`
+}
diff --git a/mtypes/members.go b/mtypes/members.go
new file mode 100644
index 0000000..381ef7b
--- /dev/null
+++ b/mtypes/members.go
@@ -0,0 +1,31 @@
+package mtypes
+
+// Mailing list members have an attribute that determines if they've subscribed to the mailing list or not.
+// This attribute may be used to filter the results returned by GetSubscribers().
+// All, Subscribed, and Unsubscribed provides a convenient and readable syntax for specifying the scope of the search.
+// We use a pointer to boolean as a kind of trinary data type:
+// if nil, the relevant data type remains unspecified.
+// Otherwise, its value is either true or false.
+var (
+	All          *bool
+	Subscribed   = ptr(true)
+	Unsubscribed = ptr(false)
+)
+
+// A Member structure represents a member of the mailing list.
+// The Vars field can represent any JSON-encodable data.
+type Member struct {
+	Address    string         `json:"address,omitempty"`
+	Name       string         `json:"name,omitempty"`
+	Subscribed *bool          `json:"subscribed,omitempty"`
+	Vars       map[string]any `json:"vars,omitempty"`
+}
+
+type MemberListResponse struct {
+	Lists  []Member `json:"items"`
+	Paging Paging   `json:"paging"`
+}
+
+type MemberResponse struct {
+	Member Member `json:"member"`
+}
diff --git a/mtypes/paging.go b/mtypes/paging.go
new file mode 100644
index 0000000..3d7fa53
--- /dev/null
+++ b/mtypes/paging.go
@@ -0,0 +1,8 @@
+package mtypes
+
+type Paging struct {
+	First    string `json:"first,omitempty"`
+	Next     string `json:"next,omitempty"`
+	Previous string `json:"previous,omitempty"`
+	Last     string `json:"last,omitempty"`
+}
diff --git a/reporting.go b/mtypes/resolution.go
similarity index 72%
rename from reporting.go
rename to mtypes/resolution.go
index f981383..482e0aa 100644
--- a/reporting.go
+++ b/mtypes/resolution.go
@@ -1,6 +1,4 @@
-package mailgun
-
-// common things for stats and metrics
+package mtypes
 
 type Resolution string
 
diff --git a/rfc2822.go b/mtypes/rfc2822.go
similarity index 98%
rename from rfc2822.go
rename to mtypes/rfc2822.go
index 6ff26fd..d28eab2 100644
--- a/rfc2822.go
+++ b/mtypes/rfc2822.go
@@ -1,4 +1,4 @@
-package mailgun
+package mtypes
 
 import (
 	"strconv"
diff --git a/mtypes/routes.go b/mtypes/routes.go
new file mode 100644
index 0000000..eada95a
--- /dev/null
+++ b/mtypes/routes.go
@@ -0,0 +1,35 @@
+package mtypes
+
+// A Route structure contains information on a configured or to-be-configured route.
+// When creating a new route, the SDK only uses a subset of the fields of this structure.
+// In particular, CreatedAt and ID are meaningless in this context, and will be ignored.
+// Only Priority, Description, Expression, and Actions need be provided.
+type Route struct {
+	// The Priority field indicates how soon the route works relative to other configured routes.
+	// Routes of equal priority are consulted in chronological order.
+	Priority int `json:"priority,omitempty"`
+	// The Description field provides a human-readable description for the route.
+	// Mailgun ignores this field except to provide the description when viewing the Mailgun web control panel.
+	Description string `json:"description,omitempty"`
+	// The Expression field lets you specify a pattern to match incoming messages against.
+	Expression string `json:"expression,omitempty"`
+	// The Actions field contains strings specifying what to do
+	// with any message which matches the provided expression.
+	Actions []string `json:"actions,omitempty"`
+
+	// The CreatedAt field provides a time-stamp for when the route came into existence.
+	CreatedAt RFC2822Time `json:"created_at,omitempty"`
+	// ID field provides a unique identifier for this route.
+	Id string `json:"id,omitempty"`
+}
+
+type RoutesListResponse struct {
+	// is -1 if Next() or First() have not been called
+	TotalCount int     `json:"total_count"`
+	Items      []Route `json:"items"`
+}
+
+type CreateRouteResp struct {
+	Message string `json:"message"`
+	Route   `json:"route"`
+}
diff --git a/mtypes/spam_complaints.go b/mtypes/spam_complaints.go
new file mode 100644
index 0000000..2429d84
--- /dev/null
+++ b/mtypes/spam_complaints.go
@@ -0,0 +1,14 @@
+package mtypes
+
+// Complaint structures track how many times one of your emails have been marked as spam.
+// the recipient thought your messages were not solicited.
+type Complaint struct {
+	Count     int         `json:"count"`
+	CreatedAt RFC2822Time `json:"created_at"`
+	Address   string      `json:"address"`
+}
+
+type ComplaintsResponse struct {
+	Paging Paging      `json:"paging"`
+	Items  []Complaint `json:"items"`
+}
diff --git a/mtypes/stored_messages.go b/mtypes/stored_messages.go
new file mode 100644
index 0000000..6f25105
--- /dev/null
+++ b/mtypes/stored_messages.go
@@ -0,0 +1,45 @@
+package mtypes
+
+// StoredMessage structures contain the (parsed) message content for an email
+// sent to a Mailgun account.
+//
+// The MessageHeaders field is special, in that it's formatted as a slice of pairs.
+// Each pair consists of a name [0] and value [1].  Array notation is used instead of a map
+// because that's how it's sent over the wire, and it's how encoding/json expects this field
+// to be.
+type StoredMessage struct {
+	Recipients        string             `json:"recipients"`
+	Sender            string             `json:"sender"`
+	From              string             `json:"from"`
+	Subject           string             `json:"subject"`
+	BodyPlain         string             `json:"body-plain"`
+	StrippedText      string             `json:"stripped-text"`
+	StrippedSignature string             `json:"stripped-signature"`
+	BodyHtml          string             `json:"body-html"`
+	StrippedHtml      string             `json:"stripped-html"`
+	Attachments       []StoredAttachment `json:"attachments"`
+	MessageUrl        string             `json:"message-url"`
+	ContentIDMap      map[string]struct {
+		URL         string `json:"url"`
+		ContentType string `json:"content-type"`
+		Name        string `json:"name"`
+		Size        int64  `json:"size"`
+	} `json:"content-id-map"`
+	MessageHeaders [][]string `json:"message-headers"`
+}
+
+// StoredAttachment structures contain information on an attachment associated with a stored message.
+type StoredAttachment struct {
+	Size        int    `json:"size"`
+	URL         string `json:"url"`
+	Name        string `json:"name"`
+	ContentType string `json:"content-type"`
+}
+
+type StoredMessageRaw struct {
+	Recipients string `json:"recipients"`
+	Sender     string `json:"sender"`
+	From       string `json:"from"`
+	Subject    string `json:"subject"`
+	BodyMime   string `json:"body-mime"`
+}
diff --git a/mtypes/subaccounts.go b/mtypes/subaccounts.go
new file mode 100644
index 0000000..a240919
--- /dev/null
+++ b/mtypes/subaccounts.go
@@ -0,0 +1,17 @@
+package mtypes
+
+// A Subaccount structure holds information about a subaccount.
+type Subaccount struct {
+	ID     string `json:"id"`
+	Name   string `json:"name"`
+	Status string `json:"status"`
+}
+
+type SubaccountResponse struct {
+	Item Subaccount `json:"subaccount"`
+}
+
+type ListSubaccountsResponse struct {
+	Items []Subaccount `json:"subaccounts"`
+	Total int          `json:"total"`
+}
diff --git a/mtypes/tags.go b/mtypes/tags.go
new file mode 100644
index 0000000..fd37488
--- /dev/null
+++ b/mtypes/tags.go
@@ -0,0 +1,17 @@
+package mtypes
+
+import (
+	"time"
+)
+
+type Tag struct {
+	Value       string     `json:"tag"`
+	Description string     `json:"description"`
+	FirstSeen   *time.Time `json:"first-seen,omitempty"`
+	LastSeen    *time.Time `json:"last-seen,omitempty"`
+}
+
+type TagsResponse struct {
+	Items  []Tag  `json:"items"`
+	Paging Paging `json:"paging"`
+}
diff --git a/mtypes/template.go b/mtypes/template.go
new file mode 100644
index 0000000..aba326f
--- /dev/null
+++ b/mtypes/template.go
@@ -0,0 +1,26 @@
+package mtypes
+
+type TemplateEngine string
+
+// Used by CreateTemplate() and AddTemplateVersion() to specify the template engine
+const (
+	TemplateEngineHandlebars = TemplateEngine("handlebars")
+	TemplateEngineGo         = TemplateEngine("go")
+)
+
+type Template struct {
+	Name        string          `json:"name"`
+	Description string          `json:"description"`
+	CreatedAt   RFC2822Time     `json:"createdAt"`
+	Version     TemplateVersion `json:"version,omitempty"`
+}
+
+type TemplateResp struct {
+	Item    Template `json:"template"`
+	Message string   `json:"message"`
+}
+
+type ListTemplateResp struct {
+	Items  []Template `json:"items"`
+	Paging Paging     `json:"paging"`
+}
diff --git a/mtypes/template_versions.go b/mtypes/template_versions.go
new file mode 100644
index 0000000..fdc2a09
--- /dev/null
+++ b/mtypes/template_versions.go
@@ -0,0 +1,18 @@
+package mtypes
+
+type TemplateVersion struct {
+	Tag       string         `json:"tag"`
+	Template  string         `json:"template,omitempty"`
+	Engine    TemplateEngine `json:"engine"`
+	CreatedAt RFC2822Time    `json:"createdAt"`
+	Comment   string         `json:"comment"`
+	Active    bool           `json:"active"`
+}
+
+type TemplateVersionListResp struct {
+	Template struct {
+		Template
+		Versions []TemplateVersion `json:"versions,omitempty"`
+	} `json:"template"`
+	Paging Paging `json:"paging"`
+}
diff --git a/mtypes/unsubscribes.go b/mtypes/unsubscribes.go
new file mode 100644
index 0000000..5e6dd15
--- /dev/null
+++ b/mtypes/unsubscribes.go
@@ -0,0 +1,13 @@
+package mtypes
+
+type Unsubscribe struct {
+	CreatedAt RFC2822Time `json:"created_at,omitempty"`
+	Tags      []string    `json:"tags,omitempty"`
+	ID        string      `json:"id,omitempty"`
+	Address   string      `json:"address"`
+}
+
+type ListUnsubscribesResponse struct {
+	Paging Paging        `json:"paging"`
+	Items  []Unsubscribe `json:"items"`
+}
diff --git a/mtypes/validation.go b/mtypes/validation.go
new file mode 100644
index 0000000..fb4c2f5
--- /dev/null
+++ b/mtypes/validation.go
@@ -0,0 +1,43 @@
+package mtypes
+
+// ValidateEmailResponse records basic facts about a validated e-mail address.
+// See the ValidateEmail method and example for more details.
+type ValidateEmailResponse struct {
+	// Echoes the address provided.
+	Address string `json:"address"`
+
+	// Indicates whether Mailgun thinks the address is from a known
+	// disposable mailbox provider.
+	IsDisposableAddress bool `json:"is_disposable_address"`
+
+	// Indicates whether Mailgun thinks the address is an email distribution list.
+	IsRoleAddress bool `json:"is_role_address"`
+
+	// A list of potential reasons why a specific validation may be unsuccessful. (Available in the v4 response)
+	Reason []string `json:"reason"`
+
+	// Result
+	Result string `json:"result"`
+
+	// Risk assessment for the provided email.
+	Risk string `json:"risk"`
+
+	LastSeen int64 `json:"last_seen,omitempty"`
+
+	// Provides a simple recommendation in case the address is invalid or
+	// Mailgun thinks you might have a typo. May be empty, in which case
+	// Mailgun has no recommendation to give.
+	DidYouMean string `json:"did_you_mean,omitempty"`
+
+	// Engagement results are a macro-level view that explain an email recipient’s propensity to engage.
+	// https://documentation.mailgun.com/docs/inboxready/mailgun-validate/validate_engagement/
+	Engagement *EngagementData `json:"engagement,omitempty"`
+
+	RootAddress string `json:"root_address,omitempty"`
+}
+
+type EngagementData struct {
+	Engaging bool   `json:"engaging"`
+	IsBot    bool   `json:"is_bot"`
+	Behavior string `json:"behavior,omitempty"`
+}
diff --git a/mtypes/webhooks.go b/mtypes/webhooks.go
new file mode 100644
index 0000000..d1c7642
--- /dev/null
+++ b/mtypes/webhooks.go
@@ -0,0 +1,31 @@
+package mtypes
+
+import (
+	"github.com/mailgun/mailgun-go/v4/events"
+)
+
+type UrlOrUrls struct {
+	Urls []string `json:"urls"`
+	Url  string   `json:"url"`
+}
+
+type WebHooksListResponse struct {
+	Webhooks map[string]UrlOrUrls `json:"webhooks"`
+}
+
+type WebHookResponse struct {
+	Webhook UrlOrUrls `json:"webhook"`
+}
+
+// Signature represents the signature portion of the webhook POST body
+type Signature struct {
+	TimeStamp string `json:"timestamp"`
+	Token     string `json:"token"`
+	Signature string `json:"signature"`
+}
+
+// WebhookPayload represents the JSON payload provided when a Webhook is called by mailgun
+type WebhookPayload struct {
+	Signature Signature      `json:"signature"`
+	EventData events.RawJSON `json:"event-data"`
+}
diff --git a/parse.go b/parse.go
index 6d89994..09c8201 100644
--- a/parse.go
+++ b/parse.go
@@ -1,5 +1,7 @@
 package mailgun
 
+// TODO(v5): move to events package?
+
 import (
 	"fmt"
 	"reflect"
diff --git a/routes.go b/routes.go
index 4693f6e..677eb23 100644
--- a/routes.go
+++ b/routes.go
@@ -6,30 +6,9 @@ import (
 	"net/url"
 	"strconv"
 	"time"
-)
 
-// A Route structure contains information on a configured or to-be-configured route.
-// When creating a new route, the SDK only uses a subset of the fields of this structure.
-// In particular, CreatedAt and ID are meaningless in this context, and will be ignored.
-// Only Priority, Description, Expression, and Actions need be provided.
-type Route struct {
-	// The Priority field indicates how soon the route works relative to other configured routes.
-	// Routes of equal priority are consulted in chronological order.
-	Priority int `json:"priority,omitempty"`
-	// The Description field provides a human-readable description for the route.
-	// Mailgun ignores this field except to provide the description when viewing the Mailgun web control panel.
-	Description string `json:"description,omitempty"`
-	// The Expression field lets you specify a pattern to match incoming messages against.
-	Expression string `json:"expression,omitempty"`
-	// The Actions field contains strings specifying what to do
-	// with any message which matches the provided expression.
-	Actions []string `json:"actions,omitempty"`
-
-	// The CreatedAt field provides a time-stamp for when the route came into existence.
-	CreatedAt RFC2822Time `json:"created_at,omitempty"`
-	// ID field provides a unique identifier for this route.
-	Id string `json:"id,omitempty"`
-}
+	"github.com/mailgun/mailgun-go/v4/mtypes"
+)
 
 // ForwardedMessage represents the payload the server will get on match
 // You can use ExtractForwardRoute() to extract PostForm into the struct, or you can use only the struct and parse the form manually
@@ -94,17 +73,6 @@ func ExtractForwardedMessage(formValues url.Values) ForwardedMessage {
 	return forwardedMessage
 }
 
-type routesListResponse struct {
-	// is -1 if Next() or First() have not been called
-	TotalCount int     `json:"total_count"`
-	Items      []Route `json:"items"`
-}
-
-type createRouteResp struct {
-	Message string `json:"message"`
-	Route   `json:"route"`
-}
-
 // ListRoutes allows you to iterate through a list of routes returned by the API
 func (mg *MailgunImpl) ListRoutes(opts *ListOptions) *RoutesIterator {
 	var limit int
@@ -119,13 +87,13 @@ func (mg *MailgunImpl) ListRoutes(opts *ListOptions) *RoutesIterator {
 	return &RoutesIterator{
 		mg:                 mg,
 		url:                generateApiUrl(mg, 3, routesEndpoint),
-		routesListResponse: routesListResponse{TotalCount: -1},
+		RoutesListResponse: mtypes.RoutesListResponse{TotalCount: -1},
 		limit:              limit,
 	}
 }
 
 type RoutesIterator struct {
-	routesListResponse
+	mtypes.RoutesListResponse
 
 	limit  int
 	mg     Mailgun
@@ -147,7 +115,7 @@ func (ri *RoutesIterator) Offset() int {
 // Next retrieves the next page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error
-func (ri *RoutesIterator) Next(ctx context.Context, items *[]Route) bool {
+func (ri *RoutesIterator) Next(ctx context.Context, items *[]mtypes.Route) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -157,7 +125,7 @@ func (ri *RoutesIterator) Next(ctx context.Context, items *[]Route) bool {
 		return false
 	}
 
-	cpy := make([]Route, len(ri.Items))
+	cpy := make([]mtypes.Route, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	if len(ri.Items) == 0 {
@@ -170,7 +138,7 @@ func (ri *RoutesIterator) Next(ctx context.Context, items *[]Route) bool {
 // First retrieves the first page of items from the api. Returns false if there
 // was an error. It also sets the iterator object to the first page.
 // Use `.Err()` to retrieve the error.
-func (ri *RoutesIterator) First(ctx context.Context, items *[]Route) bool {
+func (ri *RoutesIterator) First(ctx context.Context, items *[]mtypes.Route) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -178,7 +146,7 @@ func (ri *RoutesIterator) First(ctx context.Context, items *[]Route) bool {
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Route, len(ri.Items))
+	cpy := make([]mtypes.Route, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	ri.offset = len(ri.Items)
@@ -189,7 +157,7 @@ func (ri *RoutesIterator) First(ctx context.Context, items *[]Route) bool {
 // Calling Last() is invalid unless you first call First() or Next()
 // Returns false if there was an error. It also sets the iterator object
 // to the last page. Use `.Err()` to retrieve the error.
-func (ri *RoutesIterator) Last(ctx context.Context, items *[]Route) bool {
+func (ri *RoutesIterator) Last(ctx context.Context, items *[]mtypes.Route) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -207,7 +175,7 @@ func (ri *RoutesIterator) Last(ctx context.Context, items *[]Route) bool {
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Route, len(ri.Items))
+	cpy := make([]mtypes.Route, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	return true
@@ -216,7 +184,7 @@ func (ri *RoutesIterator) Last(ctx context.Context, items *[]Route) bool {
 // Previous retrieves the previous page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error if any
-func (ri *RoutesIterator) Previous(ctx context.Context, items *[]Route) bool {
+func (ri *RoutesIterator) Previous(ctx context.Context, items *[]mtypes.Route) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -234,7 +202,7 @@ func (ri *RoutesIterator) Previous(ctx context.Context, items *[]Route) bool {
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Route, len(ri.Items))
+	cpy := make([]mtypes.Route, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 
@@ -254,14 +222,14 @@ func (ri *RoutesIterator) fetch(ctx context.Context, skip, limit int) error {
 		r.addParameter("limit", strconv.Itoa(limit))
 	}
 
-	return getResponseFromJSON(ctx, r, &ri.routesListResponse)
+	return getResponseFromJSON(ctx, r, &ri.RoutesListResponse)
 }
 
 // CreateRoute installs a new route for your domain.
 // The route structure you provide serves as a template, and
 // only a subset of the fields influence the operation.
 // See the Route structure definition for more details.
-func (mg *MailgunImpl) CreateRoute(ctx context.Context, prototype Route) (_ignored Route, err error) {
+func (mg *MailgunImpl) CreateRoute(ctx context.Context, prototype mtypes.Route) (_ignored mtypes.Route, err error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, routesEndpoint))
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
@@ -272,7 +240,7 @@ func (mg *MailgunImpl) CreateRoute(ctx context.Context, prototype Route) (_ignor
 	for _, action := range prototype.Actions {
 		p.addValue("action", action)
 	}
-	var resp createRouteResp
+	var resp mtypes.CreateRouteResp
 	if err := postResponseFromJSON(ctx, r, p, &resp); err != nil {
 		return _ignored, err
 	}
@@ -292,17 +260,17 @@ func (mg *MailgunImpl) DeleteRoute(ctx context.Context, id string) error {
 }
 
 // GetRoute retrieves the complete route definition associated with the unique route ID.
-func (mg *MailgunImpl) GetRoute(ctx context.Context, id string) (Route, error) {
+func (mg *MailgunImpl) GetRoute(ctx context.Context, id string) (mtypes.Route, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, routesEndpoint) + "/" + id)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 	var envelope struct {
-		Message string `json:"message"`
-		*Route  `json:"route"`
+		Message       string `json:"message"`
+		*mtypes.Route `json:"route"`
 	}
 	err := getResponseFromJSON(ctx, r, &envelope)
 	if err != nil {
-		return Route{}, err
+		return mtypes.Route{}, err
 	}
 
 	return *envelope.Route, err
@@ -311,7 +279,7 @@ func (mg *MailgunImpl) GetRoute(ctx context.Context, id string) (Route, error) {
 // UpdateRoute provides an "in-place" update of the specified route.
 // Only those route fields which are non-zero or non-empty are updated.
 // All other fields remain as-is.
-func (mg *MailgunImpl) UpdateRoute(ctx context.Context, id string, route Route) (Route, error) {
+func (mg *MailgunImpl) UpdateRoute(ctx context.Context, id string, route mtypes.Route) (mtypes.Route, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, routesEndpoint) + "/" + id)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
@@ -332,7 +300,7 @@ func (mg *MailgunImpl) UpdateRoute(ctx context.Context, id string, route Route)
 	}
 	// For some reason, this API function just returns a bare Route on success.
 	// Unsure why this is the case; it seems like it ought to be a bug.
-	var envelope Route
+	var envelope mtypes.Route
 	err := putResponseFromJSON(ctx, r, p, &envelope)
 	return envelope, err
 }
diff --git a/spam_complaints.go b/spam_complaints.go
index dcaf50b..0a8802b 100644
--- a/spam_complaints.go
+++ b/spam_complaints.go
@@ -3,25 +3,14 @@ package mailgun
 import (
 	"context"
 	"strconv"
+
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 const (
 	complaintsEndpoint = "complaints"
 )
 
-// Complaint structures track how many times one of your emails have been marked as spam.
-// the recipient thought your messages were not solicited.
-type Complaint struct {
-	Count     int         `json:"count"`
-	CreatedAt RFC2822Time `json:"created_at"`
-	Address   string      `json:"address"`
-}
-
-type complaintsResponse struct {
-	Paging Paging      `json:"paging"`
-	Items  []Complaint `json:"items"`
-}
-
 // ListComplaints returns a set of spam complaints registered against your domain.
 // Recipients of your messages can click on a link which sends feedback to Mailgun
 // indicating that the message they received is, to them, spam.
@@ -37,13 +26,13 @@ func (mg *MailgunImpl) ListComplaints(domain string, opts *ListOptions) *Complai
 	url, err := r.generateUrlWithParameters()
 	return &ComplaintsIterator{
 		mg:                 mg,
-		complaintsResponse: complaintsResponse{Paging: Paging{Next: url, First: url}},
+		ComplaintsResponse: mtypes.ComplaintsResponse{Paging: mtypes.Paging{Next: url, First: url}},
 		err:                err,
 	}
 }
 
 type ComplaintsIterator struct {
-	complaintsResponse
+	mtypes.ComplaintsResponse
 	mg  Mailgun
 	err error
 }
@@ -56,7 +45,7 @@ func (ci *ComplaintsIterator) Err() error {
 // Next retrieves the next page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error
-func (ci *ComplaintsIterator) Next(ctx context.Context, items *[]Complaint) bool {
+func (ci *ComplaintsIterator) Next(ctx context.Context, items *[]mtypes.Complaint) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -64,7 +53,7 @@ func (ci *ComplaintsIterator) Next(ctx context.Context, items *[]Complaint) bool
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Complaint, len(ci.Items))
+	cpy := make([]mtypes.Complaint, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 
@@ -74,7 +63,7 @@ func (ci *ComplaintsIterator) Next(ctx context.Context, items *[]Complaint) bool
 // First retrieves the first page of items from the api. Returns false if there
 // was an error. It also sets the iterator object to the first page.
 // Use `.Err()` to retrieve the error.
-func (ci *ComplaintsIterator) First(ctx context.Context, items *[]Complaint) bool {
+func (ci *ComplaintsIterator) First(ctx context.Context, items *[]mtypes.Complaint) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -82,7 +71,7 @@ func (ci *ComplaintsIterator) First(ctx context.Context, items *[]Complaint) boo
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Complaint, len(ci.Items))
+	cpy := make([]mtypes.Complaint, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 	return true
@@ -92,7 +81,7 @@ func (ci *ComplaintsIterator) First(ctx context.Context, items *[]Complaint) boo
 // Calling Last() is invalid unless you first call First() or Next()
 // Returns false if there was an error. It also sets the iterator object
 // to the last page. Use `.Err()` to retrieve the error.
-func (ci *ComplaintsIterator) Last(ctx context.Context, items *[]Complaint) bool {
+func (ci *ComplaintsIterator) Last(ctx context.Context, items *[]mtypes.Complaint) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -100,7 +89,7 @@ func (ci *ComplaintsIterator) Last(ctx context.Context, items *[]Complaint) bool
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Complaint, len(ci.Items))
+	cpy := make([]mtypes.Complaint, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 	return true
@@ -109,7 +98,7 @@ func (ci *ComplaintsIterator) Last(ctx context.Context, items *[]Complaint) bool
 // Previous retrieves the previous page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error if any
-func (ci *ComplaintsIterator) Previous(ctx context.Context, items *[]Complaint) bool {
+func (ci *ComplaintsIterator) Previous(ctx context.Context, items *[]mtypes.Complaint) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -120,7 +109,7 @@ func (ci *ComplaintsIterator) Previous(ctx context.Context, items *[]Complaint)
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Complaint, len(ci.Items))
+	cpy := make([]mtypes.Complaint, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 
@@ -133,17 +122,17 @@ func (ci *ComplaintsIterator) fetch(ctx context.Context, url string) error {
 	r.setClient(ci.mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, ci.mg.APIKey())
 
-	return getResponseFromJSON(ctx, r, &ci.complaintsResponse)
+	return getResponseFromJSON(ctx, r, &ci.ComplaintsResponse)
 }
 
 // GetComplaint returns a single complaint record filed by a recipient at the email address provided.
 // If no complaint exists, the Complaint instance returned will be empty.
-func (mg *MailgunImpl) GetComplaint(ctx context.Context, domain, address string) (Complaint, error) {
+func (mg *MailgunImpl) GetComplaint(ctx context.Context, domain, address string) (mtypes.Complaint, error) {
 	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, complaintsEndpoint, domain) + "/" + address)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	var c Complaint
+	var c mtypes.Complaint
 	err := getResponseFromJSON(ctx, r, &c)
 	return c, err
 }
diff --git a/stored_messages.go b/stored_messages.go
index 46be347..588c299 100644
--- a/stored_messages.go
+++ b/stored_messages.go
@@ -3,65 +3,24 @@ package mailgun
 import (
 	"context"
 	"errors"
-)
-
-// StoredMessage structures contain the (parsed) message content for an email
-// sent to a Mailgun account.
-//
-// The MessageHeaders field is special, in that it's formatted as a slice of pairs.
-// Each pair consists of a name [0] and value [1].  Array notation is used instead of a map
-// because that's how it's sent over the wire, and it's how encoding/json expects this field
-// to be.
-type StoredMessage struct {
-	Recipients        string             `json:"recipients"`
-	Sender            string             `json:"sender"`
-	From              string             `json:"from"`
-	Subject           string             `json:"subject"`
-	BodyPlain         string             `json:"body-plain"`
-	StrippedText      string             `json:"stripped-text"`
-	StrippedSignature string             `json:"stripped-signature"`
-	BodyHtml          string             `json:"body-html"`
-	StrippedHtml      string             `json:"stripped-html"`
-	Attachments       []StoredAttachment `json:"attachments"`
-	MessageUrl        string             `json:"message-url"`
-	ContentIDMap      map[string]struct {
-		Url         string `json:"url"`
-		ContentType string `json:"content-type"`
-		Name        string `json:"name"`
-		Size        int64  `json:"size"`
-	} `json:"content-id-map"`
-	MessageHeaders [][]string `json:"message-headers"`
-}
 
-// StoredAttachment structures contain information on an attachment associated with a stored message.
-type StoredAttachment struct {
-	Size        int    `json:"size"`
-	Url         string `json:"url"`
-	Name        string `json:"name"`
-	ContentType string `json:"content-type"`
-}
-
-type StoredMessageRaw struct {
-	Recipients string `json:"recipients"`
-	Sender     string `json:"sender"`
-	From       string `json:"from"`
-	Subject    string `json:"subject"`
-	BodyMime   string `json:"body-mime"`
-}
+	"github.com/mailgun/mailgun-go/v4/mtypes"
+)
 
 // GetStoredMessage retrieves information about a received e-mail message.
 // This provides visibility into, e.g., replies to a message sent to a mailing list.
-func (mg *MailgunImpl) GetStoredMessage(ctx context.Context, url string) (StoredMessage, error) {
+func (mg *MailgunImpl) GetStoredMessage(ctx context.Context, url string) (mtypes.StoredMessage, error) {
 	r := newHTTPRequest(url)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	var response StoredMessage
+	var response mtypes.StoredMessage
 	err := getResponseFromJSON(ctx, r, &response)
 	return response, err
 }
 
-// Given a storage id resend the stored message to the specified recipients
+// ReSend given a storage id resend the stored message to the specified recipients
+// TODO(v5): return SendMessageResponse
 func (mg *MailgunImpl) ReSend(ctx context.Context, url string, recipients ...string) (msg, id string, err error) {
 	r := newHTTPRequest(url)
 	r.setClient(mg.HTTPClient())
@@ -83,19 +42,19 @@ func (mg *MailgunImpl) ReSend(ctx context.Context, url string, recipients ...str
 		return "", "", err
 	}
 
-	return resp.Message, resp.Id, nil
+	return resp.Message, resp.ID, nil
 }
 
 // GetStoredMessageRaw retrieves the raw MIME body of a received e-mail message.
 // Compared to GetStoredMessage, it gives access to the unparsed MIME body, and
 // thus delegates to the caller the required parsing.
-func (mg *MailgunImpl) GetStoredMessageRaw(ctx context.Context, url string) (StoredMessageRaw, error) {
+func (mg *MailgunImpl) GetStoredMessageRaw(ctx context.Context, url string) (mtypes.StoredMessageRaw, error) {
 	r := newHTTPRequest(url)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 	r.addHeader("Accept", "message/rfc2822")
 
-	var response StoredMessageRaw
+	var response mtypes.StoredMessageRaw
 	err := getResponseFromJSON(ctx, r, &response)
 	return response, err
 }
diff --git a/subaccounts.go b/subaccounts.go
index 6a45dd4..62fd304 100644
--- a/subaccounts.go
+++ b/subaccounts.go
@@ -4,6 +4,8 @@ import (
 	"context"
 	"fmt"
 	"strconv"
+
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 type ListSubaccountsOptions struct {
@@ -14,7 +16,7 @@ type ListSubaccountsOptions struct {
 }
 
 type SubaccountsIterator struct {
-	subaccountsListResponse
+	mtypes.ListSubaccountsResponse
 
 	mg        Mailgun
 	limit     int
@@ -26,22 +28,6 @@ type SubaccountsIterator struct {
 	err       error
 }
 
-// A Subaccount structure holds information about a subaccount.
-type Subaccount struct {
-	Id     string `json:"id"`
-	Name   string `json:"name"`
-	Status string `json:"status"`
-}
-
-type SubaccountResponse struct {
-	Item Subaccount `json:"subaccount"`
-}
-
-type subaccountsListResponse struct {
-	Items []Subaccount `json:"subaccounts"`
-	Total int          `json:"total"`
-}
-
 // ListSubaccounts retrieves a set of subaccount linked to the primary Mailgun account.
 func (mg *MailgunImpl) ListSubaccounts(opts *ListSubaccountsOptions) *SubaccountsIterator {
 	r := newHTTPRequest(generateSubaccountsApiUrl(mg))
@@ -64,7 +50,7 @@ func (mg *MailgunImpl) ListSubaccounts(opts *ListSubaccountsOptions) *Subaccount
 	return &SubaccountsIterator{
 		mg:                      mg,
 		url:                     generateSubaccountsApiUrl(mg),
-		subaccountsListResponse: subaccountsListResponse{Total: -1},
+		ListSubaccountsResponse: mtypes.ListSubaccountsResponse{Total: -1},
 		limit:                   limit,
 		skip:                    skip,
 		sortArray:               sortArray,
@@ -85,7 +71,7 @@ func (ri *SubaccountsIterator) Offset() int {
 // Next retrieves the next page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error
-func (ri *SubaccountsIterator) Next(ctx context.Context, items *[]Subaccount) bool {
+func (ri *SubaccountsIterator) Next(ctx context.Context, items *[]mtypes.Subaccount) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -95,7 +81,7 @@ func (ri *SubaccountsIterator) Next(ctx context.Context, items *[]Subaccount) bo
 		return false
 	}
 
-	cpy := make([]Subaccount, len(ri.Items))
+	cpy := make([]mtypes.Subaccount, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	if len(ri.Items) == 0 {
@@ -108,7 +94,7 @@ func (ri *SubaccountsIterator) Next(ctx context.Context, items *[]Subaccount) bo
 // First retrieves the first page of items from the api. Returns false if there
 // was an error. It also sets the iterator object to the first page.
 // Use `.Err()` to retrieve the error.
-func (ri *SubaccountsIterator) First(ctx context.Context, items *[]Subaccount) bool {
+func (ri *SubaccountsIterator) First(ctx context.Context, items *[]mtypes.Subaccount) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -116,7 +102,7 @@ func (ri *SubaccountsIterator) First(ctx context.Context, items *[]Subaccount) b
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Subaccount, len(ri.Items))
+	cpy := make([]mtypes.Subaccount, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	ri.offset = len(ri.Items)
@@ -127,7 +113,7 @@ func (ri *SubaccountsIterator) First(ctx context.Context, items *[]Subaccount) b
 // Calling Last() is invalid unless you first call First() or Next()
 // Returns false if there was an error. It also sets the iterator object
 // to the last page. Use `.Err()` to retrieve the error.
-func (ri *SubaccountsIterator) Last(ctx context.Context, items *[]Subaccount) bool {
+func (ri *SubaccountsIterator) Last(ctx context.Context, items *[]mtypes.Subaccount) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -145,7 +131,7 @@ func (ri *SubaccountsIterator) Last(ctx context.Context, items *[]Subaccount) bo
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Subaccount, len(ri.Items))
+	cpy := make([]mtypes.Subaccount, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 	return true
@@ -154,7 +140,7 @@ func (ri *SubaccountsIterator) Last(ctx context.Context, items *[]Subaccount) bo
 // Previous retrieves the previous page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error if any
-func (ri *SubaccountsIterator) Previous(ctx context.Context, items *[]Subaccount) bool {
+func (ri *SubaccountsIterator) Previous(ctx context.Context, items *[]mtypes.Subaccount) bool {
 	if ri.err != nil {
 		return false
 	}
@@ -172,7 +158,7 @@ func (ri *SubaccountsIterator) Previous(ctx context.Context, items *[]Subaccount
 	if ri.err != nil {
 		return false
 	}
-	cpy := make([]Subaccount, len(ri.Items))
+	cpy := make([]mtypes.Subaccount, len(ri.Items))
 	copy(cpy, ri.Items)
 	*items = cpy
 
@@ -192,54 +178,54 @@ func (ri *SubaccountsIterator) fetch(ctx context.Context, skip, limit int) error
 		r.addParameter("limit", strconv.Itoa(limit))
 	}
 
-	return getResponseFromJSON(ctx, r, &ri.subaccountsListResponse)
+	return getResponseFromJSON(ctx, r, &ri.ListSubaccountsResponse)
 }
 
 // CreateSubaccount instructs Mailgun to create a new account (Subaccount) that is linked to the primary account.
 // Subaccounts are child accounts that share the same plan and usage allocations as the primary, but have their own
 // assets (sending domains, unique users, API key, SMTP credentials, settings, statistics and site login).
 // All you need is the name of the subaccount.
-func (mg *MailgunImpl) CreateSubaccount(ctx context.Context, subaccountName string) (SubaccountResponse, error) {
+func (mg *MailgunImpl) CreateSubaccount(ctx context.Context, subaccountName string) (mtypes.SubaccountResponse, error) {
 	r := newHTTPRequest(generateSubaccountsApiUrl(mg))
 	r.setClient(mg.client)
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
 	payload := newUrlEncodedPayload()
 	payload.addValue("name", subaccountName)
-	resp := SubaccountResponse{}
+	resp := mtypes.SubaccountResponse{}
 	err := postResponseFromJSON(ctx, r, payload, &resp)
 	return resp, err
 }
 
 // GetSubaccount retrieves detailed information about subaccount using subaccountID.
-func (mg *MailgunImpl) GetSubaccount(ctx context.Context, subaccountID string) (SubaccountResponse, error) {
+func (mg *MailgunImpl) GetSubaccount(ctx context.Context, subaccountID string) (mtypes.SubaccountResponse, error) {
 	r := newHTTPRequest(generateSubaccountsApiUrl(mg) + "/" + subaccountID)
 	r.setClient(mg.client)
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	var resp SubaccountResponse
+	var resp mtypes.SubaccountResponse
 	err := getResponseFromJSON(ctx, r, &resp)
 	return resp, err
 }
 
 // EnableSubaccount instructs Mailgun to enable subaccount.
-func (mg *MailgunImpl) EnableSubaccount(ctx context.Context, subaccountId string) (SubaccountResponse, error) {
+func (mg *MailgunImpl) EnableSubaccount(ctx context.Context, subaccountId string) (mtypes.SubaccountResponse, error) {
 	r := newHTTPRequest(generateSubaccountsApiUrl(mg) + "/" + subaccountId + "/" + "enable")
 	r.setClient(mg.client)
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	resp := SubaccountResponse{}
+	resp := mtypes.SubaccountResponse{}
 	err := postResponseFromJSON(ctx, r, nil, &resp)
 	return resp, err
 }
 
 // DisableSubaccount instructs Mailgun to disable subaccount.
-func (mg *MailgunImpl) DisableSubaccount(ctx context.Context, subaccountId string) (SubaccountResponse, error) {
+func (mg *MailgunImpl) DisableSubaccount(ctx context.Context, subaccountId string) (mtypes.SubaccountResponse, error) {
 	r := newHTTPRequest(generateSubaccountsApiUrl(mg) + "/" + subaccountId + "/" + "disable")
 	r.setClient(mg.client)
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	resp := SubaccountResponse{}
+	resp := mtypes.SubaccountResponse{}
 	err := postResponseFromJSON(ctx, r, nil, &resp)
 	return resp, err
 }
diff --git a/tags.go b/tags.go
index f21e922..5e7cbef 100644
--- a/tags.go
+++ b/tags.go
@@ -4,20 +4,9 @@ import (
 	"context"
 	"net/url"
 	"strconv"
-	"time"
-)
-
-type Tag struct {
-	Value       string     `json:"tag"`
-	Description string     `json:"description"`
-	FirstSeen   *time.Time `json:"first-seen,omitempty"`
-	LastSeen    *time.Time `json:"last-seen,omitempty"`
-}
 
-type tagsResponse struct {
-	Items  []Tag  `json:"items"`
-	Paging Paging `json:"paging"`
-}
+	"github.com/mailgun/mailgun-go/v4/mtypes"
+)
 
 type ListTagOptions struct {
 	// Restrict the page size to this limit
@@ -36,11 +25,11 @@ func (mg *MailgunImpl) DeleteTag(ctx context.Context, domain, tag string) error
 }
 
 // GetTag retrieves metadata about the tag from the api
-func (mg *MailgunImpl) GetTag(ctx context.Context, domain, tag string) (Tag, error) {
+func (mg *MailgunImpl) GetTag(ctx context.Context, domain, tag string) (mtypes.Tag, error) {
 	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, tagsEndpoint, domain) + "/" + tag)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
-	var tagItem Tag
+	var tagItem mtypes.Tag
 	err := getResponseFromJSON(ctx, r, &tagItem)
 	return tagItem, err
 }
@@ -70,20 +59,20 @@ func (mg *MailgunImpl) ListTags(domain string, opts *ListTagOptions) *TagIterato
 
 	uri, err := req.generateUrlWithParameters()
 	return &TagIterator{
-		tagsResponse: tagsResponse{Paging: Paging{Next: uri, First: uri}},
+		TagsResponse: mtypes.TagsResponse{Paging: mtypes.Paging{Next: uri, First: uri}},
 		err:          err,
 		mg:           mg,
 	}
 }
 
 type TagIterator struct {
-	tagsResponse
+	mtypes.TagsResponse
 	mg  Mailgun
 	err error
 }
 
 // Next returns the next page in the list of tags
-func (ti *TagIterator) Next(ctx context.Context, items *[]Tag) bool {
+func (ti *TagIterator) Next(ctx context.Context, items *[]mtypes.Tag) bool {
 	if ti.err != nil {
 		return false
 	}
@@ -102,7 +91,7 @@ func (ti *TagIterator) Next(ctx context.Context, items *[]Tag) bool {
 }
 
 // Previous returns the previous page in the list of tags
-func (ti *TagIterator) Previous(ctx context.Context, items *[]Tag) bool {
+func (ti *TagIterator) Previous(ctx context.Context, items *[]mtypes.Tag) bool {
 	if ti.err != nil {
 		return false
 	}
@@ -125,7 +114,7 @@ func (ti *TagIterator) Previous(ctx context.Context, items *[]Tag) bool {
 }
 
 // First returns the first page in the list of tags
-func (ti *TagIterator) First(ctx context.Context, items *[]Tag) bool {
+func (ti *TagIterator) First(ctx context.Context, items *[]mtypes.Tag) bool {
 	if ti.err != nil {
 		return false
 	}
@@ -138,7 +127,7 @@ func (ti *TagIterator) First(ctx context.Context, items *[]Tag) bool {
 }
 
 // Last returns the last page in the list of tags
-func (ti *TagIterator) Last(ctx context.Context, items *[]Tag) bool {
+func (ti *TagIterator) Last(ctx context.Context, items *[]mtypes.Tag) bool {
 	if ti.err != nil {
 		return false
 	}
@@ -160,7 +149,7 @@ func (ti *TagIterator) fetch(ctx context.Context, uri string) error {
 	req := newHTTPRequest(uri)
 	req.setClient(ti.mg.HTTPClient())
 	req.setBasicAuth(basicAuthUser, ti.mg.APIKey())
-	return getResponseFromJSON(ctx, req, &ti.tagsResponse)
+	return getResponseFromJSON(ctx, req, &ti.TagsResponse)
 }
 
 func canFetchPage(slug string) bool {
diff --git a/template.go b/template.go
index 8951bea..09ae09b 100644
--- a/template.go
+++ b/template.go
@@ -4,35 +4,12 @@ import (
 	"context"
 	"errors"
 	"strconv"
-)
-
-type TemplateEngine string
 
-// Used by CreateTemplate() and AddTemplateVersion() to specify the template engine
-const (
-	TemplateEngineHandlebars = TemplateEngine("handlebars")
-	TemplateEngineGo         = TemplateEngine("go")
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
-type Template struct {
-	Name        string          `json:"name"`
-	Description string          `json:"description"`
-	CreatedAt   RFC2822Time     `json:"createdAt"`
-	Version     TemplateVersion `json:"version,omitempty"`
-}
-
-type templateResp struct {
-	Item    Template `json:"template"`
-	Message string   `json:"message"`
-}
-
-type templateListResp struct {
-	Items  []Template `json:"items"`
-	Paging Paging     `json:"paging"`
-}
-
 // Create a new template which can be used to attach template versions to
-func (mg *MailgunImpl) CreateTemplate(ctx context.Context, domain string, template *Template) error {
+func (mg *MailgunImpl) CreateTemplate(ctx context.Context, domain string, template *mtypes.Template) error {
 	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, templatesEndpoint, domain))
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
@@ -59,7 +36,7 @@ func (mg *MailgunImpl) CreateTemplate(ctx context.Context, domain string, templa
 		payload.addValue("tag", template.Version.Tag)
 	}
 
-	var resp templateResp
+	var resp mtypes.TemplateResp
 	if err := postResponseFromJSON(ctx, r, payload, &resp); err != nil {
 		return err
 	}
@@ -68,22 +45,22 @@ func (mg *MailgunImpl) CreateTemplate(ctx context.Context, domain string, templa
 }
 
 // GetTemplate gets a template given the template name
-func (mg *MailgunImpl) GetTemplate(ctx context.Context, domain, name string) (Template, error) {
+func (mg *MailgunImpl) GetTemplate(ctx context.Context, domain, name string) (mtypes.Template, error) {
 	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, templatesEndpoint, domain) + "/" + name)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 	r.addParameter("active", "yes")
 
-	var resp templateResp
+	var resp mtypes.TemplateResp
 	err := getResponseFromJSON(ctx, r, &resp)
 	if err != nil {
-		return Template{}, err
+		return mtypes.Template{}, err
 	}
 	return resp.Item, nil
 }
 
 // Update the name and description of a template
-func (mg *MailgunImpl) UpdateTemplate(ctx context.Context, domain string, template *Template) error {
+func (mg *MailgunImpl) UpdateTemplate(ctx context.Context, domain string, template *mtypes.Template) error {
 	if template.Name == "" {
 		return errors.New("UpdateTemplate() Template.Name cannot be empty")
 	}
@@ -100,7 +77,7 @@ func (mg *MailgunImpl) UpdateTemplate(ctx context.Context, domain string, templa
 		p.addValue("description", template.Description)
 	}
 
-	var resp templateResp
+	var resp mtypes.TemplateResp
 	err := putResponseFromJSON(ctx, r, p, &resp)
 	if err != nil {
 		return err
@@ -119,7 +96,7 @@ func (mg *MailgunImpl) DeleteTemplate(ctx context.Context, domain, name string)
 }
 
 type TemplatesIterator struct {
-	templateListResp
+	mtypes.ListTemplateResp
 	mg  Mailgun
 	err error
 }
@@ -145,7 +122,7 @@ func (mg *MailgunImpl) ListTemplates(domain string, opts *ListTemplateOptions) *
 	url, err := r.generateUrlWithParameters()
 	return &TemplatesIterator{
 		mg:               mg,
-		templateListResp: templateListResp{Paging: Paging{Next: url, First: url}},
+		ListTemplateResp: mtypes.ListTemplateResp{Paging: mtypes.Paging{Next: url, First: url}},
 		err:              err,
 	}
 }
@@ -158,7 +135,7 @@ func (ti *TemplatesIterator) Err() error {
 // Next retrieves the next page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error
-func (ti *TemplatesIterator) Next(ctx context.Context, items *[]Template) bool {
+func (ti *TemplatesIterator) Next(ctx context.Context, items *[]mtypes.Template) bool {
 	if ti.err != nil {
 		return false
 	}
@@ -166,7 +143,7 @@ func (ti *TemplatesIterator) Next(ctx context.Context, items *[]Template) bool {
 	if ti.err != nil {
 		return false
 	}
-	cpy := make([]Template, len(ti.Items))
+	cpy := make([]mtypes.Template, len(ti.Items))
 	copy(cpy, ti.Items)
 	*items = cpy
 
@@ -176,7 +153,7 @@ func (ti *TemplatesIterator) Next(ctx context.Context, items *[]Template) bool {
 // First retrieves the first page of items from the api. Returns false if there
 // was an error. It also sets the iterator object to the first page.
 // Use `.Err()` to retrieve the error.
-func (ti *TemplatesIterator) First(ctx context.Context, items *[]Template) bool {
+func (ti *TemplatesIterator) First(ctx context.Context, items *[]mtypes.Template) bool {
 	if ti.err != nil {
 		return false
 	}
@@ -184,7 +161,7 @@ func (ti *TemplatesIterator) First(ctx context.Context, items *[]Template) bool
 	if ti.err != nil {
 		return false
 	}
-	cpy := make([]Template, len(ti.Items))
+	cpy := make([]mtypes.Template, len(ti.Items))
 	copy(cpy, ti.Items)
 	*items = cpy
 	return true
@@ -194,7 +171,7 @@ func (ti *TemplatesIterator) First(ctx context.Context, items *[]Template) bool
 // Calling Last() is invalid unless you first call First() or Next()
 // Returns false if there was an error. It also sets the iterator object
 // to the last page. Use `.Err()` to retrieve the error.
-func (ti *TemplatesIterator) Last(ctx context.Context, items *[]Template) bool {
+func (ti *TemplatesIterator) Last(ctx context.Context, items *[]mtypes.Template) bool {
 	if ti.err != nil {
 		return false
 	}
@@ -202,7 +179,7 @@ func (ti *TemplatesIterator) Last(ctx context.Context, items *[]Template) bool {
 	if ti.err != nil {
 		return false
 	}
-	cpy := make([]Template, len(ti.Items))
+	cpy := make([]mtypes.Template, len(ti.Items))
 	copy(cpy, ti.Items)
 	*items = cpy
 	return true
@@ -211,7 +188,7 @@ func (ti *TemplatesIterator) Last(ctx context.Context, items *[]Template) bool {
 // Previous retrieves the previous page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error if any
-func (ti *TemplatesIterator) Previous(ctx context.Context, items *[]Template) bool {
+func (ti *TemplatesIterator) Previous(ctx context.Context, items *[]mtypes.Template) bool {
 	if ti.err != nil {
 		return false
 	}
@@ -222,7 +199,7 @@ func (ti *TemplatesIterator) Previous(ctx context.Context, items *[]Template) bo
 	if ti.err != nil {
 		return false
 	}
-	cpy := make([]Template, len(ti.Items))
+	cpy := make([]mtypes.Template, len(ti.Items))
 	copy(cpy, ti.Items)
 	*items = cpy
 
@@ -235,5 +212,5 @@ func (ti *TemplatesIterator) fetch(ctx context.Context, url string) error {
 	r.setClient(ti.mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, ti.mg.APIKey())
 
-	return getResponseFromJSON(ctx, r, &ti.templateListResp)
+	return getResponseFromJSON(ctx, r, &ti.ListTemplateResp)
 }
diff --git a/template_versions.go b/template_versions.go
index 65b9a0b..31e472c 100644
--- a/template_versions.go
+++ b/template_versions.go
@@ -3,27 +3,12 @@ package mailgun
 import (
 	"context"
 	"strconv"
-)
-
-type TemplateVersion struct {
-	Tag       string         `json:"tag"`
-	Template  string         `json:"template,omitempty"`
-	Engine    TemplateEngine `json:"engine"`
-	CreatedAt RFC2822Time    `json:"createdAt"`
-	Comment   string         `json:"comment"`
-	Active    bool           `json:"active"`
-}
 
-type templateVersionListResp struct {
-	Template struct {
-		Template
-		Versions []TemplateVersion `json:"versions,omitempty"`
-	} `json:"template"`
-	Paging Paging `json:"paging"`
-}
+	"github.com/mailgun/mailgun-go/v4/mtypes"
+)
 
 // AddTemplateVersion adds a template version to a template
-func (mg *MailgunImpl) AddTemplateVersion(ctx context.Context, domain, templateName string, version *TemplateVersion) error {
+func (mg *MailgunImpl) AddTemplateVersion(ctx context.Context, domain, templateName string, version *mtypes.TemplateVersion) error {
 	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, templatesEndpoint, domain) + "/" + templateName + "/versions")
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
@@ -44,7 +29,7 @@ func (mg *MailgunImpl) AddTemplateVersion(ctx context.Context, domain, templateN
 		payload.addValue("active", boolToString(version.Active))
 	}
 
-	var resp templateResp
+	var resp mtypes.TemplateResp
 	if err := postResponseFromJSON(ctx, r, payload, &resp); err != nil {
 		return err
 	}
@@ -53,21 +38,21 @@ func (mg *MailgunImpl) AddTemplateVersion(ctx context.Context, domain, templateN
 }
 
 // GetTemplateVersion gets a specific version of a template
-func (mg *MailgunImpl) GetTemplateVersion(ctx context.Context, domain, templateName, tag string) (TemplateVersion, error) {
+func (mg *MailgunImpl) GetTemplateVersion(ctx context.Context, domain, templateName, tag string) (mtypes.TemplateVersion, error) {
 	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, templatesEndpoint, domain) + "/" + templateName + "/versions/" + tag)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	var resp templateResp
+	var resp mtypes.TemplateResp
 	err := getResponseFromJSON(ctx, r, &resp)
 	if err != nil {
-		return TemplateVersion{}, err
+		return mtypes.TemplateVersion{}, err
 	}
 	return resp.Item.Version, nil
 }
 
 // Update the comment and mark a version of a template active
-func (mg *MailgunImpl) UpdateTemplateVersion(ctx context.Context, domain, templateName string, version *TemplateVersion) error {
+func (mg *MailgunImpl) UpdateTemplateVersion(ctx context.Context, domain, templateName string, version *mtypes.TemplateVersion) error {
 	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, templatesEndpoint, domain) + "/" + templateName + "/versions/" + version.Tag)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
@@ -83,7 +68,7 @@ func (mg *MailgunImpl) UpdateTemplateVersion(ctx context.Context, domain, templa
 		p.addValue("template", version.Template)
 	}
 
-	var resp templateResp
+	var resp mtypes.TemplateResp
 	err := putResponseFromJSON(ctx, r, p, &resp)
 	if err != nil {
 		return err
@@ -102,7 +87,7 @@ func (mg *MailgunImpl) DeleteTemplateVersion(ctx context.Context, domain, templa
 }
 
 type TemplateVersionsIterator struct {
-	templateVersionListResp
+	mtypes.TemplateVersionListResp
 	mg  Mailgun
 	err error
 }
@@ -120,7 +105,7 @@ func (mg *MailgunImpl) ListTemplateVersions(domain, templateName string, opts *L
 	url, err := r.generateUrlWithParameters()
 	return &TemplateVersionsIterator{
 		mg:                      mg,
-		templateVersionListResp: templateVersionListResp{Paging: Paging{Next: url, First: url}},
+		TemplateVersionListResp: mtypes.TemplateVersionListResp{Paging: mtypes.Paging{Next: url, First: url}},
 		err:                     err,
 	}
 }
@@ -133,7 +118,7 @@ func (li *TemplateVersionsIterator) Err() error {
 // Next retrieves the next page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error
-func (li *TemplateVersionsIterator) Next(ctx context.Context, items *[]TemplateVersion) bool {
+func (li *TemplateVersionsIterator) Next(ctx context.Context, items *[]mtypes.TemplateVersion) bool {
 	if li.err != nil {
 		return false
 	}
@@ -141,7 +126,7 @@ func (li *TemplateVersionsIterator) Next(ctx context.Context, items *[]TemplateV
 	if li.err != nil {
 		return false
 	}
-	cpy := make([]TemplateVersion, len(li.Template.Versions))
+	cpy := make([]mtypes.TemplateVersion, len(li.Template.Versions))
 	copy(cpy, li.Template.Versions)
 	*items = cpy
 
@@ -151,7 +136,7 @@ func (li *TemplateVersionsIterator) Next(ctx context.Context, items *[]TemplateV
 // First retrieves the first page of items from the api. Returns false if there
 // was an error. It also sets the iterator object to the first page.
 // Use `.Err()` to retrieve the error.
-func (li *TemplateVersionsIterator) First(ctx context.Context, items *[]TemplateVersion) bool {
+func (li *TemplateVersionsIterator) First(ctx context.Context, items *[]mtypes.TemplateVersion) bool {
 	if li.err != nil {
 		return false
 	}
@@ -159,7 +144,7 @@ func (li *TemplateVersionsIterator) First(ctx context.Context, items *[]Template
 	if li.err != nil {
 		return false
 	}
-	cpy := make([]TemplateVersion, len(li.Template.Versions))
+	cpy := make([]mtypes.TemplateVersion, len(li.Template.Versions))
 	copy(cpy, li.Template.Versions)
 	*items = cpy
 	return true
@@ -169,7 +154,7 @@ func (li *TemplateVersionsIterator) First(ctx context.Context, items *[]Template
 // Calling Last() is invalid unless you first call First() or Next()
 // Returns false if there was an error. It also sets the iterator object
 // to the last page. Use `.Err()` to retrieve the error.
-func (li *TemplateVersionsIterator) Last(ctx context.Context, items *[]TemplateVersion) bool {
+func (li *TemplateVersionsIterator) Last(ctx context.Context, items *[]mtypes.TemplateVersion) bool {
 	if li.err != nil {
 		return false
 	}
@@ -177,7 +162,7 @@ func (li *TemplateVersionsIterator) Last(ctx context.Context, items *[]TemplateV
 	if li.err != nil {
 		return false
 	}
-	cpy := make([]TemplateVersion, len(li.Template.Versions))
+	cpy := make([]mtypes.TemplateVersion, len(li.Template.Versions))
 	copy(cpy, li.Template.Versions)
 	*items = cpy
 	return true
@@ -186,7 +171,7 @@ func (li *TemplateVersionsIterator) Last(ctx context.Context, items *[]TemplateV
 // Previous retrieves the previous page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error if any
-func (li *TemplateVersionsIterator) Previous(ctx context.Context, items *[]TemplateVersion) bool {
+func (li *TemplateVersionsIterator) Previous(ctx context.Context, items *[]mtypes.TemplateVersion) bool {
 	if li.err != nil {
 		return false
 	}
@@ -197,7 +182,7 @@ func (li *TemplateVersionsIterator) Previous(ctx context.Context, items *[]Templ
 	if li.err != nil {
 		return false
 	}
-	cpy := make([]TemplateVersion, len(li.Template.Versions))
+	cpy := make([]mtypes.TemplateVersion, len(li.Template.Versions))
 	copy(cpy, li.Template.Versions)
 	*items = cpy
 
@@ -210,5 +195,5 @@ func (li *TemplateVersionsIterator) fetch(ctx context.Context, url string) error
 	r.setClient(li.mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, li.mg.APIKey())
 
-	return getResponseFromJSON(ctx, r, &li.templateVersionListResp)
+	return getResponseFromJSON(ctx, r, &li.TemplateVersionListResp)
 }
diff --git a/unsubscribes.go b/unsubscribes.go
index d76b419..7148859 100644
--- a/unsubscribes.go
+++ b/unsubscribes.go
@@ -3,19 +3,9 @@ package mailgun
 import (
 	"context"
 	"strconv"
-)
-
-type Unsubscribe struct {
-	CreatedAt RFC2822Time `json:"created_at,omitempty"`
-	Tags      []string    `json:"tags,omitempty"`
-	ID        string      `json:"id,omitempty"`
-	Address   string      `json:"address"`
-}
 
-type unsubscribesResponse struct {
-	Paging Paging        `json:"paging"`
-	Items  []Unsubscribe `json:"items"`
-}
+	"github.com/mailgun/mailgun-go/v4/mtypes"
+)
 
 // Fetches the list of unsubscribes
 func (mg *MailgunImpl) ListUnsubscribes(domain string, opts *ListOptions) *UnsubscribesIterator {
@@ -29,14 +19,14 @@ func (mg *MailgunImpl) ListUnsubscribes(domain string, opts *ListOptions) *Unsub
 	}
 	url, err := r.generateUrlWithParameters()
 	return &UnsubscribesIterator{
-		mg:                   mg,
-		unsubscribesResponse: unsubscribesResponse{Paging: Paging{Next: url, First: url}},
-		err:                  err,
+		mg:                       mg,
+		ListUnsubscribesResponse: mtypes.ListUnsubscribesResponse{Paging: mtypes.Paging{Next: url, First: url}},
+		err:                      err,
 	}
 }
 
 type UnsubscribesIterator struct {
-	unsubscribesResponse
+	mtypes.ListUnsubscribesResponse
 	mg  Mailgun
 	err error
 }
@@ -49,7 +39,7 @@ func (ci *UnsubscribesIterator) Err() error {
 // Next retrieves the next page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error
-func (ci *UnsubscribesIterator) Next(ctx context.Context, items *[]Unsubscribe) bool {
+func (ci *UnsubscribesIterator) Next(ctx context.Context, items *[]mtypes.Unsubscribe) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -57,7 +47,7 @@ func (ci *UnsubscribesIterator) Next(ctx context.Context, items *[]Unsubscribe)
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Unsubscribe, len(ci.Items))
+	cpy := make([]mtypes.Unsubscribe, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 
@@ -67,7 +57,7 @@ func (ci *UnsubscribesIterator) Next(ctx context.Context, items *[]Unsubscribe)
 // First retrieves the first page of items from the api. Returns false if there
 // was an error. It also sets the iterator object to the first page.
 // Use `.Err()` to retrieve the error.
-func (ci *UnsubscribesIterator) First(ctx context.Context, items *[]Unsubscribe) bool {
+func (ci *UnsubscribesIterator) First(ctx context.Context, items *[]mtypes.Unsubscribe) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -75,7 +65,7 @@ func (ci *UnsubscribesIterator) First(ctx context.Context, items *[]Unsubscribe)
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Unsubscribe, len(ci.Items))
+	cpy := make([]mtypes.Unsubscribe, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 	return true
@@ -85,7 +75,7 @@ func (ci *UnsubscribesIterator) First(ctx context.Context, items *[]Unsubscribe)
 // Calling Last() is invalid unless you first call First() or Next()
 // Returns false if there was an error. It also sets the iterator object
 // to the last page. Use `.Err()` to retrieve the error.
-func (ci *UnsubscribesIterator) Last(ctx context.Context, items *[]Unsubscribe) bool {
+func (ci *UnsubscribesIterator) Last(ctx context.Context, items *[]mtypes.Unsubscribe) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -93,7 +83,7 @@ func (ci *UnsubscribesIterator) Last(ctx context.Context, items *[]Unsubscribe)
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Unsubscribe, len(ci.Items))
+	cpy := make([]mtypes.Unsubscribe, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 	return true
@@ -102,7 +92,7 @@ func (ci *UnsubscribesIterator) Last(ctx context.Context, items *[]Unsubscribe)
 // Previous retrieves the previous page of items from the api. Returns false when there
 // no more pages to retrieve or if there was an error. Use `.Err()` to retrieve
 // the error if any
-func (ci *UnsubscribesIterator) Previous(ctx context.Context, items *[]Unsubscribe) bool {
+func (ci *UnsubscribesIterator) Previous(ctx context.Context, items *[]mtypes.Unsubscribe) bool {
 	if ci.err != nil {
 		return false
 	}
@@ -113,7 +103,7 @@ func (ci *UnsubscribesIterator) Previous(ctx context.Context, items *[]Unsubscri
 	if ci.err != nil {
 		return false
 	}
-	cpy := make([]Unsubscribe, len(ci.Items))
+	cpy := make([]mtypes.Unsubscribe, len(ci.Items))
 	copy(cpy, ci.Items)
 	*items = cpy
 
@@ -126,16 +116,16 @@ func (ci *UnsubscribesIterator) fetch(ctx context.Context, url string) error {
 	r.setClient(ci.mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, ci.mg.APIKey())
 
-	return getResponseFromJSON(ctx, r, &ci.unsubscribesResponse)
+	return getResponseFromJSON(ctx, r, &ci.ListUnsubscribesResponse)
 }
 
 // Retreives a single unsubscribe record. Can be used to check if a given address is present in the list of unsubscribed users.
-func (mg *MailgunImpl) GetUnsubscribe(ctx context.Context, domain, address string) (Unsubscribe, error) {
+func (mg *MailgunImpl) GetUnsubscribe(ctx context.Context, domain, address string) (mtypes.Unsubscribe, error) {
 	r := newHTTPRequest(generateApiV3UrlWithTarget(mg, unsubscribesEndpoint, domain, address))
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	envelope := Unsubscribe{}
+	envelope := mtypes.Unsubscribe{}
 	err := getResponseFromJSON(ctx, r, &envelope)
 
 	return envelope, err
@@ -154,7 +144,7 @@ func (mg *MailgunImpl) CreateUnsubscribe(ctx context.Context, domain, address, t
 }
 
 // CreateUnsubscribes adds multiple e-mail addresses to the domain's unsubscription table.
-func (mg *MailgunImpl) CreateUnsubscribes(ctx context.Context, domain string, unsubscribes []Unsubscribe) error {
+func (mg *MailgunImpl) CreateUnsubscribes(ctx context.Context, domain string, unsubscribes []mtypes.Unsubscribe) error {
 	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, unsubscribesEndpoint, domain))
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
diff --git a/validation.go b/validation.go
index 1fedc36..3e7300e 100644
--- a/validation.go
+++ b/validation.go
@@ -3,54 +3,14 @@ package mailgun
 import (
 	"context"
 	"fmt"
-)
-
-// ValidateEmailResponse records basic facts about a validated e-mail address.
-// See the ValidateEmail method and example for more details.
-type ValidateEmailResponse struct {
-	// Echoes the address provided.
-	Address string `json:"address"`
-
-	// Indicates whether Mailgun thinks the address is from a known
-	// disposable mailbox provider.
-	IsDisposableAddress bool `json:"is_disposable_address"`
-
-	// Indicates whether Mailgun thinks the address is an email distribution list.
-	IsRoleAddress bool `json:"is_role_address"`
-
-	// A list of potential reasons why a specific validation may be unsuccessful. (Available in the v4 response)
-	Reason []string `json:"reason"`
-
-	// Result
-	Result string `json:"result"`
-
-	// Risk assessment for the provided email.
-	Risk string `json:"risk"`
 
-	LastSeen int64 `json:"last_seen,omitempty"`
-
-	// Provides a simple recommendation in case the address is invalid or
-	// Mailgun thinks you might have a typo. May be empty, in which case
-	// Mailgun has no recommendation to give.
-	DidYouMean string `json:"did_you_mean,omitempty"`
-
-	// Engagement results are a macro-level view that explain an email recipient’s propensity to engage.
-	// https://documentation.mailgun.com/docs/inboxready/mailgun-validate/validate_engagement/
-	Engagement *EngagementData `json:"engagement,omitempty"`
-
-	RootAddress string `json:"root_address,omitempty"`
-}
-
-type EngagementData struct {
-	Engaging bool   `json:"engaging"`
-	IsBot    bool   `json:"is_bot"`
-	Behavior string `json:"behavior,omitempty"`
-}
+	"github.com/mailgun/mailgun-go/v4/mtypes"
+)
 
 // ValidateEmail performs various checks on the email address provided to ensure it's correctly formatted.
 // It may also be used to break an email address into its sub-components.
 // https://documentation.mailgun.com/docs/inboxready/mailgun-validate/single-valid-ir/
-func (mg *MailgunImpl) ValidateEmail(ctx context.Context, email string, mailBoxVerify bool) (ValidateEmailResponse, error) {
+func (mg *MailgunImpl) ValidateEmail(ctx context.Context, email string, mailBoxVerify bool) (mtypes.ValidateEmailResponse, error) {
 	r := newHTTPRequest(fmt.Sprintf("%s/v4/address/validate", mg.APIBase()))
 	r.setClient(mg.HTTPClient())
 	r.addParameter("address", email)
@@ -59,10 +19,10 @@ func (mg *MailgunImpl) ValidateEmail(ctx context.Context, email string, mailBoxV
 	}
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	var res ValidateEmailResponse
+	var res mtypes.ValidateEmailResponse
 	err := getResponseFromJSON(ctx, r, &res)
 	if err != nil {
-		return ValidateEmailResponse{}, err
+		return mtypes.ValidateEmailResponse{}, err
 	}
 
 	return res, nil
diff --git a/webhooks.go b/webhooks.go
index 04212b3..68e58f1 100644
--- a/webhooks.go
+++ b/webhooks.go
@@ -11,22 +11,9 @@ import (
 	"fmt"
 	"io"
 
-	"github.com/mailgun/mailgun-go/v4/events"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
-type UrlOrUrls struct {
-	Urls []string `json:"urls"`
-	Url  string   `json:"url"`
-}
-
-type WebHooksListResponse struct {
-	Webhooks map[string]UrlOrUrls `json:"webhooks"`
-}
-
-type WebHookResponse struct {
-	Webhook UrlOrUrls `json:"webhook"`
-}
-
 // ListWebhooks returns the complete set of webhooks configured for your domain.
 // Note that a zero-length mapping is not an error.
 func (mg *MailgunImpl) ListWebhooks(ctx context.Context, domain string) (map[string][]string, error) {
@@ -34,7 +21,7 @@ func (mg *MailgunImpl) ListWebhooks(ctx context.Context, domain string) (map[str
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	var body WebHooksListResponse
+	var body mtypes.WebHooksListResponse
 	err := getResponseFromJSON(ctx, r, &body)
 	if err != nil {
 		return nil, err
@@ -80,7 +67,7 @@ func (mg *MailgunImpl) GetWebhook(ctx context.Context, domain, name string) ([]s
 	r := newHTTPRequest(generateV3DomainsApiUrl(mg, webhooksEndpoint, domain) + "/" + name)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
-	var body WebHookResponse
+	var body mtypes.WebHookResponse
 	if err := getResponseFromJSON(ctx, r, &body); err != nil {
 		return nil, err
 	}
@@ -107,21 +94,8 @@ func (mg *MailgunImpl) UpdateWebhook(ctx context.Context, domain, name string, u
 	return err
 }
 
-// Signature represents the signature portion of the webhook POST body
-type Signature struct {
-	TimeStamp string `json:"timestamp"`
-	Token     string `json:"token"`
-	Signature string `json:"signature"`
-}
-
-// WebhookPayload represents the JSON payload provided when a Webhook is called by mailgun
-type WebhookPayload struct {
-	Signature Signature      `json:"signature"`
-	EventData events.RawJSON `json:"event-data"`
-}
-
 // VerifyWebhookSignature - use this method to parse the webhook signature given as JSON in the webhook response
-func (mg *MailgunImpl) VerifyWebhookSignature(sig Signature) (verified bool, err error) {
+func (mg *MailgunImpl) VerifyWebhookSignature(sig mtypes.Signature) (verified bool, err error) {
 	webhookSigningKey := mg.WebhookSigningKey()
 	if webhookSigningKey == "" {
 		return false, fmt.Errorf("webhook signing key is not set")


Test patch:
diff --git a/analytics_test.go b/analytics_test.go
index dcb81d5..970600b 100644
--- a/analytics_test.go
+++ b/analytics_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -14,37 +15,37 @@ func TestListMetrics(t *testing.T) {
 	err := mg.SetAPIBase(server.URL())
 	require.NoError(t, err)
 
-	start, _ := mailgun.NewRFC2822Time("Tue, 24 Sep 2024 00:00:00 +0000")
-	end, _ := mailgun.NewRFC2822Time("Tue, 24 Oct 2024 00:00:00 +0000")
+	start, _ := mtypes.NewRFC2822Time("Tue, 24 Sep 2024 00:00:00 +0000")
+	end, _ := mtypes.NewRFC2822Time("Tue, 24 Oct 2024 00:00:00 +0000")
 
-	opts := mailgun.MetricsOptions{
+	opts := mtypes.MetricsRequest{
 		Start: start,
 		End:   end,
-		Pagination: mailgun.MetricsPagination{
+		Pagination: mtypes.MetricsPagination{
 			Limit: 10,
 		},
 	}
 	// filter by domain
-	opts.Filter.BoolGroupAnd = []mailgun.MetricsFilterPredicate{{
+	opts.Filter.BoolGroupAnd = []mtypes.MetricsFilterPredicate{{
 		Attribute:     "domain",
 		Comparator:    "=",
-		LabeledValues: []mailgun.MetricsLabeledValue{{Label: testDomain, Value: testDomain}},
+		LabeledValues: []mtypes.MetricsLabeledValue{{Label: testDomain, Value: testDomain}},
 	}}
 
-	wantResp := mailgun.MetricsResponse{
+	wantResp := mtypes.MetricsResponse{
 		Start:      start,
 		End:        end,
 		Resolution: "day",
 		Duration:   "30d",
 		Dimensions: []string{"time"},
-		Items: []mailgun.MetricsItem{
+		Items: []mtypes.MetricsItem{
 			{
-				Dimensions: []mailgun.MetricsDimension{{
+				Dimensions: []mtypes.MetricsDimension{{
 					Dimension:    "time",
 					Value:        "Tue, 24 Sep 2024 00:00:00 +0000",
 					DisplayValue: "Tue, 24 Sep 2024 00:00:00 +0000",
 				}},
-				Metrics: mailgun.Metrics{
+				Metrics: mtypes.Metrics{
 					SentCount:      ptr(uint64(4)),
 					DeliveredCount: ptr(uint64(3)),
 					OpenedCount:    ptr(uint64(2)),
@@ -52,7 +53,7 @@ func TestListMetrics(t *testing.T) {
 				},
 			},
 		},
-		Pagination: mailgun.MetricsPagination{
+		Pagination: mtypes.MetricsPagination{
 			Sort:  "",
 			Skip:  0,
 			Limit: 10,
@@ -63,7 +64,7 @@ func TestListMetrics(t *testing.T) {
 	it, err := mg.ListMetrics(opts)
 	require.NoError(t, err)
 
-	var page mailgun.MetricsResponse
+	var page mtypes.MetricsResponse
 	ctx := context.Background()
 	more := it.Next(ctx, &page)
 	require.Nil(t, it.Err())
diff --git a/attachments_test.go b/attachments_test.go
index 964f8d8..9df5fb3 100644
--- a/attachments_test.go
+++ b/attachments_test.go
@@ -42,7 +42,7 @@ func TestMultipleAttachments(t *testing.T) {
 	require.NoError(t, err)
 
 	id = strings.Trim(id, "<>")
-	t.Logf("New Email: %s Id: %s\n", msg, id)
+	t.Logf("New Email: %s ID: %s\n", msg, id)
 
 	e, err := findAcceptedMessage(mg, id)
 	require.NoError(t, err)
diff --git a/bounces_test.go b/bounces_test.go
index 9329f45..489b0d9 100644
--- a/bounces_test.go
+++ b/bounces_test.go
@@ -10,6 +10,7 @@ import (
 	"time"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/require"
 )
 
@@ -21,7 +22,7 @@ func TestGetBounces(t *testing.T) {
 	ctx := context.Background()
 	it := mg.ListBounces(testDomain, nil)
 
-	var page []mailgun.Bounce
+	var page []mtypes.Bounce
 	for it.Next(ctx, &page) {
 		for _, bounce := range page {
 			t.Logf("Bounce: %+v\n", bounce)
@@ -55,7 +56,7 @@ func TestAddDelBounces(t *testing.T) {
 
 	findBounce := func(address string) bool {
 		it := mg.ListBounces(testDomain, nil)
-		var page []mailgun.Bounce
+		var page []mtypes.Bounce
 		for it.Next(ctx, &page) {
 			require.True(t, len(page) != 0)
 			for _, bounce := range page {
@@ -115,7 +116,7 @@ func TestAddDelBounceList(t *testing.T) {
 
 	findBounce := func(address string) bool {
 		it := mg.ListBounces(testDomain, nil)
-		var page []mailgun.Bounce
+		var page []mtypes.Bounce
 		for it.Next(ctx, &page) {
 			require.True(t, len(page) != 0)
 			for _, bounce := range page {
@@ -131,13 +132,13 @@ func TestAddDelBounceList(t *testing.T) {
 		return false
 	}
 
-	createdAt, err := mailgun.NewRFC2822Time("Thu, 13 Oct 2011 18:02:00 +0000")
+	createdAt, err := mtypes.NewRFC2822Time("Thu, 13 Oct 2011 18:02:00 +0000")
 	if err != nil {
 		t.Fatalf("invalid time")
 	}
 
 	// Generate a list of bounces
-	bounces := []mailgun.Bounce{
+	bounces := []mtypes.Bounce{
 		{
 			Code:    "550",
 			Address: fmt.Sprintf("%s@%s", strings.ToLower(randomString(8, "bounce")), domain),
@@ -176,7 +177,7 @@ func TestAddDelBounceList(t *testing.T) {
 	require.NoError(t, err)
 
 	it := mg.ListBounces(testDomain, nil)
-	var page []mailgun.Bounce
+	var page []mtypes.Bounce
 	if it.Next(ctx, &page) {
 		t.Fatalf("Expected no item in the bounce list")
 	}
diff --git a/credentials_test.go b/credentials_test.go
index 8d81106..cb2ccac 100644
--- a/credentials_test.go
+++ b/credentials_test.go
@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/require"
 )
 
@@ -18,7 +19,7 @@ func TestGetCredentials(t *testing.T) {
 	ctx := context.Background()
 	it := mg.ListCredentials(testDomain, nil)
 
-	var page []mailgun.Credential
+	var page []mtypes.Credential
 	for it.Next(ctx, &page) {
 		t.Logf("Login\tCreated At\t\n")
 		for _, c := range page {
diff --git a/domains_test.go b/domains_test.go
index f0836bf..f51a2c8 100644
--- a/domains_test.go
+++ b/domains_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -24,7 +25,7 @@ func TestListDomains(t *testing.T) {
 	ctx := context.Background()
 
 	it := mg.ListDomains(nil)
-	var page []mailgun.Domain
+	var page []mtypes.Domain
 	for it.Next(ctx, &page) {
 		for _, d := range page {
 			t.Logf("TestListDomains: %#v\n", d)
@@ -43,7 +44,7 @@ func TestGetSingleDomain(t *testing.T) {
 	ctx := context.Background()
 
 	it := mg.ListDomains(nil)
-	var page []mailgun.Domain
+	var page []mtypes.Domain
 	require.True(t, it.Next(ctx, &page))
 	require.NoError(t, it.Err())
 
@@ -85,7 +86,7 @@ func TestAddUpdateDeleteDomain(t *testing.T) {
 
 	// First, we need to add the domain.
 	_, err = mg.CreateDomain(ctx, "mx.mailgun.test",
-		&mailgun.CreateDomainOptions{SpamAction: mailgun.SpamActionTag, Password: "supersecret", WebScheme: "http"})
+		&mailgun.CreateDomainOptions{SpamAction: mtypes.SpamActionTag, Password: "supersecret", WebScheme: "http"})
 	require.NoError(t, err)
 
 	// Then, we update it.
diff --git a/events_test.go b/events_test.go
index f89b389..b81d22e 100644
--- a/events_test.go
+++ b/events_test.go
@@ -93,7 +93,7 @@ func TestEventPoller(t *testing.T) {
 	msg, id, err := mg.Send(ctx, m)
 	require.NoError(t, err)
 
-	t.Logf("New Email: %s Id: %s\n", msg, id)
+	t.Logf("New Email: %s ID: %s\n", msg, id)
 
 	var accepted *events.Accepted
 	for e := range eventChan {
diff --git a/examples_test.go b/examples_test.go
index 1e296ab..02d5ce2 100644
--- a/examples_test.go
+++ b/examples_test.go
@@ -13,6 +13,7 @@ import (
 
 	"github.com/mailgun/mailgun-go/v4"
 	"github.com/mailgun/mailgun-go/v4/events"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 )
 
 func ExampleMailgunImpl_ValidateEmail() {
@@ -37,7 +38,7 @@ func ExampleMailgunImpl_UpdateMailingList() {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
 	defer cancel()
 
-	_, err := mg.UpdateMailingList(ctx, "joe-stat@example.com", mailgun.MailingList{
+	_, err := mg.UpdateMailingList(ctx, "joe-stat@example.com", mtypes.MailingList{
 		Name:        "Joe Stat",
 		Description: "Joe's status report list",
 	})
@@ -99,7 +100,7 @@ func ExampleMailgunImpl_ListRoutes() {
 	defer cancel()
 
 	it := mg.ListRoutes(nil)
-	var page []mailgun.Route
+	var page []mtypes.Route
 	for it.Next(ctx, &page) {
 		for _, r := range page {
 			log.Printf("Route pri=%d expr=%s desc=%s", r.Priority, r.Expression, r.Description)
@@ -119,7 +120,7 @@ func ExampleMailgunImpl_VerifyWebhookSignature() {
 	}
 
 	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		var payload mailgun.WebhookPayload
+		var payload mtypes.WebhookPayload
 		if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
 			fmt.Printf("decode JSON error: %s", err)
 			w.WriteHeader(http.StatusNotAcceptable)
diff --git a/httphelpers.go b/http.go
similarity index 100%
rename from httphelpers.go
rename to http.go
diff --git a/integration_test.go b/integration_test.go
index ca81f4e..c013b7c 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -24,18 +25,18 @@ func TestIntegrationMailgunImpl_ListMetrics(t *testing.T) {
 	domain := os.Getenv("MG_DOMAIN")
 	require.NotEmpty(t, domain)
 
-	opts := mailgun.MetricsOptions{
-		End:      mailgun.RFC2822Time(time.Now().UTC()),
+	opts := mtypes.MetricsRequest{
+		End:      mtypes.RFC2822Time(time.Now().UTC()),
 		Duration: "30d",
-		Pagination: mailgun.MetricsPagination{
+		Pagination: mtypes.MetricsPagination{
 			Limit: 10,
 		},
 	}
 	// filter by domain
-	opts.Filter.BoolGroupAnd = []mailgun.MetricsFilterPredicate{{
+	opts.Filter.BoolGroupAnd = []mtypes.MetricsFilterPredicate{{
 		Attribute:     "domain",
 		Comparator:    "=",
-		LabeledValues: []mailgun.MetricsLabeledValue{{Label: domain, Value: domain}},
+		LabeledValues: []mtypes.MetricsLabeledValue{{Label: domain, Value: domain}},
 	}}
 
 	iter, err := mg.ListMetrics(opts)
@@ -46,7 +47,7 @@ func TestIntegrationMailgunImpl_ListMetrics(t *testing.T) {
 	defer cancel()
 
 	for i := 0; i < 2; i++ {
-		var resp mailgun.MetricsResponse
+		var resp mtypes.MetricsResponse
 		more := iter.Next(ctx, &resp)
 		if iter.Err() != nil {
 			require.NoError(t, err)
diff --git a/mailgun_test.go b/mailgun_test.go
index c47541c..a38d836 100644
--- a/mailgun_test.go
+++ b/mailgun_test.go
@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -33,7 +34,7 @@ func TestInvalidBaseAPI(t *testing.T) {
 
 func TestValidBaseAPI(t *testing.T) {
 	testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
-		var resp mailgun.GetDomainResponse
+		var resp mtypes.GetDomainResponse
 		b, err := json.Marshal(resp)
 		require.NoError(t, err)
 
diff --git a/mailing_lists_test.go b/mailing_lists_test.go
index 03aead8..ae07a30 100644
--- a/mailing_lists_test.go
+++ b/mailing_lists_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -17,11 +18,11 @@ func TestMailingListMembers(t *testing.T) {
 
 	ctx := context.Background()
 	address := randomEmail("list", testDomain)
-	_, err = mg.CreateMailingList(ctx, mailgun.MailingList{
+	_, err = mg.CreateMailingList(ctx, mtypes.MailingList{
 		Address:     address,
 		Name:        address,
 		Description: "TestMailingListMembers-related mailing list",
-		AccessLevel: mailgun.AccessLevelMembers,
+		AccessLevel: mtypes.AccessLevelMembers,
 	})
 	require.NoError(t, err)
 	defer func() {
@@ -29,7 +30,7 @@ func TestMailingListMembers(t *testing.T) {
 	}()
 
 	var countMembers = func() int {
-		var page []mailgun.Member
+		var page []mtypes.Member
 		var count int
 
 		it := mg.ListMembers(address, nil)
@@ -41,10 +42,10 @@ func TestMailingListMembers(t *testing.T) {
 	}
 
 	startCount := countMembers()
-	protoJoe := mailgun.Member{
+	protoJoe := mtypes.Member{
 		Address:    "joe@example.com",
 		Name:       "Joe Example",
-		Subscribed: mailgun.Subscribed,
+		Subscribed: mtypes.Subscribed,
 	}
 	require.NoError(t, mg.CreateMember(ctx, true, address, protoJoe))
 	newCount := countMembers()
@@ -57,7 +58,7 @@ func TestMailingListMembers(t *testing.T) {
 	assert.Equal(t, protoJoe.Subscribed, theMember.Subscribed)
 	assert.Len(t, theMember.Vars, 0)
 
-	_, err = mg.UpdateMember(ctx, "joe@example.com", address, mailgun.Member{
+	_, err = mg.UpdateMember(ctx, "joe@example.com", address, mtypes.Member{
 		Name: "Joe Cool",
 	})
 	require.NoError(t, err)
@@ -69,17 +70,17 @@ func TestMailingListMembers(t *testing.T) {
 	assert.Equal(t, startCount, countMembers())
 
 	err = mg.CreateMemberList(ctx, nil, address, []any{
-		mailgun.Member{
+		mtypes.Member{
 			Address:    "joe.user1@example.com",
 			Name:       "Joe's debugging account",
-			Subscribed: mailgun.Unsubscribed,
+			Subscribed: mtypes.Unsubscribed,
 		},
-		mailgun.Member{
+		mtypes.Member{
 			Address:    "Joe Cool <joe.user2@example.com>",
 			Name:       "Joe's Cool Account",
-			Subscribed: mailgun.Subscribed,
+			Subscribed: mtypes.Subscribed,
 		},
-		mailgun.Member{
+		mtypes.Member{
 			Address: "joe.user3@example.com",
 			Vars: map[string]any{
 				"packet-email": "KW9ABC @ BOGBBS-4.#NCA.CA.USA.NOAM",
@@ -103,18 +104,18 @@ func TestMailingLists(t *testing.T) {
 	ctx := context.Background()
 
 	address := randomEmail("list", testDomain)
-	protoList := mailgun.MailingList{
+	protoList := mtypes.MailingList{
 		Address:         address,
 		Name:            "List1",
 		Description:     "A list created by an acceptance test.",
-		AccessLevel:     mailgun.AccessLevelMembers,
-		ReplyPreference: mailgun.ReplyPreferenceSender,
+		AccessLevel:     mtypes.AccessLevelMembers,
+		ReplyPreference: mtypes.ReplyPreferenceSender,
 	}
 
 	var countLists = func() int {
 		var count int
 		it := mg.ListMailingLists(nil)
-		var page []mailgun.MailingList
+		var page []mtypes.MailingList
 		for it.Next(ctx, &page) {
 			count += len(page)
 		}
@@ -140,7 +141,7 @@ func TestMailingLists(t *testing.T) {
 	protoList.CreatedAt = theList.CreatedAt // ignore this field when comparing.
 	assert.Equal(t, theList, protoList)
 
-	_, err = mg.UpdateMailingList(ctx, address, mailgun.MailingList{
+	_, err = mg.UpdateMailingList(ctx, address, mtypes.MailingList{
 		Description: "A list whose description changed",
 	})
 	require.NoError(t, err)
@@ -161,7 +162,7 @@ func TestListMailingListRegression(t *testing.T) {
 	ctx := context.Background()
 	address := "test@example.com"
 
-	_, err = mg.CreateMailingList(ctx, mailgun.MailingList{
+	_, err = mg.CreateMailingList(ctx, mtypes.MailingList{
 		Address:     address,
 		Name:        "paging",
 		Description: "Test paging",
@@ -174,7 +175,7 @@ func TestListMailingListRegression(t *testing.T) {
 			vars = map[string]any{"has": "vars"}
 		}
 
-		err := mg.CreateMember(ctx, false, address, mailgun.Member{
+		err := mg.CreateMember(ctx, false, address, mtypes.Member{
 			Address: fmt.Sprintf("%03d@example.com", i),
 			Vars:    vars,
 		})
@@ -183,7 +184,7 @@ func TestListMailingListRegression(t *testing.T) {
 
 	it := mg.ListMembers(address, nil)
 
-	var members []mailgun.Member
+	var members []mtypes.Member
 	var found int
 	for it.Next(ctx, &members) {
 		for _, m := range members {
diff --git a/rfc2822_test.go b/mtypes/rfc2822_test.go
similarity index 98%
rename from rfc2822_test.go
rename to mtypes/rfc2822_test.go
index 770b348..250da52 100644
--- a/rfc2822_test.go
+++ b/mtypes/rfc2822_test.go
@@ -1,4 +1,4 @@
-package mailgun
+package mtypes
 
 import (
 	"encoding/json"
diff --git a/routes_test.go b/routes_test.go
index 7b8a20a..0410071 100644
--- a/routes_test.go
+++ b/routes_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -17,7 +18,7 @@ func TestRouteCRUD(t *testing.T) {
 	ctx := context.Background()
 	var countRoutes = func() int {
 		it := mg.ListRoutes(nil)
-		var page []mailgun.Route
+		var page []mtypes.Route
 		it.Next(ctx, &page)
 		require.NoError(t, it.Err())
 		return it.TotalCount
@@ -25,7 +26,7 @@ func TestRouteCRUD(t *testing.T) {
 
 	routeCount := countRoutes()
 
-	newRoute, err := mg.CreateRoute(ctx, mailgun.Route{
+	newRoute, err := mg.CreateRoute(ctx, mtypes.Route{
 		Priority:    1,
 		Description: "Sample Route",
 		Expression:  "match_recipient(\".*@samples.mailgun.org\")",
@@ -50,7 +51,7 @@ func TestRouteCRUD(t *testing.T) {
 	require.NoError(t, err)
 	assert.Equal(t, newRoute, theRoute)
 
-	changedRoute, err := mg.UpdateRoute(ctx, newRoute.Id, mailgun.Route{
+	changedRoute, err := mg.UpdateRoute(ctx, newRoute.Id, mtypes.Route{
 		Priority: 2,
 	})
 	require.NoError(t, err)
@@ -65,7 +66,7 @@ func TestRoutesIterator(t *testing.T) {
 
 	it := mg.ListRoutes(&mailgun.ListOptions{Limit: 2})
 
-	var firstPage, secondPage, previousPage, lastPage []mailgun.Route
+	var firstPage, secondPage, previousPage, lastPage []mtypes.Route
 	var ctx = context.Background()
 
 	// Calling Last() is invalid unless you first use First() or Next()
diff --git a/spam_complaints_test.go b/spam_complaints_test.go
index d0edac5..11b9d0e 100644
--- a/spam_complaints_test.go
+++ b/spam_complaints_test.go
@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/require"
 )
 
@@ -18,7 +19,7 @@ func TestGetComplaints(t *testing.T) {
 	ctx := context.Background()
 
 	it := mg.ListComplaints(testDomain, nil)
-	var page []mailgun.Complaint
+	var page []mtypes.Complaint
 	for it.Next(ctx, &page) {
 	}
 	require.NoError(t, it.Err())
@@ -51,7 +52,7 @@ func TestCreateDeleteComplaint(t *testing.T) {
 		it := mg.ListComplaints(testDomain, nil)
 		require.NoError(t, it.Err())
 
-		var page []mailgun.Complaint
+		var page []mtypes.Complaint
 		for it.Next(ctx, &page) {
 			for _, complaint := range page {
 				t.Logf("Complaint Address: %s\n", complaint.Address)
@@ -84,7 +85,7 @@ func TestCreateDeleteComplaintList(t *testing.T) {
 		it := mg.ListComplaints(testDomain, nil)
 		require.NoError(t, it.Err())
 
-		var page []mailgun.Complaint
+		var page []mtypes.Complaint
 		for it.Next(ctx, &page) {
 			for _, complaint := range page {
 				t.Logf("Complaint Address: %s\n", complaint.Address)
diff --git a/storage_test.go b/storage_test.go
index 249671b..bcf0f75 100644
--- a/storage_test.go
+++ b/storage_test.go
@@ -23,7 +23,7 @@ func TestStorage(t *testing.T) {
 	msg, id, err := mg.Send(ctx, m)
 	require.NoError(t, err)
 
-	t.Logf("New Email: %s Id: %s\n", msg, id)
+	t.Logf("New Email: %s ID: %s\n", msg, id)
 
 	url, err := findStoredMessageURL(mg, strings.Trim(id, "<>"))
 	require.NoError(t, err)
diff --git a/subaccounts_test.go b/subaccounts_test.go
index a2cb077..3032b3e 100644
--- a/subaccounts_test.go
+++ b/subaccounts_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -26,7 +27,7 @@ func TestListSubaccounts(t *testing.T) {
 
 	ctx := context.Background()
 
-	var page []mailgun.Subaccount
+	var page []mtypes.Subaccount
 	for iterator.Next(ctx, &page) {
 		for _, d := range page {
 			t.Logf("TestListSubaccounts: %#v\n", d)
@@ -47,11 +48,11 @@ func TestGetSubaccount(t *testing.T) {
 	iterator := mg.ListSubaccounts(nil)
 	require.NotNil(t, iterator)
 
-	page := make([]mailgun.Subaccount, 0, 1)
+	page := make([]mtypes.Subaccount, 0, 1)
 	require.True(t, iterator.Next(context.Background(), &page))
 	require.NoError(t, iterator.Err())
 
-	resp, err := mg.GetSubaccount(ctx, page[0].Id)
+	resp, err := mg.GetSubaccount(ctx, page[0].ID)
 	require.NoError(t, err)
 	require.NotNil(t, resp)
 }
diff --git a/tags_test.go b/tags_test.go
index 9d890b0..e984c60 100644
--- a/tags_test.go
+++ b/tags_test.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/mailgun/errors"
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -39,7 +40,7 @@ func TestTags(t *testing.T) {
 
 	// Should return a list of available tags
 	it := mg.ListTags(testDomain, nil)
-	var page []mailgun.Tag
+	var page []mtypes.Tag
 	for it.Next(ctx, &page) {
 		require.True(t, len(page) != 0)
 	}
@@ -48,7 +49,7 @@ func TestTags(t *testing.T) {
 	// Should return a limited list of available tags
 	cursor := mg.ListTags(testDomain, &mailgun.ListTagOptions{Limit: 1})
 
-	var tags []mailgun.Tag
+	var tags []mtypes.Tag
 	for cursor.Next(ctx, &tags) {
 		require.Len(t, tags, 1)
 	}
diff --git a/template_test.go b/template_test.go
index 0fbf3de..f1c3e9a 100644
--- a/template_test.go
+++ b/template_test.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/mailgun/errors"
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -22,7 +23,7 @@ func TestTemplateCRUD(t *testing.T) {
 	findTemplate := func(name string) bool {
 		it := mg.ListTemplates(testDomain, nil)
 
-		var page []mailgun.Template
+		var page []mtypes.Template
 		for it.Next(ctx, &page) {
 			for _, template := range page {
 				if template.Name == name {
@@ -40,7 +41,7 @@ func TestTemplateCRUD(t *testing.T) {
 		UpdatedDesc = "Mailgun-Go Test Updated Description"
 	)
 
-	tmpl := mailgun.Template{
+	tmpl := mtypes.Template{
 		Name:        Name,
 		Description: Description,
 	}
diff --git a/template_versions_test.go b/template_versions_test.go
index 39c1adf..ff194e6 100644
--- a/template_versions_test.go
+++ b/template_versions_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -19,7 +20,7 @@ func TestTemplateVersionsCRUD(t *testing.T) {
 	findVersion := func(templateName, tag string) bool {
 		it := mg.ListTemplateVersions(testDomain, templateName, nil)
 
-		var page []mailgun.TemplateVersion
+		var page []mtypes.TemplateVersion
 		for it.Next(ctx, &page) {
 			for _, v := range page {
 				if v.Tag == tag {
@@ -38,26 +39,26 @@ func TestTemplateVersionsCRUD(t *testing.T) {
 		Tag            = "v1"
 	)
 
-	tmpl := mailgun.Template{
+	tmpl := mtypes.Template{
 		Name: randomString(10, "Mailgun-go-TestTemplateVersionsCRUD-"),
 	}
 
 	// Create a template
 	require.NoError(t, mg.CreateTemplate(ctx, testDomain, &tmpl))
 
-	version := mailgun.TemplateVersion{
+	version := mtypes.TemplateVersion{
 		Tag:      Tag,
 		Comment:  Comment,
 		Template: Template,
 		Active:   true,
-		Engine:   mailgun.TemplateEngineGo,
+		Engine:   mtypes.TemplateEngineGo,
 	}
 
 	// Add a version version
 	require.NoError(t, mg.AddTemplateVersion(ctx, testDomain, tmpl.Name, &version))
 	assert.Equal(t, Tag, version.Tag)
 	assert.Equal(t, Comment, version.Comment)
-	assert.Equal(t, mailgun.TemplateEngineGo, version.Engine)
+	assert.Equal(t, mtypes.TemplateEngineGo, version.Engine)
 
 	// Ensure the version is in the list
 	require.True(t, findVersion(tmpl.Name, version.Tag))
@@ -75,12 +76,12 @@ func TestTemplateVersionsCRUD(t *testing.T) {
 	assert.Equal(t, Template+"updated", updated.Template)
 
 	// Add a new active Version
-	version2 := mailgun.TemplateVersion{
+	version2 := mtypes.TemplateVersion{
 		Tag:      "v2",
 		Comment:  Comment,
 		Template: Template,
 		Active:   true,
-		Engine:   mailgun.TemplateEngineGo,
+		Engine:   mtypes.TemplateEngineGo,
 	}
 	require.NoError(t, mg.AddTemplateVersion(ctx, testDomain, tmpl.Name, &version2))
 
diff --git a/unsubscribes_test.go b/unsubscribes_test.go
index deb04f6..74a6ad1 100644
--- a/unsubscribes_test.go
+++ b/unsubscribes_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/require"
 )
 
@@ -22,7 +23,7 @@ func TestCreateUnsubscriber(t *testing.T) {
 }
 
 func TestCreateUnsubscribes(t *testing.T) {
-	unsubscribes := []mailgun.Unsubscribe{
+	unsubscribes := []mtypes.Unsubscribe{
 		{
 			Address: randomEmail("unsubcribe", os.Getenv("MG_DOMAIN")),
 		},
@@ -49,7 +50,7 @@ func TestListUnsubscribes(t *testing.T) {
 	ctx := context.Background()
 
 	it := mg.ListUnsubscribes(testDomain, nil)
-	var page []mailgun.Unsubscribe
+	var page []mtypes.Unsubscribe
 	for it.Next(ctx, &page) {
 		t.Logf("Received %d unsubscribe records.\n", len(page))
 		if len(page) > 0 {
diff --git a/webhooks_test.go b/webhooks_test.go
index 9f73aa6..f2bea9d 100644
--- a/webhooks_test.go
+++ b/webhooks_test.go
@@ -14,6 +14,7 @@ import (
 	"testing"
 
 	"github.com/mailgun/mailgun-go/v4"
+	"github.com/mailgun/mailgun-go/v4/mtypes"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -86,7 +87,7 @@ func TestVerifyWebhookSignature(t *testing.T) {
 
 	for _, v := range signedTests {
 		fields := getSignatureFields(mg.WebhookSigningKey(), v)
-		sig := mailgun.Signature{
+		sig := mtypes.Signature{
 			TimeStamp: fields["timestamp"],
 			Token:     fields["token"],
 			Signature: fields["signature"],