Repository: mailgun/mailgun-go
Pull request #402 (2025-03-13 14:37:05)

Fix TODOs and remove deprecated things


Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 921d6ea..d428c70 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -74,7 +74,7 @@ linters-settings:
 
   revive:
     rules:
-      # TODO(v5): enable:
+      # TODO(v6): enable:
       #      # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#argument-limit
       #      - name: argument-limit
       #        severity: warning
@@ -461,7 +461,8 @@ linters-settings:
         disabled: false
         exclude: [""]
 
-      # TODO(v5): enable:
+      # Rule panics
+      # TODO: enable after the fix
       #      # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-naming
       #      - name: var-naming
       #        severity: warning
diff --git a/README.md b/README.md
index f06b6de..111782c 100644
--- a/README.md
+++ b/README.md
@@ -53,13 +53,13 @@ func main() {
 	defer cancel()
 
 	// Send the message with a 10-second timeout
-	resp, id, err := mg.Send(ctx, message)
+	resp, err := mg.Send(ctx, message)
 
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	fmt.Printf("ID: %s Resp: %s\n", id, resp)
+	fmt.Printf("ID: %s Resp: %s\n", resp.ID, resp.Message)
 }
 ```
 
@@ -306,13 +306,13 @@ func main() {
 	defer cancel()
 
 	// Send the message with a 10-second timeout
-	resp, id, err := mg.Send(ctx, message)
+	resp, err := mg.Send(ctx, message)
 
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	fmt.Printf("ID: %s Resp: %s\n", id, resp)
+	fmt.Printf("ID: %s Resp: %s\n", resp.ID, resp.Message)
 }
 ```
 
@@ -361,12 +361,12 @@ func main() {
 	defer cancel()
 
 	// Send the message with a 10-second timeout
-	resp, id, err := mg.Send(ctx, message)
+	resp, err := mg.Send(ctx, message)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	fmt.Printf("ID: %s Resp: %s\n", id, resp)
+	fmt.Printf("ID: %s Resp: %s\n", resp.ID, resp.Message)
 }
 ```
 
diff --git a/domains.go b/domains.go
index 5071967..0ed909a 100644
--- a/domains.go
+++ b/domains.go
@@ -175,7 +175,7 @@ func (mg *Client) GetDomain(ctx context.Context, domain string, _ *GetDomainOpti
 	return resp, err
 }
 
-func (mg *Client) VerifyDomain(ctx context.Context, domain string) (mtypes.GetDomainResponse, error) {
+func (mg *Client) VerifyAndReturnDomain(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())
@@ -195,7 +195,7 @@ type CreateDomainOptions struct {
 	Wildcard           bool
 	ForceDKIMAuthority bool
 	DKIMKeySize        int
-	IPS                []string
+	IPs                []string
 	WebScheme          string
 }
 
@@ -226,8 +226,8 @@ func (mg *Client) CreateDomain(ctx context.Context, name string, opts *CreateDom
 		if opts.DKIMKeySize != 0 {
 			payload.addValue("dkim_key_size", strconv.Itoa(opts.DKIMKeySize))
 		}
-		if len(opts.IPS) != 0 {
-			payload.addValue("ips", strings.Join(opts.IPS, ","))
+		if len(opts.IPs) != 0 {
+			payload.addValue("ips", strings.Join(opts.IPs, ","))
 		}
 		if opts.Password != "" {
 			payload.addValue("smtp_password", opts.Password)
diff --git a/events/parse.go b/events/parse.go
index 346ac43..44f829c 100644
--- a/events/parse.go
+++ b/events/parse.go
@@ -73,7 +73,7 @@ func ParseEvents(raw []RawJSON) ([]Event, error) {
 	return result, nil
 }
 
-// Parse converts raw bytes data into an event struct. Can accept events.RawJSON as input
+// ParseEvent converts raw bytes data into an event struct. Can accept events.RawJSON as input
 func ParseEvent(raw []byte) (Event, error) {
 	// Try to recognize the event first.
 	var e EventName
diff --git a/examples/examples.go b/examples/examples.go
index 585ab58..7a51489 100644
--- a/examples/examples.go
+++ b/examples/examples.go
@@ -433,7 +433,7 @@ func ListDomainIPS(domain, apiKey string) ([]mtypes.IPAddress, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	return mg.ListDomainIPS(ctx, domain)
+	return mg.ListDomainIPs(ctx, domain)
 }
 
 func GetDomainTracking(domain, apiKey string) (mtypes.DomainTracking, error) {
@@ -489,7 +489,7 @@ func ListIPS(domain, apiKey string) ([]mtypes.IPAddress, error) {
 
 	// Pass 'true' as the first parameter to only return dedicated ips,
 	// pass 'true' as the second parameter to only return enabled ips.
-	return mg.ListIPS(ctx, true, true)
+	return mg.ListIPs(ctx, true, true)
 }
 
 func GetTagLimits(domain, apiKey string) (mtypes.TagLimits, error) {
@@ -664,7 +664,7 @@ func DeleteMailingList(apiKey string) error {
 	return mg.DeleteMailingList(ctx, "list@example.com")
 }
 
-func ResendMessage(domain, apiKey string) (string, string, error) {
+func ResendMessage(domain, apiKey string) (mtypes.SendMessageResponse, error) {
 	mg := mailgun.NewMailgun(apiKey)
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
@@ -691,8 +691,8 @@ func SendComplexMessage(domain, apiKey string) (string, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	_, id, err := mg.Send(ctx, m)
-	return id, err
+	resp, err := mg.Send(ctx, m)
+	return resp.ID, err
 }
 
 func SendWithConnectionOptions(domain, apiKey string) (string, error) {
@@ -711,8 +711,8 @@ func SendWithConnectionOptions(domain, apiKey string) (string, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	_, id, err := mg.Send(ctx, m)
-	return id, err
+	resp, err := mg.Send(ctx, m)
+	return resp.ID, err
 }
 
 func SendInlineImage(domain, apiKey string) (string, error) {
@@ -732,8 +732,8 @@ func SendInlineImage(domain, apiKey string) (string, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	_, id, err := mg.Send(ctx, m)
-	return id, err
+	resp, err := mg.Send(ctx, m)
+	return resp.ID, err
 }
 
 func SendMessageNoTracking(domain, apiKey string) (string, error) {
@@ -750,8 +750,8 @@ func SendMessageNoTracking(domain, apiKey string) (string, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	_, id, err := mg.Send(ctx, m)
-	return id, err
+	resp, err := mg.Send(ctx, m)
+	return resp.ID, err
 }
 
 func SendMimeMessage(domain, apiKey string) (string, error) {
@@ -766,8 +766,8 @@ func SendMimeMessage(domain, apiKey string) (string, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	_, id, err := mg.Send(ctx, m)
-	return id, err
+	resp, err := mg.Send(ctx, m)
+	return resp.ID, err
 }
 
 func SendScheduledMessage(domain, apiKey string) (string, error) {
@@ -784,8 +784,8 @@ func SendScheduledMessage(domain, apiKey string) (string, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	_, id, err := mg.Send(ctx, m)
-	return id, err
+	resp, err := mg.Send(ctx, m)
+	return resp.ID, err
 }
 
 func SendSimpleMessage(domain, apiKey string) (string, error) {
@@ -801,8 +801,8 @@ func SendSimpleMessage(domain, apiKey string) (string, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	_, id, err := mg.Send(ctx, m)
-	return id, err
+	resp, err := mg.Send(ctx, m)
+	return resp.ID, err
 }
 
 func SendTaggedMessage(domain, apiKey string) (string, error) {
@@ -823,8 +823,8 @@ func SendTaggedMessage(domain, apiKey string) (string, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	_, id, err := mg.Send(ctx, m)
-	return id, err
+	resp, err := mg.Send(ctx, m)
+	return resp.ID, err
 }
 
 func SendTemplateMessage(domain, apiKey string) (string, error) {
@@ -852,8 +852,8 @@ func SendTemplateMessage(domain, apiKey string) (string, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
 	defer cancel()
 
-	_, id, err := mg.Send(ctx, m)
-	return id, err
+	resp, err := mg.Send(ctx, m)
+	return resp.ID, err
 }
 
 func UpdateDomainConnection(domain, apiKey string) error {
@@ -936,8 +936,8 @@ func SendMessageWithTemplate(domain, apiKey string) error {
 	m.AddVariable("title", "Hello Templates")
 	m.AddVariable("body", "Body of the message")
 
-	_, id, err := mg.Send(ctx, m)
-	fmt.Printf("Queued: %s", id)
+	resp, err := mg.Send(ctx, m)
+	fmt.Printf("Queued: %s", resp.ID)
 	return err
 }
 
diff --git a/exports.go b/exports.go
index e79185d..f65cdc9 100644
--- a/exports.go
+++ b/exports.go
@@ -66,7 +66,7 @@ func (mg *Client) GetExportLink(ctx context.Context, id string) (string, error)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
 
-	r.addHeader("User-Agent", MailgunGoUserAgent)
+	r.addHeader("User-Agent", UserAgent)
 
 	req, err := r.NewRequest(ctx, http.MethodGet, nil)
 	if err != nil {
diff --git a/ips.go b/ips.go
index c13eee1..4f25aa5 100644
--- a/ips.go
+++ b/ips.go
@@ -6,8 +6,8 @@ import (
 	"github.com/mailgun/mailgun-go/v5/mtypes"
 )
 
-// ListIPS returns a list of IPs assigned to your account
-func (mg *Client) ListIPS(ctx context.Context, dedicated, enabled bool) ([]mtypes.IPAddress, error) {
+// ListIPs returns a list of IPs assigned to your account
+func (mg *Client) ListIPs(ctx context.Context, dedicated, enabled bool) ([]mtypes.IPAddress, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, ipsEndpoint))
 	r.setClient(mg.HTTPClient())
 	if dedicated {
@@ -39,8 +39,8 @@ func (mg *Client) GetIP(ctx context.Context, ip string) (mtypes.IPAddress, error
 	return resp, err
 }
 
-// ListDomainIPS returns a list of IPs currently assigned to the specified domain.
-func (mg *Client) ListDomainIPS(ctx context.Context, domain string) ([]mtypes.IPAddress, error) {
+// ListDomainIPs returns a list of IPs currently assigned to the specified domain.
+func (mg *Client) 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())
@@ -56,7 +56,7 @@ func (mg *Client) ListDomainIPS(ctx context.Context, domain string) ([]mtypes.IP
 	return result, nil
 }
 
-// AddDomainIP Assign a dedicated IP to the domain specified.
+// AddDomainIP assign a dedicated IP to the domain specified.
 func (mg *Client) AddDomainIP(ctx context.Context, domain, ip string) error {
 	r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/ips")
 	r.setClient(mg.HTTPClient())
@@ -68,7 +68,7 @@ func (mg *Client) AddDomainIP(ctx context.Context, domain, ip string) error {
 	return err
 }
 
-// DeleteDomainIP Unassign an IP from the domain specified.
+// DeleteDomainIP unassign an IP from the domain specified.
 func (mg *Client) DeleteDomainIP(ctx context.Context, domain, ip string) error {
 	r := newHTTPRequest(generateApiUrl(mg, 3, domainsEndpoint) + "/" + domain + "/ips/" + ip)
 	r.setClient(mg.HTTPClient())
diff --git a/mailgun.go b/mailgun.go
index c1723ea..df1e877 100644
--- a/mailgun.go
+++ b/mailgun.go
@@ -134,8 +134,8 @@ type Mailgun interface {
 	AddOverrideHeader(k string, v string)
 
 	// Send attempts to queue a message (see CommonMessage, NewMessage, and its methods) for delivery.
-	Send(ctx context.Context, m SendableMessage) (mes string, id string, err error)
-	ReSend(ctx context.Context, id string, recipients ...string) (string, string, error)
+	Send(ctx context.Context, m Message) (mtypes.SendMessageResponse, error)
+	ReSend(ctx context.Context, url string, recipients ...string) (mtypes.SendMessageResponse, error)
 
 	ListBounces(domain string, opts *ListOptions) *BouncesIterator
 	GetBounce(ctx context.Context, domain, address string) (mtypes.Bounce, error)
@@ -153,7 +153,7 @@ type Mailgun interface {
 	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) (mtypes.GetDomainResponse, error)
+	VerifyAndReturnDomain(ctx context.Context, name string) (mtypes.GetDomainResponse, error)
 
 	UpdateDomainConnection(ctx context.Context, domain string, dc mtypes.DomainConnection) error
 	GetDomainConnection(ctx context.Context, domain string) (mtypes.DomainConnection, error)
@@ -216,9 +216,9 @@ type Mailgun interface {
 	ListEvents(domain string, opts *ListEventOptions) *EventIterator
 	PollEvents(domain string, opts *ListEventOptions) *EventPoller
 
-	ListIPS(ctx context.Context, dedicated, enabled bool) ([]mtypes.IPAddress, error)
+	ListIPs(ctx context.Context, dedicated, enabled bool) ([]mtypes.IPAddress, error)
 	GetIP(ctx context.Context, ip string) (mtypes.IPAddress, error)
-	ListDomainIPS(ctx context.Context, domain 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
 
diff --git a/messages.go b/messages.go
index bfe4a68..657fb1e 100644
--- a/messages.go
+++ b/messages.go
@@ -10,6 +10,8 @@ import (
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/mailgun/mailgun-go/v5/mtypes"
 )
 
 // MaxNumberOfRecipients represents the largest batch of recipients that Mailgun can support in a single API call.
@@ -113,13 +115,6 @@ type MimeMessage struct {
 	body io.ReadCloser
 }
 
-// TODO(v5): return from Send()
-// TODO(v5): move to mtypes?
-type sendMessageResponse struct {
-	Message string `json:"message"`
-	ID      string `json:"id"`
-}
-
 // TrackingOptions contains fields relevant to tracking.
 type TrackingOptions struct {
 	Tracking       bool
@@ -127,7 +122,7 @@ type TrackingOptions struct {
 	TrackingOpens  bool
 }
 
-// Specific abstracts the common characteristics between plain text and MIME messages.
+// The Specific abstracts the common characteristics between plain text and MIME messages.
 type Specific interface {
 	// AddCC appends a receiver to the carbon-copy header of a message.
 	AddCC(string)
@@ -598,8 +593,7 @@ func (m *CommonMessage) Headers() map[string]string {
 // ErrInvalidMessage is returned by `Send()` when the `mailgun.CommonMessage` struct is incomplete
 var ErrInvalidMessage = errors.New("message not valid")
 
-// TODO(v5): rename to Message?
-type SendableMessage interface {
+type Message interface {
 	Domain() string
 	To() []string
 	Tags() []string
@@ -649,40 +643,42 @@ type SendableMessage interface {
 //	}
 //
 // See the public mailgun documentation for all possible return codes and error messages
-func (mg *Client) Send(ctx context.Context, m SendableMessage) (mes, id string, err error) {
+func (mg *Client) Send(ctx context.Context, m Message) (mtypes.SendMessageResponse, error) {
+	var response mtypes.SendMessageResponse
+
 	if m.Domain() == "" {
-		err = errors.New("you must provide a valid domain before calling Send()")
-		return "", "", err
+		err := errors.New("you must provide a valid domain before calling Send()")
+		return response, 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
+		err := fmt.Errorf("you called Send() with a domain that contains invalid characters")
+		return response, err
 	}
 
 	if mg.apiKey == "" {
-		err = errors.New("you must provide a valid api-key before calling Send()")
-		return "", "", err
+		err := errors.New("you must provide a valid api-key before calling Send()")
+		return response, err
 	}
 
 	if !isValid(m) {
-		err = ErrInvalidMessage
-		return "", "", err
+		err := ErrInvalidMessage
+		return response, err
 	}
 
 	if m.STOPeriod() != "" && m.RecipientCount() > 1 {
-		err = errors.New("STO can only be used on a per-message basis")
-		return "", "", err
+		err := errors.New("STO can only be used on a per-message basis")
+		return response, err
 	}
 	payload := NewFormDataPayload()
 
 	m.AddValues(payload)
 
 	// TODO: make (CommonMessage).AddValues()?
-	err = addMessageValues(payload, m)
+	err := addMessageValues(payload, m)
 	if err != nil {
-		return "", "", err
+		return response, err
 	}
 
 	r := newHTTPRequest(generateApiV3UrlWithDomain(mg, m.Endpoint(), m.Domain()))
@@ -693,17 +689,12 @@ func (mg *Client) Send(ctx context.Context, m SendableMessage) (mes, id string,
 		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
+	return response, err
 }
 
-func addMessageValues(dst *FormDataPayload, src SendableMessage) error {
+func addMessageValues(dst *FormDataPayload, src Message) error {
 	addMessageOptions(dst, src)
 	addMessageHeaders(dst, src)
 
@@ -717,7 +708,7 @@ func addMessageValues(dst *FormDataPayload, src SendableMessage) error {
 	return nil
 }
 
-func addMessageOptions(dst *FormDataPayload, src SendableMessage) {
+func addMessageOptions(dst *FormDataPayload, src Message) {
 	for _, to := range src.To() {
 		dst.addValue("to", to)
 	}
@@ -764,7 +755,7 @@ func addMessageOptions(dst *FormDataPayload, src SendableMessage) {
 	}
 }
 
-func addMessageHeaders(dst *FormDataPayload, src SendableMessage) {
+func addMessageHeaders(dst *FormDataPayload, src Message) {
 	if src.Headers() != nil {
 		for header, value := range src.Headers() {
 			dst.addValue("h:"+header, value)
@@ -772,7 +763,7 @@ func addMessageHeaders(dst *FormDataPayload, src SendableMessage) {
 	}
 }
 
-func addMessageVariables(dst *FormDataPayload, src SendableMessage) error {
+func addMessageVariables(dst *FormDataPayload, src Message) error {
 	if src.Variables() != nil {
 		for variable, value := range src.Variables() {
 			dst.addValue("v:"+variable, value)
@@ -796,7 +787,7 @@ func addMessageVariables(dst *FormDataPayload, src SendableMessage) error {
 	return nil
 }
 
-func addMessageAttachment(dst *FormDataPayload, src SendableMessage) {
+func addMessageAttachment(dst *FormDataPayload, src Message) {
 	if src.Attachments() != nil {
 		for _, attachment := range src.Attachments() {
 			dst.addFile("attachment", attachment)
@@ -871,7 +862,7 @@ func trueFalse(b bool) string {
 
 // isValid returns true if, and only if,
 // a CommonMessage instance is sufficiently initialized to send via the Mailgun interface.
-func isValid(m SendableMessage) bool {
+func isValid(m Message) bool {
 	if m == nil {
 		return false
 	}
diff --git a/mtypes/messages.go b/mtypes/messages.go
new file mode 100644
index 0000000..805090d
--- /dev/null
+++ b/mtypes/messages.go
@@ -0,0 +1,6 @@
+package mtypes
+
+type SendMessageResponse struct {
+	Message string `json:"message"`
+	ID      string `json:"id"`
+}
diff --git a/rest_shim.go b/rest_shim.go
index 57b5ec1..e656d9b 100644
--- a/rest_shim.go
+++ b/rest_shim.go
@@ -6,10 +6,10 @@ import (
 	"net/http"
 )
 
-// The MailgunGoUserAgent identifies the client to the server, for logging purposes.
+// The UserAgent identifies the client to the server, for logging purposes.
 // In the event of problems requiring a human administrator's assistance,
 // this user agent allows them to identify the client from human-generated activity.
-const MailgunGoUserAgent = "mailgun-go/" + Version
+const UserAgent = "mailgun-go/" + Version
 
 // UnexpectedResponseError this error will be returned whenever a Mailgun API returns an error response.
 // Your application can check the Actual field to see the actual HTTP response code returned.
@@ -63,7 +63,7 @@ var expected = []int{200, 202, 204}
 // makeRequest shim performs a generic request, checking for a positive outcome.
 // See simplehttp.MakeRequest for more details.
 func makeRequest(ctx context.Context, r *httpRequest, method string, p payload) (*httpResponse, error) {
-	r.addHeader("User-Agent", MailgunGoUserAgent)
+	r.addHeader("User-Agent", UserAgent)
 	rsp, err := r.makeRequest(ctx, method, p)
 	if (err == nil) && notGood(rsp.Code, expected) {
 		return rsp, newError(method, r.URL, expected, rsp)
@@ -74,7 +74,7 @@ func makeRequest(ctx context.Context, r *httpRequest, method string, p payload)
 // getResponseFromJSON shim performs a GET request, checking for a positive outcome.
 // See simplehttp.GetResponseFromJSON for more details.
 func getResponseFromJSON(ctx context.Context, r *httpRequest, v any) error {
-	r.addHeader("User-Agent", MailgunGoUserAgent)
+	r.addHeader("User-Agent", UserAgent)
 	response, err := r.makeGetRequest(ctx)
 	if err != nil {
 		return err
@@ -88,7 +88,7 @@ func getResponseFromJSON(ctx context.Context, r *httpRequest, v any) error {
 // postResponseFromJSON shim performs a POST request, checking for a positive outcome.
 // See simplehttp.PostResponseFromJSON for more details.
 func postResponseFromJSON(ctx context.Context, r *httpRequest, p payload, v any) error {
-	r.addHeader("User-Agent", MailgunGoUserAgent)
+	r.addHeader("User-Agent", UserAgent)
 	response, err := r.makePostRequest(ctx, p)
 	if err != nil {
 		return err
@@ -102,7 +102,7 @@ func postResponseFromJSON(ctx context.Context, r *httpRequest, p payload, v any)
 // putResponseFromJSON shim performs a PUT request, checking for a positive outcome.
 // See simplehttp.PutResponseFromJSON for more details.
 func putResponseFromJSON(ctx context.Context, r *httpRequest, p payload, v any) error {
-	r.addHeader("User-Agent", MailgunGoUserAgent)
+	r.addHeader("User-Agent", UserAgent)
 	response, err := r.makePutRequest(ctx, p)
 	if err != nil {
 		return err
@@ -116,7 +116,7 @@ func putResponseFromJSON(ctx context.Context, r *httpRequest, p payload, v any)
 // makeGetRequest shim performs a GET request, checking for a positive outcome.
 // See simplehttp.MakeGetRequest for more details.
 func makeGetRequest(ctx context.Context, r *httpRequest) (*httpResponse, error) {
-	r.addHeader("User-Agent", MailgunGoUserAgent)
+	r.addHeader("User-Agent", UserAgent)
 	rsp, err := r.makeGetRequest(ctx)
 	if (err == nil) && notGood(rsp.Code, expected) {
 		return rsp, newError(http.MethodGet, r.URL, expected, rsp)
@@ -127,7 +127,7 @@ func makeGetRequest(ctx context.Context, r *httpRequest) (*httpResponse, error)
 // makePostRequest shim performs a POST request, checking for a positive outcome.
 // See simplehttp.MakePostRequest for more details.
 func makePostRequest(ctx context.Context, r *httpRequest, p payload) (*httpResponse, error) {
-	r.addHeader("User-Agent", MailgunGoUserAgent)
+	r.addHeader("User-Agent", UserAgent)
 	rsp, err := r.makePostRequest(ctx, p)
 	if (err == nil) && notGood(rsp.Code, expected) {
 		return rsp, newError(http.MethodPost, r.URL, expected, rsp)
@@ -138,7 +138,7 @@ func makePostRequest(ctx context.Context, r *httpRequest, p payload) (*httpRespo
 // makePutRequest shim performs a PUT request, checking for a positive outcome.
 // See simplehttp.MakePutRequest for more details.
 func makePutRequest(ctx context.Context, r *httpRequest, p payload) (*httpResponse, error) {
-	r.addHeader("User-Agent", MailgunGoUserAgent)
+	r.addHeader("User-Agent", UserAgent)
 	rsp, err := r.makePutRequest(ctx, p)
 	if (err == nil) && notGood(rsp.Code, expected) {
 		return rsp, newError(http.MethodPut, r.URL, expected, rsp)
@@ -149,7 +149,7 @@ func makePutRequest(ctx context.Context, r *httpRequest, p payload) (*httpRespon
 // makeDeleteRequest shim performs a DELETE request, checking for a positive outcome.
 // See simplehttp.MakeDeleteRequest for more details.
 func makeDeleteRequest(ctx context.Context, r *httpRequest) (*httpResponse, error) {
-	r.addHeader("User-Agent", MailgunGoUserAgent)
+	r.addHeader("User-Agent", UserAgent)
 	rsp, err := r.makeDeleteRequest(ctx)
 	if (err == nil) && notGood(rsp.Code, expected) {
 		return rsp, newError(http.MethodDelete, r.URL, expected, rsp)
diff --git a/stored_messages.go b/stored_messages.go
index ee0c3d5..7a60b92 100644
--- a/stored_messages.go
+++ b/stored_messages.go
@@ -19,9 +19,10 @@ func (mg *Client) GetStoredMessage(ctx context.Context, url string) (mtypes.Stor
 	return response, err
 }
 
-// ReSend given a storage id resend the stored message to the specified recipients
-// TODO(v5): return SendMessageResponse
-func (mg *Client) ReSend(ctx context.Context, url string, recipients ...string) (msg, id string, err error) {
+// ReSend given a storage url resend the stored message to the specified recipients
+func (mg *Client) ReSend(ctx context.Context, url string, recipients ...string) (mtypes.SendMessageResponse, error) {
+	var resp mtypes.SendMessageResponse
+
 	r := newHTTPRequest(url)
 	r.setClient(mg.HTTPClient())
 	r.setBasicAuth(basicAuthUser, mg.APIKey())
@@ -29,20 +30,19 @@ func (mg *Client) ReSend(ctx context.Context, url string, recipients ...string)
 	payload := NewFormDataPayload()
 
 	if len(recipients) == 0 {
-		return "", "", errors.New("must provide at least one recipient")
+		return resp, errors.New("must provide at least one recipient")
 	}
 
 	for _, to := range recipients {
 		payload.addValue("to", to)
 	}
 
-	var resp sendMessageResponse
-	err = postResponseFromJSON(ctx, r, payload, &resp)
+	err := postResponseFromJSON(ctx, r, payload, &resp)
 	if err != nil {
-		return "", "", err
+		return resp, err
 	}
 
-	return resp.Message, resp.ID, nil
+	return resp, nil
 }
 
 // GetStoredMessageRaw retrieves the raw MIME body of a received e-mail message.
diff --git a/version.go b/version.go
index b847176..8ba1b69 100644
--- a/version.go
+++ b/version.go
@@ -2,4 +2,4 @@ package mailgun
 
 // Version of current release
 // TODO(vtopc): automate this
-const Version = "5.0.0-rc1"
+const Version = "5.0.0"


Test patch:
diff --git a/attachments_test.go b/attachments_test.go
index ea844fc..2bd3591 100644
--- a/attachments_test.go
+++ b/attachments_test.go
@@ -38,11 +38,11 @@ func TestMultipleAttachments(t *testing.T) {
 	m.AddAttachment(createAttachment(t))
 	m.AddAttachment(createAttachment(t))
 
-	msg, id, err := mg.Send(ctx, m)
+	resp, err := mg.Send(ctx, m)
 	require.NoError(t, err)
 
-	id = strings.Trim(id, "<>")
-	t.Logf("New Email: %s ID: %s\n", msg, id)
+	id := strings.Trim(resp.ID, "<>")
+	t.Logf("New Email: %s ID: %s\n", resp.Message, id)
 
 	e, err := findAcceptedMessage(mg, id)
 	require.NoError(t, err)
diff --git a/domains_test.go b/domains_test.go
index c2de24e..ece44ae 100644
--- a/domains_test.go
+++ b/domains_test.go
@@ -105,6 +105,6 @@ func TestDomainVerify(t *testing.T) {
 
 	ctx := context.Background()
 
-	_, err = mg.VerifyDomain(ctx, testDomain)
+	_, err = mg.VerifyAndReturnDomain(ctx, testDomain)
 	require.NoError(t, err)
 }
diff --git a/events_test.go b/events_test.go
index 84ac6d2..cb1c277 100644
--- a/events_test.go
+++ b/events_test.go
@@ -90,10 +90,10 @@ func TestEventPoller(t *testing.T) {
 
 	// Send an email
 	m := mailgun.NewMessage(testDomain, "root@"+testDomain, "Subject", "Text Body", "user@"+testDomain)
-	msg, id, err := mg.Send(ctx, m)
+	resp, 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", resp.Message, resp.ID)
 
 	var accepted *events.Accepted
 	for e := range eventChan {
@@ -101,7 +101,7 @@ func TestEventPoller(t *testing.T) {
 		case *events.Accepted:
 			t.Logf("Accepted Event: %s - %v", event.Message.Headers.MessageID, event.GetTimestamp())
 			// If we find our accepted email event
-			if id == ("<" + event.Message.Headers.MessageID + ">") {
+			if resp.ID == ("<" + event.Message.Headers.MessageID + ">") {
 				accepted = event
 				cancel()
 			}
diff --git a/examples_test.go b/examples_test.go
index 03b567e..8a4a2f9 100644
--- a/examples_test.go
+++ b/examples_test.go
@@ -66,12 +66,12 @@ func ExampleMailgun_Send_constructed() {
 	m.SetTracking(true)
 	m.SetDeliveryTime(time.Now().Add(24 * time.Hour))
 	m.SetHTML("<html><body><h1>Testing some Mailgun Awesomeness!!</h1></body></html>")
-	_, id, err := mg.Send(ctx, m)
+	resp, err := mg.Send(ctx, m)
 	if err != nil {
 		log.Println(err)
 		return
 	}
-	log.Printf("Message id=%s", id)
+	log.Printf("Message id=%s", resp.ID)
 }
 
 func ExampleMailgun_Send_mime() {
@@ -90,12 +90,12 @@ Testing some Mailgun MIME awesomeness!
 
 	mg := mailgun.NewMailgun("my_api_key")
 	m := mailgun.NewMIMEMessage("example.com", io.NopCloser(strings.NewReader(exampleMime)), "bargle.garf@example.com")
-	_, id, err := mg.Send(ctx, m)
+	resp, err := mg.Send(ctx, m)
 	if err != nil {
 		log.Println(err)
 		return
 	}
-	log.Printf("Message id=%s", id)
+	log.Printf("Message id=%s", resp.ID)
 }
 
 func ExampleMailgun_ListRoutes() {
diff --git a/ips_test.go b/ips_test.go
index 1cfc4ec..b06f3ec 100644
--- a/ips_test.go
+++ b/ips_test.go
@@ -9,13 +9,13 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-func TestListIPS(t *testing.T) {
+func TestListIPs(t *testing.T) {
 	mg := mailgun.NewMailgun(testKey)
 	err := mg.SetAPIBase(server.URL())
 	require.NoError(t, err)
 
 	ctx := context.Background()
-	list, err := mg.ListIPS(ctx, false, false)
+	list, err := mg.ListIPs(ctx, false, false)
 	require.NoError(t, err)
 	require.Len(t, list, 2)
 
@@ -27,7 +27,7 @@ func TestListIPS(t *testing.T) {
 	assert.Equal(t, "luna.mailgun.net", ip.RDNS)
 }
 
-func TestDomainIPS(t *testing.T) {
+func TestDomainIPs(t *testing.T) {
 	mg := mailgun.NewMailgun(testKey)
 	err := mg.SetAPIBase(server.URL())
 	require.NoError(t, err)
@@ -36,7 +36,7 @@ func TestDomainIPS(t *testing.T) {
 	err = mg.AddDomainIP(ctx, testDomain, "192.172.1.1")
 	require.NoError(t, err)
 
-	list, err := mg.ListDomainIPS(ctx, testDomain)
+	list, err := mg.ListDomainIPs(ctx, testDomain)
 	require.NoError(t, err)
 
 	require.Len(t, list, 1)
@@ -45,7 +45,7 @@ func TestDomainIPS(t *testing.T) {
 	err = mg.DeleteDomainIP(ctx, testDomain, "192.172.1.1")
 	require.NoError(t, err)
 
-	list, err = mg.ListDomainIPS(ctx, testDomain)
+	list, err = mg.ListDomainIPs(ctx, testDomain)
 	require.NoError(t, err)
 
 	require.Len(t, list, 0)
diff --git a/messages_test.go b/messages_test.go
index d668e97..008424a 100644
--- a/messages_test.go
+++ b/messages_test.go
@@ -29,7 +29,6 @@ Date: Thu, 6 Mar 2014 00:37:52 +0000
 Testing some Mailgun MIME awesomeness!
 `
 	templateText  = "Greetings %recipient.name%!  Your reserved seat is at table %recipient.table%."
-	exampleDomain = "testDomain"
 	exampleAPIKey = "testAPIKey"
 )
 
@@ -58,9 +57,9 @@ func TestSendMGPlain(t *testing.T) {
 
 		ctx := context.Background()
 		m := mailgun.NewMessage(fromUser, exampleSubject, exampleText, toUser)
-		msg, id, err := mg.Send(ctx, m)
+		resp, err := mg.Send(ctx, m)
 		require.NoError(t, err)
-		t.Log("TestSendPlain:MSG(" + msg + "),ID(" + id + ")")
+		t.Log("TestSendPlain:MSG(" + resp.Message + "),ID(" + resp.ID + ")")
 	})
 }
 
@@ -77,9 +76,9 @@ func TestSendMGPlainWithTracking(t *testing.T) {
 		ctx := context.Background()
 		m := mailgun.NewMessage(fromUser, exampleSubject, exampleText, toUser)
 		m.SetTracking(true)
-		msg, id, err := mg.Send(ctx, m)
+		resp, err := mg.Send(ctx, m)
 		require.NoError(t, err)
-		t.Log("TestSendPlainWithTracking:MSG(" + msg + "),ID(" + id + ")")
+		t.Log("TestSendPlainWithTracking:MSG(" + resp.Message + "),ID(" + resp.ID + ")")
 	})
 }
 
@@ -96,9 +95,9 @@ func TestSendMGPlainAt(t *testing.T) {
 		ctx := context.Background()
 		m := mailgun.NewMessage(fromUser, exampleSubject, exampleText, toUser)
 		m.SetDeliveryTime(time.Now().Add(5 * time.Minute))
-		msg, id, err := mg.Send(ctx, m)
+		resp, err := mg.Send(ctx, m)
 		require.NoError(t, err)
-		t.Log("TestSendPlainAt:MSG(" + msg + "),ID(" + id + ")")
+		t.Log("TestSendPlainAt:MSG(" + resp.Message + "),ID(" + resp.ID + ")")
 	})
 }
 
@@ -116,9 +115,9 @@ func TestSendMGSTO(t *testing.T) {
 		m := mailgun.NewMessage(fromUser, exampleSubject, exampleText, toUser)
 		err = m.SetSTOPeriod("24h")
 		require.NoError(t, err)
-		msg, id, err := mg.Send(ctx, m)
+		resp, err := mg.Send(ctx, m)
 		require.NoError(t, err)
-		t.Log("TestSendMGSTO:MSG(" + msg + "),ID(" + id + ")")
+		t.Log("TestSendMGSTO:MSG(" + resp.Message + "),ID(" + resp.ID + ")")
 	})
 }
 
@@ -135,9 +134,9 @@ func TestSendMGHtml(t *testing.T) {
 		ctx := context.Background()
 		m := mailgun.NewMessage(fromUser, exampleSubject, exampleText, toUser)
 		m.SetHTML(exampleHtml)
-		msg, id, err := mg.Send(ctx, m)
+		resp, err := mg.Send(ctx, m)
 		require.NoError(t, err)
-		t.Log("TestSendHtml:MSG(" + msg + "),ID(" + id + ")")
+		t.Log("TestSendHtml:MSG(" + resp.Message + "),ID(" + resp.Message + ")")
 	})
 }
 
@@ -155,9 +154,9 @@ func TestSendMGAMPHtml(t *testing.T) {
 		m := mailgun.NewMessage(fromUser, exampleSubject, exampleText, toUser)
 		m.SetHTML(exampleHtml)
 		m.SetAmpHTML(exampleAMPHtml)
-		msg, id, err := mg.Send(ctx, m)
+		resp, err := mg.Send(ctx, m)
 		require.NoError(t, err)
-		t.Log("TestSendHtml:MSG(" + msg + "),ID(" + id + ")")
+		t.Log("TestSendHtml:MSG(" + resp.Message + "),ID(" + resp.ID + ")")
 	})
 }
 
@@ -174,9 +173,9 @@ func TestSendMGTracking(t *testing.T) {
 		ctx := context.Background()
 		m := mailgun.NewMessage(fromUser, exampleSubject, exampleText+"Tracking!\n", toUser)
 		m.SetTracking(false)
-		msg, id, err := mg.Send(ctx, m)
+		resp, err := mg.Send(ctx, m)
 		require.NoError(t, err)
-		t.Log("TestSendTracking:MSG(" + msg + "),ID(" + id + ")")
+		t.Log("TestSendTracking:MSG(" + resp.Message + "),ID(" + resp.ID + ")")
 	})
 }
 
@@ -199,9 +198,9 @@ func TestSendMGTrackingClicksHtmlOnly(t *testing.T) {
 			TrackingOpens:  true,
 		}
 		m.SetTrackingOptions(&options)
-		msg, id, err := mg.Send(ctx, m)
+		resp, err := mg.Send(ctx, m)
 		require.NoError(t, err)
-		t.Log("TestSendHtml:MSG(" + msg + "),ID(" + id + ")")
+		t.Log("TestSendHtml:MSG(" + resp.Message + "),ID(" + resp.ID + ")")
 	})
 }
 
@@ -223,9 +222,9 @@ func TestSendMGTag(t *testing.T) {
 		require.NoError(t, err)
 		err = m.AddTag("BlortTag")
 		require.NoError(t, err)
-		msg, id, err := mg.Send(ctx, m)
+		resp, err := mg.Send(ctx, m)
 		require.NoError(t, err)
-		t.Log("TestSendTag:MSG(" + msg + "),ID(" + id + ")")
+		t.Log("TestSendTag:MSG(" + resp.Message + "),ID(" + resp.ID + ")")
 	})
 }
 
@@ -241,9 +240,9 @@ func TestSendMGMIME(t *testing.T) {
 
 		ctx := context.Background()
 		m := mailgun.NewMIMEMessage(os.Getenv("MG_DOMAIN"), io.NopCloser(strings.NewReader(exampleMime)), toUser)
-		msg, id, err := mg.Send(ctx, m)
+		resp, err := mg.Send(ctx, m)
 		require.NoError(t, err)
-		t.Log("TestSendMIME:MSG(" + msg + "),ID(" + id + ")")
+		t.Log("TestSendMIME:MSG(" + resp.Message + "),ID(" + resp.ID + ")")
 	})
 }
 
@@ -284,7 +283,7 @@ func TestSendMGBatchRecipientVariables(t *testing.T) {
 			"table": 42,
 		})
 		require.NoError(t, err)
-		_, _, err = mg.Send(ctx, m)
+		_, err = mg.Send(ctx, m)
 		require.NoError(t, err)
 	})
 }
@@ -317,10 +316,10 @@ func TestSendMGOffline(t *testing.T) {
 	ctx := context.Background()
 
 	m := mailgun.NewMessage(exampleDomain, fromUser, exampleSubject, exampleText, toUser)
-	msg, id, err := mg.Send(ctx, m)
+	resp, err := mg.Send(ctx, m)
 	require.NoError(t, err)
-	assert.Equal(t, exampleMessage, msg)
-	assert.Equal(t, exampleID, id)
+	assert.Equal(t, exampleMessage, resp.Message)
+	assert.Equal(t, exampleID, resp.ID)
 }
 
 func TestSendMGSeparateDomain(t *testing.T) {
@@ -353,10 +352,10 @@ func TestSendMGSeparateDomain(t *testing.T) {
 	m := mailgun.NewMessage(exampleDomain, fromUser, exampleSubject, exampleText, toUser)
 	m.AddDomain(signingDomain)
 
-	msg, id, err := mg.Send(ctx, m)
+	resp, err := mg.Send(ctx, m)
 	require.NoError(t, err)
-	assert.Equal(t, exampleMessage, msg)
-	assert.Equal(t, exampleID, id)
+	assert.Equal(t, exampleMessage, resp.Message)
+	assert.Equal(t, exampleID, resp.ID)
 }
 
 func TestSendMGMessageVariables(t *testing.T) {
@@ -414,10 +413,10 @@ func TestSendMGMessageVariables(t *testing.T) {
 	err = m.AddTemplateVariable("templateVariable", exampleTemplateVariable)
 	require.NoError(t, err)
 
-	msg, id, err := mg.Send(context.Background(), m)
+	resp, err := mg.Send(context.Background(), m)
 	require.NoError(t, err)
-	assert.Equal(t, exampleMessage, msg)
-	assert.Equal(t, exampleID, id)
+	assert.Equal(t, exampleMessage, resp.Message)
+	assert.Equal(t, exampleID, resp.ID)
 }
 
 func TestAddRecipientsError(t *testing.T) {
@@ -479,7 +478,7 @@ func TestSendDomainError(t *testing.T) {
 
 		m := mailgun.NewMessage(c.domain, fromUser, exampleSubject, exampleText, "test@test.com")
 
-		_, _, err = mg.Send(ctx, m)
+		_, err = mg.Send(ctx, m)
 		if c.isValid {
 			require.NoError(t, err)
 		} else {
@@ -505,7 +504,7 @@ func TestSendEOFError(t *testing.T) {
 	require.NoError(t, err)
 
 	m := mailgun.NewMessage(exampleDomain, fromUser, exampleSubject, exampleText, toUser)
-	_, _, err = mg.Send(context.Background(), m)
+	_, err = mg.Send(context.Background(), m)
 	require.NotNil(t, err)
 	// TODO(vtopc): do not compare strings, use errors.Is or errors.As:
 	require.Contains(t, err.Error(), "remote server prematurely closed connection: Post ")
@@ -532,19 +531,19 @@ func TestHasRecipient(t *testing.T) {
 
 	// No recipient
 	m := mailgun.NewMessage(exampleDomain, fromUser, exampleSubject, exampleText)
-	_, _, err = mg.Send(context.Background(), m)
+	_, err = mg.Send(context.Background(), m)
 	require.EqualError(t, err, "message not valid")
 
 	// Provided Bcc
 	m = mailgun.NewMessage(exampleDomain, fromUser, exampleSubject, exampleText)
 	m.AddBCC(recipient)
-	_, _, err = mg.Send(context.Background(), m)
+	_, err = mg.Send(context.Background(), m)
 	require.NoError(t, err)
 
 	// Provided cc
 	m = mailgun.NewMessage(exampleDomain, fromUser, exampleSubject, exampleText)
 	m.AddCC(recipient)
-	_, _, err = mg.Send(context.Background(), m)
+	_, err = mg.Send(context.Background(), m)
 	require.NoError(t, err)
 }
 
@@ -570,14 +569,14 @@ func TestResendStored(t *testing.T) {
 	err := mg.SetAPIBase(srv.URL)
 	require.NoError(t, err)
 
-	_, _, err = mg.ReSend(context.Background(), srv.URL+"/v3/some-url")
+	_, err = mg.ReSend(context.Background(), srv.URL+"/v3/some-url")
 	require.NotNil(t, err)
 	require.EqualError(t, err, "must provide at least one recipient")
 
-	msg, id, err := mg.ReSend(context.Background(), srv.URL+"/v3/some-url", toUser)
+	resp, err := mg.ReSend(context.Background(), srv.URL+"/v3/some-url", toUser)
 	require.NoError(t, err)
-	assert.Equal(t, exampleMessage, msg)
-	assert.Equal(t, exampleID, id)
+	assert.Equal(t, exampleMessage, resp.Message)
+	assert.Equal(t, exampleID, resp.ID)
 }
 
 func TestAddOverrideHeader(t *testing.T) {
@@ -610,10 +609,10 @@ func TestAddOverrideHeader(t *testing.T) {
 	m.SetRequireTLS(true)
 	m.SetSkipVerification(true)
 
-	msg, id, err := mg.Send(ctx, m)
+	resp, err := mg.Send(ctx, m)
 	require.NoError(t, err)
-	assert.Equal(t, exampleMessage, msg)
-	assert.Equal(t, exampleID, id)
+	assert.Equal(t, exampleMessage, resp.Message)
+	assert.Equal(t, exampleID, resp.ID)
 }
 
 func TestOnBehalfOfSubaccount(t *testing.T) {
@@ -648,10 +647,10 @@ func TestOnBehalfOfSubaccount(t *testing.T) {
 	m.SetRequireTLS(true)
 	m.SetSkipVerification(true)
 
-	msg, id, err := mg.Send(ctx, m)
+	resp, err := mg.Send(ctx, m)
 	require.NoError(t, err)
-	assert.Equal(t, exampleMessage, msg)
-	assert.Equal(t, exampleID, id)
+	assert.Equal(t, exampleMessage, resp.Message)
+	assert.Equal(t, exampleID, resp.ID)
 }
 
 func TestSendTLSOptions(t *testing.T) {
@@ -686,10 +685,10 @@ func TestSendTLSOptions(t *testing.T) {
 	m.SetRequireTLS(true)
 	m.SetSkipVerification(true)
 
-	msg, id, err := mg.Send(ctx, m)
+	resp, err := mg.Send(ctx, m)
 	require.NoError(t, err)
-	assert.Equal(t, exampleMessage, msg)
-	assert.Equal(t, exampleID, id)
+	assert.Equal(t, exampleMessage, resp.Message)
+	assert.Equal(t, exampleID, resp.ID)
 }
 
 func TestSendTemplate(t *testing.T) {
@@ -717,10 +716,10 @@ func TestSendTemplate(t *testing.T) {
 	m := mailgun.NewMessage(exampleDomain, fromUser, exampleSubject, "", toUser)
 	m.SetTemplate(templateName)
 
-	msg, id, err := mg.Send(ctx, m)
+	resp, err := mg.Send(ctx, m)
 	require.NoError(t, err)
-	assert.Equal(t, exampleMessage, msg)
-	assert.Equal(t, exampleID, id)
+	assert.Equal(t, exampleMessage, resp.Message)
+	assert.Equal(t, exampleID, resp.ID)
 }
 
 func TestSendTemplateOptions(t *testing.T) {
@@ -754,14 +753,14 @@ func TestSendTemplateOptions(t *testing.T) {
 	m.SetTemplateRenderText(true)
 	m.SetTemplateVersion(templateVersionTag)
 
-	msg, id, err := mg.Send(ctx, m)
+	resp, err := mg.Send(ctx, m)
 	require.NoError(t, err)
-	assert.Equal(t, exampleMessage, msg)
-	assert.Equal(t, exampleID, id)
+	assert.Equal(t, exampleMessage, resp.Message)
+	assert.Equal(t, exampleID, resp.ID)
 }
 
 func TestSendableMessageIface(t *testing.T) {
 	m := mailgun.NewMessage(domain, fromUser, exampleSubject, exampleText)
 
-	assert.Implements(t, (*mailgun.SendableMessage)(nil), m)
+	assert.Implements(t, (*mailgun.Message)(nil), m)
 }
diff --git a/storage_test.go b/storage_test.go
index 9922476..17314ce 100644
--- a/storage_test.go
+++ b/storage_test.go
@@ -20,22 +20,22 @@ func TestStorage(t *testing.T) {
 	var ctx = context.Background()
 
 	m := mailgun.NewMessage(testDomain, "root@"+testDomain, "Subject", "Text Body", "stored@"+testDomain)
-	msg, id, err := mg.Send(ctx, m)
+	resp, 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", resp.Message, resp.ID)
 
-	url, err := findStoredMessageURL(mg, strings.Trim(id, "<>"))
+	url, err := findStoredMessageURL(mg, strings.Trim(resp.ID, "<>"))
 	require.NoError(t, err)
 
-	resp, err := mg.GetStoredMessage(ctx, url)
+	stored, err := mg.GetStoredMessage(ctx, url)
 	require.NoError(t, err)
 
-	assert.Equal(t, "Subject", resp.Subject)
-	assert.Equal(t, "root@"+testDomain, resp.From)
-	assert.Equal(t, "stored@"+testDomain, resp.Recipients)
+	assert.Equal(t, "Subject", stored.Subject)
+	assert.Equal(t, "root@"+testDomain, stored.From)
+	assert.Equal(t, "stored@"+testDomain, stored.Recipients)
 
-	_, _, err = mg.ReSend(ctx, url, "resend@"+testDomain)
+	_, err = mg.ReSend(ctx, url, "resend@"+testDomain)
 	require.NoError(t, err)
 }
 
diff --git a/tags_test.go b/tags_test.go
index 2c622b6..8be996e 100644
--- a/tags_test.go
+++ b/tags_test.go
@@ -32,7 +32,7 @@ func TestTags(t *testing.T) {
 
 	ctx := context.Background()
 	// Create an email with some tags attached
-	_, _, err = mg.Send(ctx, msg)
+	_, err = mg.Send(ctx, msg)
 	require.NoError(t, err)
 
 	// Wait for the tag to show up