Repository: cloudflare/cloudflare-go
Pull request #1034 (2022-08-11 02:43:49)

Add support for Email routing
## Description

Adds support for the email routing APIs
https://api.cloudflare.com/#email-routing---destination-addresses-properties
https://api.cloudflare.com/#email-routing---routing-rules-properties
https://api.cloudflare.com/#email-routing---settings-properties

## Has your change been tested?

Unit tests added and all unit tests pass

## Types of changes

What sort of change does your code introduce/modify?

- [ ] Bug fix (non-breaking change which fixes an issue)
- [X] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:

- [X] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [X] I have added tests to cover my changes.
- [X] All new and existing tests passed.
- [X] This change is using publicly documented (api.cloudflare.com or developers.cloudflare.com) and stable APIs.



Patch:
diff --git a/.changelog/1010.txt b/.changelog/1010.txt
deleted file mode 100644
index df85b31f4..000000000
--- a/.changelog/1010.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:enhancement
-workers: Add support to upload module workers
-```
diff --git a/.changelog/1017.txt b/.changelog/1017.txt
deleted file mode 100644
index 56bd30386..000000000
--- a/.changelog/1017.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:enhancement
-lockdown: automatically paginate `List` results unless `Page` and `PerPage` are provided
-```
\ No newline at end of file
diff --git a/.changelog/1034.txt b/.changelog/1034.txt
new file mode 100644
index 000000000..bd57aca22
--- /dev/null
+++ b/.changelog/1034.txt
@@ -0,0 +1,9 @@
+```release-note:enhancement
+email_routing_destination: Adds support for the email routing destination API
+```
+```release-note:enhancement
+email_routing_rules: Adds support for the email routing rules API
+```
+```release-note:enhancement
+email_routing_settings: Adds support for the email routing settings API
+```
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 590715642..83a6e0176 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,12 +4,10 @@ ENHANCEMENTS:
 
 * filter: fix double endpoint calls & moving towards common method signature ([#1016](https://github.com/cloudflare/cloudflare-go/issues/1016))
 * firewall_rule: fix double endpoint calls & moving towards common method signature ([#1016](https://github.com/cloudflare/cloudflare-go/issues/1016))
-* lockdown: automatically paginate `List` results unless `Page` and `PerPage` are provided ([#1017](https://github.com/cloudflare/cloudflare-go/issues/1017))
 * r2: Add in support for creating and deleting R2 buckets ([#1028](https://github.com/cloudflare/cloudflare-go/issues/1028))
 * workers-account-settings: Add in support for Workers account settings API ([#1027](https://github.com/cloudflare/cloudflare-go/issues/1027))
 * workers-subdomain: Add in support Workers Subdomain API ([#1031](https://github.com/cloudflare/cloudflare-go/issues/1031))
 * workers-tail: Add in support for Workers tail API ([#1026](https://github.com/cloudflare/cloudflare-go/issues/1026))
-* workers: Add support to upload module workers ([#1010](https://github.com/cloudflare/cloudflare-go/issues/1010))
 
 DEPENDENCIES:
 
diff --git a/cmd/flarectl/zone.go b/cmd/flarectl/zone.go
index 5cd2c81a4..ad771b11c 100644
--- a/cmd/flarectl/zone.go
+++ b/cmd/flarectl/zone.go
@@ -133,25 +133,21 @@ func zoneCreateLockdown(c *cli.Context) error {
 			Value:  c.StringSlice("values")[index],
 		})
 	}
-	params := cloudflare.ZoneLockdownCreateParams{
+	lockdown := cloudflare.ZoneLockdown{
 		Description:    c.String("description"),
 		URLs:           c.StringSlice("urls"),
 		Configurations: zonelockdownconfigs,
 	}
 
-	resp, err := api.CreateZoneLockdown(context.Background(), cloudflare.ZoneIdentifier(zoneID), params)
+	var resp *cloudflare.ZoneLockdownResponse
+
+	resp, err = api.CreateZoneLockdown(context.Background(), zoneID, lockdown)
 	if err != nil {
 		fmt.Fprintln(os.Stderr, "Error creating ZONE lock down: ", err)
 		return err
 	}
-
 	output := make([][]string, 0, 1)
-
-	format := []string{
-		resp.ID,
-	}
-
-	output = append(output, format)
+	output = append(output, formatLockdownResponse(resp))
 
 	writeTable(c, output, "ID")
 
@@ -339,6 +335,12 @@ func formatCacheResponse(resp cloudflare.PurgeCacheResponse) []string {
 	}
 }
 
+func formatLockdownResponse(resp *cloudflare.ZoneLockdownResponse) []string {
+	return []string{
+		resp.Result.ID,
+	}
+}
+
 func zoneExport(c *cli.Context) error {
 	var zone string
 	if c.NArg() > 0 {
diff --git a/email_routing_destination.go b/email_routing_destination.go
new file mode 100644
index 000000000..f91bd593e
--- /dev/null
+++ b/email_routing_destination.go
@@ -0,0 +1,155 @@
+package cloudflare
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"time"
+)
+
+type EmailRoutingDestinationAddress struct {
+	Tag      string     `json:"tag,omitempty"`
+	Email    string     `json:"email,omitempty"`
+	Verified *time.Time `json:"verified,omitempty"`
+	Created  *time.Time `json:"created,omitempty"`
+	Modified *time.Time `json:"modified,omitempty"`
+}
+
+type ListEmailRoutingAddressParameters struct {
+	ResultInfo
+	Direction string `url:"direction,omitempty"`
+	Verified  *bool  `url:"verified,omitempty"`
+}
+
+type ListEmailRoutingAddressResponse struct {
+	Result     []EmailRoutingDestinationAddress `json:"result"`
+	ResultInfo `json:"result_info"`
+	Response
+}
+
+type CreateEmailRoutingAddressParameters struct {
+	Email string `json:"email,omitempty"`
+}
+
+type CreateEmailRoutingAddressResponse struct {
+	Result EmailRoutingDestinationAddress `json:"result,omitempty"`
+	Response
+}
+
+// ListEmailRoutingDestinationAddresses Lists existing destination addresses.
+//
+// API reference: https://api.cloudflare.com/#email-routing---destination-addresses-list-destination-addresses
+func (api *API) ListEmailRoutingDestinationAddresses(ctx context.Context, rc *ResourceContainer, params ListEmailRoutingAddressParameters) ([]EmailRoutingDestinationAddress, *ResultInfo, error) {
+	if rc.Identifier == "" {
+		return []EmailRoutingDestinationAddress{}, &ResultInfo{}, ErrMissingAccountID
+	}
+
+	autoPaginate := true
+	if params.PerPage >= 1 || params.Page >= 1 {
+		autoPaginate = false
+	}
+
+	if params.PerPage < 1 {
+		params.PerPage = 50
+	}
+
+	if params.Page < 1 {
+		params.Page = 1
+	}
+
+	var addresses []EmailRoutingDestinationAddress
+	var eResponse ListEmailRoutingAddressResponse
+	for {
+		uri := buildURI(fmt.Sprintf("/accounts/%s/email/routing/addresses", rc.Identifier), params)
+
+		res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+		if err != nil {
+			return []EmailRoutingDestinationAddress{}, &ResultInfo{}, err
+		}
+		err = json.Unmarshal(res, &eResponse)
+		if err != nil {
+			return []EmailRoutingDestinationAddress{}, &ResultInfo{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		}
+		addresses = append(addresses, eResponse.Result...)
+		params.ResultInfo = eResponse.ResultInfo.Next()
+		if params.ResultInfo.Done() || !autoPaginate {
+			break
+		}
+	}
+
+	return addresses, &eResponse.ResultInfo, nil
+}
+
+// CreateEmailRoutingDestinationAddress Create a destination address to forward your emails to.
+// Destination addresses need to be verified before they become active.
+//
+// API reference: https://api.cloudflare.com/#email-routing---destination-addresses-list-destination-addresses
+func (api *API) CreateEmailRoutingDestinationAddress(ctx context.Context, rc *ResourceContainer, params CreateEmailRoutingAddressParameters) (EmailRoutingDestinationAddress, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingDestinationAddress{}, ErrMissingAccountID
+	}
+
+	uri := fmt.Sprintf("/accounts/%s/email/routing/addresses", rc.Identifier)
+
+	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params)
+	if err != nil {
+		return EmailRoutingDestinationAddress{}, ErrMissingAccountID
+	}
+
+	var r CreateEmailRoutingAddressResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingDestinationAddress{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r.Result, nil
+}
+
+// GetEmailRoutingDestinationAddress Gets information for a specific destination email already created.
+//
+// API reference: https://api.cloudflare.com/#email-routing---destination-addresses-get-a-destination-address
+func (api *API) GetEmailRoutingDestinationAddress(ctx context.Context, rc *ResourceContainer, addressID string) (EmailRoutingDestinationAddress, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingDestinationAddress{}, ErrMissingAccountID
+	}
+
+	uri := fmt.Sprintf("/accounts/%s/email/routing/addresses/%s", rc.Identifier, addressID)
+
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return EmailRoutingDestinationAddress{}, ErrMissingAccountID
+	}
+
+	var r CreateEmailRoutingAddressResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingDestinationAddress{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r.Result, nil
+}
+
+// DeleteEmailRoutingDestinationAddress Gets information for a specific destination email already created.
+//
+// API reference: https://api.cloudflare.com/#email-routing---destination-addresses-delete-destination-address
+func (api *API) DeleteEmailRoutingDestinationAddress(ctx context.Context, rc *ResourceContainer, addressID string) (EmailRoutingDestinationAddress, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingDestinationAddress{}, ErrMissingAccountID
+	}
+
+	uri := fmt.Sprintf("/accounts/%s/email/routing/addresses/%s", rc.Identifier, addressID)
+
+	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
+	if err != nil {
+		return EmailRoutingDestinationAddress{}, ErrMissingAccountID
+	}
+
+	var r CreateEmailRoutingAddressResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingDestinationAddress{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r.Result, nil
+}
diff --git a/email_routing_rules.go b/email_routing_rules.go
new file mode 100644
index 000000000..97a12b2f5
--- /dev/null
+++ b/email_routing_rules.go
@@ -0,0 +1,270 @@
+package cloudflare
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+)
+
+var ErrMissingRuleID = errors.New("required rule id missing")
+
+type EmailRoutingRuleMatcher struct {
+	Type  string `json:"type,omitempty"`
+	Field string `json:"field,omitempty"`
+	Value string `json:"value,omitempty"`
+}
+
+type EmailRoutingRuleAction struct {
+	Type  string   `json:"type,omitempty"`
+	Value []string `json:"value,omitempty"`
+}
+
+type EmailRoutingRule struct {
+	Tag      string                    `json:"tag,omitempty"`
+	Name     string                    `json:"name,omitempty"`
+	Priority int                       `json:"priority,omitempty"`
+	Enabled  *bool                     `json:"enabled,omitempty"`
+	Matchers []EmailRoutingRuleMatcher `json:"matchers,omitempty"`
+	Actions  []EmailRoutingRuleAction  `json:"actions,omitempty"`
+}
+
+type ListEmailRoutingRulesParameters struct {
+	Enabled *bool `url:"enabled,omitempty"`
+	ResultInfo
+}
+
+type ListEmailRoutingRuleResponse struct {
+	Result     []EmailRoutingRule `json:"result"`
+	ResultInfo `json:"result_info,omitempty"`
+	Response
+}
+
+type CreateEmailRoutingRuleParameters struct {
+	Matchers []EmailRoutingRuleMatcher `json:"matchers,omitempty"`
+	Actions  []EmailRoutingRuleAction  `json:"actions,omitempty"`
+	Name     string                    `json:"name,omitempty"`
+	Enabled  *bool                     `json:"enabled,omitempty"`
+	Priority int                       `json:"priority,omitempty"`
+}
+
+type CreateEmailRoutingRuleResponse struct {
+	Result EmailRoutingRule `json:"result"`
+	Response
+}
+
+type GetEmailRoutingRuleResponse struct {
+	Result EmailRoutingRule `json:"result"`
+	Response
+}
+
+type UpdateEmailRoutingRuleParameters struct {
+	Matchers []EmailRoutingRuleMatcher `json:"matchers,omitempty"`
+	Actions  []EmailRoutingRuleAction  `json:"actions,omitempty"`
+	Name     string                    `json:"name,omitempty"`
+	Enabled  *bool                     `json:"enabled,omitempty"`
+	Priority int                       `json:"priority,omitempty"`
+	RuleID   string
+}
+
+type EmailRoutingCatchAllRule struct {
+	Tag      string                    `json:"tag,omitempty"`
+	Name     string                    `json:"name,omitempty"`
+	Enabled  *bool                     `json:"enabled,omitempty"`
+	Matchers []EmailRoutingRuleMatcher `json:"matchers,omitempty"`
+	Actions  []EmailRoutingRuleAction  `json:"actions,omitempty"`
+}
+
+// ListEmailRoutingRules Lists existing routing rules.
+//
+// API reference: https://api.cloudflare.com/#email-routing---routing-rules-list-routing-rules
+func (api *API) ListEmailRoutingRules(ctx context.Context, rc *ResourceContainer, params ListEmailRoutingRulesParameters) ([]EmailRoutingRule, *ResultInfo, error) {
+	if rc.Identifier == "" {
+		return []EmailRoutingRule{}, &ResultInfo{}, ErrMissingZoneID
+	}
+
+	autoPaginate := true
+	if params.PerPage >= 1 || params.Page >= 1 {
+		autoPaginate = false
+	}
+	if params.PerPage < 1 {
+		params.PerPage = 50
+	}
+	if params.Page < 1 {
+		params.Page = 1
+	}
+
+	var rules []EmailRoutingRule
+	var rResponse ListEmailRoutingRuleResponse
+	for {
+		uri := buildURI(fmt.Sprintf("/zones/%s/email/routing/rules", rc.Identifier), params)
+
+		res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+		if err != nil {
+			return []EmailRoutingRule{}, &ResultInfo{}, err
+		}
+
+		err = json.Unmarshal(res, &rResponse)
+		if err != nil {
+			return []EmailRoutingRule{}, &ResultInfo{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		}
+
+		rules = append(rules, rResponse.Result...)
+		params.ResultInfo = rResponse.ResultInfo.Next()
+		if params.ResultInfo.Done() || !autoPaginate {
+			break
+		}
+	}
+
+	return rules, &rResponse.ResultInfo, nil
+}
+
+// CreateEmailRoutingRule Create a new routing rule.
+// Rules consist of a custom email address paired with a destination address, that forward emails to the destination address you chose.
+//
+// API reference: https://api.cloudflare.com/#email-routing---routing-rules-create-routing-rule
+func (api *API) CreateEmailRoutingRule(ctx context.Context, rc *ResourceContainer, params CreateEmailRoutingRuleParameters) (EmailRoutingRule, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingRule{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/email/routing/rules", rc.Identifier)
+
+	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params)
+	if err != nil {
+		return EmailRoutingRule{}, err
+	}
+
+	var r CreateEmailRoutingRuleResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingRule{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r.Result, nil
+}
+
+// GetEmailRoutingRule Get information for a specific routing rule already created.
+//
+// API reference: https://api.cloudflare.com/#email-routing---routing-rules-get-routing-rule
+func (api *API) GetEmailRoutingRule(ctx context.Context, rc *ResourceContainer, ruleID string) (EmailRoutingRule, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingRule{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/email/routing/rules/%s", rc.Identifier, ruleID)
+
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return EmailRoutingRule{}, err
+	}
+
+	var r GetEmailRoutingRuleResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingRule{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r.Result, nil
+}
+
+// UpdateEmailRoutingRule Update actions, matches, or enable/disable specific routing rules
+//
+// API reference: https://api.cloudflare.com/#email-routing---routing-rules-update-routing-rule
+func (api *API) UpdateEmailRoutingRule(ctx context.Context, rc *ResourceContainer, params UpdateEmailRoutingRuleParameters) (EmailRoutingRule, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingRule{}, ErrMissingZoneID
+	}
+
+	if params.RuleID == "" {
+		return EmailRoutingRule{}, ErrMissingRuleID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/email/routing/rules/%s", rc.Identifier, params.RuleID)
+
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
+	if err != nil {
+		return EmailRoutingRule{}, err
+	}
+
+	var r GetEmailRoutingRuleResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingRule{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r.Result, nil
+}
+
+// DeleteEmailRoutingRule Delete a specific routing rule.
+//
+// API reference: https://api.cloudflare.com/#email-routing---routing-rules-delete-routing-rule
+func (api *API) DeleteEmailRoutingRule(ctx context.Context, rc *ResourceContainer, ruleID string) (EmailRoutingRule, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingRule{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/email/routing/rules/%s", rc.Identifier, ruleID)
+
+	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
+	if err != nil {
+		return EmailRoutingRule{}, err
+	}
+
+	var r GetEmailRoutingRuleResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingRule{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r.Result, nil
+}
+
+// GetEmailRoutingCatchAllRule Get information on the default catch-all routing rule.
+//
+// API reference: https://api.cloudflare.com/#email-routing---routing-rules-get-catch-all-rule
+func (api *API) GetEmailRoutingCatchAllRule(ctx context.Context, rc *ResourceContainer) (EmailRoutingCatchAllRule, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingCatchAllRule{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/email/routing/rules/catch_all", rc.Identifier)
+
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return EmailRoutingCatchAllRule{}, err
+	}
+
+	var r EmailRoutingCatchAllRule
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingCatchAllRule{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r, nil
+}
+
+// UpdateEmailRoutingCatchAllRule Enable or disable catch-all routing rule, or change action to forward to specific destination address.
+//
+// API reference: https://api.cloudflare.com/#email-routing---routing-rules-update-catch-all-rule
+func (api *API) UpdateEmailRoutingCatchAllRule(ctx context.Context, rc *ResourceContainer, params EmailRoutingCatchAllRule) (EmailRoutingCatchAllRule, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingCatchAllRule{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/email/routing/rules/catch_all", rc.Identifier)
+
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
+	if err != nil {
+		return EmailRoutingCatchAllRule{}, err
+	}
+
+	var r EmailRoutingCatchAllRule
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingCatchAllRule{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r, nil
+}
diff --git a/email_routing_settings.go b/email_routing_settings.go
new file mode 100644
index 000000000..b02be87f7
--- /dev/null
+++ b/email_routing_settings.go
@@ -0,0 +1,117 @@
+package cloudflare
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"time"
+)
+
+type EmailRoutingSettings struct {
+	Tag        string     `json:"tag,omitempty"`
+	Name       string     `json:"name,omitempty"`
+	Enabled    bool       `json:"enabled,omitempty"`
+	Created    *time.Time `json:"created,omitempty"`
+	Modified   *time.Time `json:"modified,omitempty"`
+	SkipWizard *bool      `json:"skip_wizard,omitempty"`
+	Status     string     `json:"status,omitempty"`
+}
+
+type EmailRoutingSettingsResponse struct {
+	Result EmailRoutingSettings `json:"result,omitempty"`
+	Response
+}
+
+type EmailRoutingDNSSettingsResponse struct {
+	Result []DNSRecord `json:"result,omitempty"`
+	Response
+}
+
+// GetEmailRoutingSettings Get information about the settings for your Email Routing zone.
+//
+// API reference: https://api.cloudflare.com/#email-routing---settings-get-email-routing-settings
+func (api *API) GetEmailRoutingSettings(ctx context.Context, rc *ResourceContainer) (EmailRoutingSettings, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingSettings{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/email/routing", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return EmailRoutingSettings{}, err
+	}
+
+	var r EmailRoutingSettingsResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingSettings{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r.Result, nil
+}
+
+// EnableEmailRouting Enable you Email Routing zone. Add and lock the necessary MX and SPF records.
+//
+// API reference: https://api.cloudflare.com/#email-routing---settings-enable-email-routing
+func (api *API) EnableEmailRouting(ctx context.Context, rc *ResourceContainer) (EmailRoutingSettings, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingSettings{}, ErrMissingZoneID
+	}
+	uri := fmt.Sprintf("/zones/%s/email/routing/enabled", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, nil)
+	if err != nil {
+		return EmailRoutingSettings{}, err
+	}
+
+	var r EmailRoutingSettingsResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingSettings{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r.Result, nil
+}
+
+// DisableEmailRouting Disable your Email Routing zone. Also removes additional MX records previously required for Email Routing to work.
+//
+// API reference: https://api.cloudflare.com/#email-routing---settings-enable-email-routing
+func (api *API) DisableEmailRouting(ctx context.Context, rc *ResourceContainer) (EmailRoutingSettings, error) {
+	if rc.Identifier == "" {
+		return EmailRoutingSettings{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/email/routing/disable", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, nil)
+	if err != nil {
+		return EmailRoutingSettings{}, err
+	}
+
+	var r EmailRoutingSettingsResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return EmailRoutingSettings{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+	return r.Result, nil
+}
+
+// GetEmailRoutingDNSSettings Show the DNS records needed to configure your Email Routing zone.
+//
+// API reference: https://api.cloudflare.com/#email-routing---settings-disable-email-routing
+func (api *API) GetEmailRoutingDNSSettings(ctx context.Context, rc *ResourceContainer) ([]DNSRecord, error) {
+	if rc.Identifier == "" {
+		return []DNSRecord{}, ErrMissingZoneID
+	}
+	uri := fmt.Sprintf("/zones/%s/email/routing/dns", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return []DNSRecord{}, err
+	}
+
+	var r EmailRoutingDNSSettingsResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return []DNSRecord{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+	return r.Result, nil
+}
diff --git a/lockdown.go b/lockdown.go
index bcbdd11f5..50009f796 100644
--- a/lockdown.go
+++ b/lockdown.go
@@ -5,6 +5,8 @@ import (
 	"encoding/json"
 	"fmt"
 	"net/http"
+	"net/url"
+	"strconv"
 	"time"
 )
 
@@ -44,145 +46,109 @@ type ZoneLockdownListResponse struct {
 	ResultInfo `json:"result_info"`
 }
 
-// ZoneLockdownCreateParams contains required and optional params
-// for creating a zone lockdown.
-type ZoneLockdownCreateParams struct {
-	Description    string               `json:"description"`
-	URLs           []string             `json:"urls"`
-	Configurations []ZoneLockdownConfig `json:"configurations"`
-	Paused         bool                 `json:"paused"`
-}
-
-// ZoneLockdownUpdateParams contains required and optional params
-// for updating a zone lockdown.
-type ZoneLockdownUpdateParams struct {
-	ID             string               `json:"id"`
-	Description    string               `json:"description"`
-	URLs           []string             `json:"urls"`
-	Configurations []ZoneLockdownConfig `json:"configurations"`
-	Paused         bool                 `json:"paused"`
-}
-
-type LockdownListParams struct {
-	ResultInfo
-}
-
 // CreateZoneLockdown creates a Zone ZoneLockdown rule for the given zone ID.
 //
 // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-create-a-ZoneLockdown-rule
-func (api *API) CreateZoneLockdown(ctx context.Context, rc *ResourceContainer, params ZoneLockdownCreateParams) (ZoneLockdown, error) {
-	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns", rc.Identifier)
-	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params)
+func (api *API) CreateZoneLockdown(ctx context.Context, zoneID string, ld ZoneLockdown) (*ZoneLockdownResponse, error) {
+	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns", zoneID)
+	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, ld)
 	if err != nil {
-		return ZoneLockdown{}, err
+		return nil, err
 	}
 
 	response := &ZoneLockdownResponse{}
 	err = json.Unmarshal(res, &response)
 	if err != nil {
-		return ZoneLockdown{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
 
-	return response.Result, nil
+	return response, nil
 }
 
-// UpdateZoneLockdown updates a Zone ZoneLockdown rule (based on the ID) for the given zone ID.
+// UpdateZoneLockdown updates a Zone ZoneLockdown rule (based on the ID) for the
+// given zone ID.
 //
 // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-update-ZoneLockdown-rule
-func (api *API) UpdateZoneLockdown(ctx context.Context, rc *ResourceContainer, params ZoneLockdownUpdateParams) (ZoneLockdown, error) {
-	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", rc.Identifier, params.ID)
-	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
+func (api *API) UpdateZoneLockdown(ctx context.Context, zoneID string, id string, ld ZoneLockdown) (*ZoneLockdownResponse, error) {
+	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, ld)
 	if err != nil {
-		return ZoneLockdown{}, err
+		return nil, err
 	}
 
 	response := &ZoneLockdownResponse{}
 	err = json.Unmarshal(res, &response)
 	if err != nil {
-		return ZoneLockdown{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
 
-	return response.Result, nil
+	return response, nil
 }
 
-// DeleteZoneLockdown deletes a Zone ZoneLockdown rule (based on the ID) for the given zone ID.
+// DeleteZoneLockdown deletes a Zone ZoneLockdown rule (based on the ID) for the
+// given zone ID.
 //
 // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-delete-ZoneLockdown-rule
-func (api *API) DeleteZoneLockdown(ctx context.Context, rc *ResourceContainer, id string) (ZoneLockdown, error) {
-	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", rc.Identifier, id)
+func (api *API) DeleteZoneLockdown(ctx context.Context, zoneID string, id string) (*ZoneLockdownResponse, error) {
+	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
 	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
 	if err != nil {
-		return ZoneLockdown{}, err
+		return nil, err
 	}
 
 	response := &ZoneLockdownResponse{}
 	err = json.Unmarshal(res, &response)
 	if err != nil {
-		return ZoneLockdown{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
 
-	return response.Result, nil
+	return response, nil
 }
 
-// ZoneLockdown retrieves a Zone ZoneLockdown rule (based on the ID) for the given zone ID.
+// ZoneLockdown retrieves a Zone ZoneLockdown rule (based on the ID) for the
+// given zone ID.
 //
 // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-ZoneLockdown-rule-details
-func (api *API) ZoneLockdown(ctx context.Context, rc *ResourceContainer, id string) (ZoneLockdown, error) {
-	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", rc.Identifier, id)
+func (api *API) ZoneLockdown(ctx context.Context, zoneID string, id string) (*ZoneLockdownResponse, error) {
+	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
 	if err != nil {
-		return ZoneLockdown{}, err
+		return nil, err
 	}
 
 	response := &ZoneLockdownResponse{}
 	err = json.Unmarshal(res, &response)
 	if err != nil {
-		return ZoneLockdown{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
 
-	return response.Result, nil
+	return response, nil
 }
 
-// ListZoneLockdowns retrieves every Zone ZoneLockdown rules for a given zone ID.
-//
-// Automatically paginates all results unless `params.PerPage` and `params.Page`
-// is set.
+// ListZoneLockdowns retrieves a list of Zone ZoneLockdown rules for a given
+// zone ID by page number.
 //
 // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-list-ZoneLockdown-rules
-func (api *API) ListZoneLockdowns(ctx context.Context, rc *ResourceContainer, params LockdownListParams) ([]ZoneLockdown, *ResultInfo, error) {
-	autoPaginate := true
-	if params.PerPage >= 1 || params.Page >= 1 {
-		autoPaginate = false
+func (api *API) ListZoneLockdowns(ctx context.Context, zoneID string, page int) (*ZoneLockdownListResponse, error) {
+	v := url.Values{}
+	if page <= 0 {
+		page = 1
 	}
-	if params.PerPage < 1 {
-		params.PerPage = 50
-	}
-	if params.Page < 1 {
-		params.Page = 1
-	}
-
-	var zoneLockdowns []ZoneLockdown
-	var zResponse ZoneLockdownListResponse
-	for {
-		uri := buildURI(fmt.Sprintf("/zones/%s/firewall/lockdowns", rc.Identifier), params)
 
-		res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
-		if err != nil {
-			return []ZoneLockdown{}, &ResultInfo{}, err
-		}
+	v.Set("page", strconv.Itoa(page))
+	v.Set("per_page", strconv.Itoa(100))
 
-		err = json.Unmarshal(res, &zResponse)
-		if err != nil {
-			return []ZoneLockdown{}, &ResultInfo{}, fmt.Errorf("failed to unmarshal filters JSON data: %w", err)
-		}
-
-		zoneLockdowns = append(zoneLockdowns, zResponse.Result...)
-		params.ResultInfo = zResponse.ResultInfo.Next()
+	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns?%s", zoneID, v.Encode())
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return nil, err
+	}
 
-		if params.ResultInfo.Done() || !autoPaginate {
-			break
-		}
+	response := &ZoneLockdownListResponse{}
+	err = json.Unmarshal(res, &response)
+	if err != nil {
+		return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
 
-	return zoneLockdowns, &zResponse.ResultInfo, nil
+	return response, nil
 }
diff --git a/workers.go b/workers.go
index 59db346e6..a1f812b25 100644
--- a/workers.go
+++ b/workers.go
@@ -26,10 +26,6 @@ type WorkerRequestParams struct {
 type WorkerScriptParams struct {
 	Script string
 
-	// Module changes the Content-Type header to specify the script is an
-	// ES Module syntax script.
-	Module bool
-
 	// Bindings should be a map where the keys are the binding name, and the
 	// values are the binding content
 	Bindings map[string]WorkerBinding
@@ -596,16 +592,11 @@ func (api *API) ListWorkerScripts(ctx context.Context) (WorkerListResponse, erro
 // UploadWorker push raw script content for your worker.
 //
 // API reference: https://api.cloudflare.com/#worker-script-upload-worker
-func (api *API) UploadWorker(ctx context.Context, requestParams *WorkerRequestParams, params *WorkerScriptParams) (WorkerScriptResponse, error) {
-	if params.Module {
-		return api.UploadWorkerWithBindings(ctx, requestParams, params)
-	}
-
-	contentType := "application/javascript"
+func (api *API) UploadWorker(ctx context.Context, requestParams *WorkerRequestParams, data string) (WorkerScriptResponse, error) {
 	if requestParams.ScriptName != "" {
-		return api.uploadWorkerWithName(ctx, requestParams.ScriptName, contentType, []byte(params.Script))
+		return api.uploadWorkerWithName(ctx, requestParams.ScriptName, "application/javascript", []byte(data))
 	}
-	return api.uploadWorkerForZone(ctx, requestParams.ZoneID, contentType, []byte(params.Script))
+	return api.uploadWorkerForZone(ctx, requestParams.ZoneID, "application/javascript", []byte(data))
 }
 
 // UploadWorkerWithBindings push raw script content and bindings for your worker
@@ -664,23 +655,15 @@ func formatMultipartBody(params *WorkerScriptParams) (string, []byte, error) {
 	defer mpw.Close()
 
 	// Write metadata part
-	var scriptPartName string
+	scriptPartName := "script"
 	meta := struct {
-		BodyPart   string              `json:"body_part,omitempty"`
-		MainModule string              `json:"main_module,omitempty"`
-		Bindings   []workerBindingMeta `json:"bindings"`
+		BodyPart string              `json:"body_part"`
+		Bindings []workerBindingMeta `json:"bindings"`
 	}{
+		BodyPart: scriptPartName,
 		Bindings: make([]workerBindingMeta, 0, len(params.Bindings)),
 	}
 
-	if params.Module {
-		scriptPartName = "worker.mjs"
-		meta.MainModule = scriptPartName
-	} else {
-		scriptPartName = "script"
-		meta.BodyPart = scriptPartName
-	}
-
 	bodyWriters := make([]workerBindingBodyWriter, 0, len(params.Bindings))
 	for name, b := range params.Bindings {
 		bindingMeta, bodyWriter, err := b.serialize(name)
@@ -710,16 +693,8 @@ func formatMultipartBody(params *WorkerScriptParams) (string, []byte, error) {
 
 	// Write script part
 	hdr = textproto.MIMEHeader{}
-
-	contentType := "application/javascript"
-	if params.Module {
-		contentType = "application/javascript+module"
-		hdr.Set("content-disposition", fmt.Sprintf(`form-data; name="%s"; filename="%[1]s"`, scriptPartName))
-	} else {
-		hdr.Set("content-disposition", fmt.Sprintf(`form-data; name="%s"`, scriptPartName))
-	}
-	hdr.Set("content-type", contentType)
-
+	hdr.Set("content-disposition", fmt.Sprintf(`form-data; name="%s"`, scriptPartName))
+	hdr.Set("content-type", "application/javascript")
 	pw, err = mpw.CreatePart(hdr)
 	if err != nil {
 		return "", nil, err


Test patch:
diff --git a/access_audit_log_test.go b/access_audit_log_test.go
index f851aefcd..b71c3f99b 100644
--- a/access_audit_log_test.go
+++ b/access_audit_log_test.go
@@ -24,7 +24,7 @@ func TestAccessAuditLogs(t *testing.T) {
   "result": [
     {
       "user_email": "michelle@example.com",
-      "ip_address": "198.41.129.166",
+      "ip_address": "198.51.100.1",
       "app_uid": "df7e2w5f-02b7-4d9d-af26-8d1988fca630",
       "app_domain": "test.example.com/admin",
       "action": "login",
@@ -43,7 +43,7 @@ func TestAccessAuditLogs(t *testing.T) {
 
 	want := []AccessAuditLogRecord{{
 		UserEmail:  "michelle@example.com",
-		IPAddress:  "198.41.129.166",
+		IPAddress:  "198.51.100.1",
 		AppUID:     "df7e2w5f-02b7-4d9d-af26-8d1988fca630",
 		AppDomain:  "test.example.com/admin",
 		Action:     "login",
diff --git a/api_token_test.go b/api_token_test.go
index 524b83e1d..a48759832 100644
--- a/api_token_test.go
+++ b/api_token_test.go
@@ -350,11 +350,11 @@ func TestUpdateAPIToken(t *testing.T) {
     "condition": {
       "request.ip": {
         "in": [
-          "199.27.128.0/21",
+          "198.51.100.0/24",
           "2400:cb00::/32"
         ],
         "not_in": [
-          "199.27.128.0/21",
+          "198.51.100.0/24",
           "2400:cb00::/32"
         ]
       }
@@ -395,8 +395,8 @@ func TestUpdateAPIToken(t *testing.T) {
 		},
 		Condition: &APITokenCondition{
 			RequestIP: &APITokenRequestIPCondition{
-				In:    []string{"199.27.128.0/21", "2400:cb00::/32"},
-				NotIn: []string{"199.27.128.0/21", "2400:cb00::/32"},
+				In:    []string{"198.51.100.0/24", "2400:cb00::/32"},
+				NotIn: []string{"198.51.100.0/24", "2400:cb00::/32"},
 			},
 		},
 	}
diff --git a/cloudflare_test.go b/cloudflare_test.go
index 89aaa90b3..ed5f2662e 100644
--- a/cloudflare_test.go
+++ b/cloudflare_test.go
@@ -153,7 +153,7 @@ func TestClient_RetryCanSucceedAfterErrors(t *testing.T) {
             "success": true,
             "errors": [],
             "messages": [],
-            "result": 
+            "result":
                 {
                     "id": "17b5962d775c646f3f9725cbc7a53df4",
                     "created_on": "2014-01-01T05:20:00.12345Z",
@@ -165,7 +165,7 @@ func TestClient_RetryCanSucceedAfterErrors(t *testing.T) {
                     "origins": [
                       {
                         "name": "app-server-1",
-                        "address": "0.0.0.0",
+                        "address": "198.51.100.1",
                         "enabled": true
                       }
                     ],
diff --git a/dns_example_test.go b/dns_example_test.go
index 8e72450c8..8f0a5f33c 100644
--- a/dns_example_test.go
+++ b/dns_example_test.go
@@ -41,8 +41,8 @@ func ExampleAPI_DNSRecords_filterByContent() {
 		log.Fatal(err)
 	}
 
-	// Fetch only records whose content is 127.0.0.1
-	localhost := cloudflare.DNSRecord{Content: "127.0.0.1"}
+	// Fetch only records whose content is 198.51.100.1
+	localhost := cloudflare.DNSRecord{Content: "198.51.100.1"}
 	recs, err := api.DNSRecords(context.Background(), zoneID, localhost)
 	if err != nil {
 		log.Fatal(err)
diff --git a/email_routing_destination_test.go b/email_routing_destination_test.go
new file mode 100644
index 000000000..b4451d366
--- /dev/null
+++ b/email_routing_destination_test.go
@@ -0,0 +1,171 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+const testEmailID = "ea95132c15732412d22c1476fa83f27a"
+
+func createTestDestinationAddress() EmailRoutingDestinationAddress {
+	verified, _ := time.Parse(time.RFC3339, "2014-01-02T02:20:00Z")
+	created, _ := time.Parse(time.RFC3339, "2014-01-02T02:20:00Z")
+	modified, _ := time.Parse(time.RFC3339, "2014-01-02T02:20:00Z")
+	return EmailRoutingDestinationAddress{
+		Tag:      testEmailID,
+		Email:    "user@example.com",
+		Verified: &verified,
+		Created:  &created,
+		Modified: &modified,
+	}
+}
+
+func TestEmailRouting_ListDestinationAddress(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/email/routing/addresses", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": [
+    {
+      "tag": "ea95132c15732412d22c1476fa83f27a",
+      "email": "user@example.com",
+      "verified": "2014-01-02T02:20:00Z",
+      "created": "2014-01-02T02:20:00Z",
+      "modified": "2014-01-02T02:20:00Z"
+    }
+  ],
+  "result_info": {
+    "page": 1,
+    "per_page": 20,
+    "count": 1,
+    "total_count": 1
+  }
+}`)
+	})
+
+	_, _, err := client.ListEmailRoutingDestinationAddresses(context.Background(), AccountIdentifier(""), ListEmailRoutingAddressParameters{})
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingAccountID, err)
+	}
+
+	want := createTestDestinationAddress()
+
+	res, resInfo, err := client.ListEmailRoutingDestinationAddresses(context.Background(), AccountIdentifier(testAccountID), ListEmailRoutingAddressParameters{})
+	if assert.NoError(t, err) {
+		assert.Equal(t, resInfo.Page, 1)
+		assert.Equal(t, want, res[0])
+	}
+}
+
+func TestEmailRouting_CreateDestinationAddress(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/email/routing/addresses", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": {
+    "tag": "ea95132c15732412d22c1476fa83f27a",
+    "email": "user@example.com",
+    "verified": "2014-01-02T02:20:00Z",
+    "created": "2014-01-02T02:20:00Z",
+    "modified": "2014-01-02T02:20:00Z"
+  }
+}`)
+	})
+
+	_, err := client.CreateEmailRoutingDestinationAddress(context.Background(), AccountIdentifier(""), CreateEmailRoutingAddressParameters{})
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingAccountID, err)
+	}
+
+	want := createTestDestinationAddress()
+
+	res, err := client.CreateEmailRoutingDestinationAddress(context.Background(), AccountIdentifier(testAccountID), CreateEmailRoutingAddressParameters{Email: "user@example.com"})
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
+
+func TestEmailRouting_GetDestinationAddress(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/email/routing/addresses/"+testEmailID, func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": {
+    "tag": "ea95132c15732412d22c1476fa83f27a",
+    "email": "user@example.com",
+    "verified": "2014-01-02T02:20:00Z",
+    "created": "2014-01-02T02:20:00Z",
+    "modified": "2014-01-02T02:20:00Z"
+  }
+}`)
+	})
+
+	_, err := client.GetEmailRoutingDestinationAddress(context.Background(), AccountIdentifier(""), "")
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingAccountID, err)
+	}
+
+	want := createTestDestinationAddress()
+
+	res, err := client.GetEmailRoutingDestinationAddress(context.Background(), AccountIdentifier(testAccountID), testEmailID)
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
+
+func TestEmailRouting_DeleteDestinationAddress(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/email/routing/addresses/"+testEmailID, func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": {
+    "tag": "ea95132c15732412d22c1476fa83f27a",
+    "email": "user@example.com",
+    "verified": "2014-01-02T02:20:00Z",
+    "created": "2014-01-02T02:20:00Z",
+    "modified": "2014-01-02T02:20:00Z"
+  }
+}`)
+	})
+
+	_, err := client.DeleteEmailRoutingDestinationAddress(context.Background(), AccountIdentifier(""), "")
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingAccountID, err)
+	}
+
+	want := createTestDestinationAddress()
+
+	res, err := client.DeleteEmailRoutingDestinationAddress(context.Background(), AccountIdentifier(testAccountID), testEmailID)
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
diff --git a/email_routing_rules_test.go b/email_routing_rules_test.go
new file mode 100644
index 000000000..502f88ef2
--- /dev/null
+++ b/email_routing_rules_test.go
@@ -0,0 +1,383 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+var testEmailRoutingRule = EmailRoutingRule{
+	Tag:      "a7e6fb77503c41d8a7f3113c6918f10c",
+	Name:     "Rule send to user@example.net",
+	Priority: 0,
+	Enabled:  BoolPtr(true),
+	Matchers: []EmailRoutingRuleMatcher{
+		{
+			Type:  "literal",
+			Field: "to",
+			Value: "test@example.com",
+		},
+	},
+	Actions: []EmailRoutingRuleAction{
+		{
+			Type:  "forward",
+			Value: []string{"destinationaddress@example.net"},
+		},
+	},
+}
+
+func TestEmailRouting_ListRoutingRules(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/email/routing/rules", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": [
+    {
+      "tag": "a7e6fb77503c41d8a7f3113c6918f10c",
+      "name": "Rule send to user@example.net",
+      "priority": 0,
+      "enabled": true,
+      "matchers": [
+        {
+          "type": "literal",
+          "field": "to",
+          "value": "test@example.com"
+        }
+      ],
+      "actions": [
+        {
+          "type": "forward",
+          "value": [
+            "destinationaddress@example.net"
+          ]
+        }
+      ]
+    }
+  ],
+  "result_info": {
+    "page": 1,
+    "per_page": 20,
+    "count": 1,
+    "total_count": 1
+  }
+}`)
+	})
+
+	_, _, err := client.ListEmailRoutingRules(context.Background(), AccountIdentifier(""), ListEmailRoutingRulesParameters{})
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingZoneID, err)
+	}
+
+	res, resInfo, err := client.ListEmailRoutingRules(context.Background(), AccountIdentifier(testZoneID), ListEmailRoutingRulesParameters{Enabled: BoolPtr(true)})
+	if assert.NoError(t, err) {
+		assert.Equal(t, resInfo.Page, 1)
+		assert.Equal(t, testEmailRoutingRule, res[0])
+	}
+}
+
+func TestEmailRouting_CreateRoutingRule(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/email/routing/rules", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": {
+    "tag": "a7e6fb77503c41d8a7f3113c6918f10c",
+    "name": "Rule send to user@example.net",
+    "priority": 0,
+    "enabled": true,
+    "matchers": [
+      {
+        "type": "literal",
+        "field": "to",
+        "value": "test@example.com"
+      }
+    ],
+    "actions": [
+      {
+        "type": "forward",
+        "value": [
+          "destinationaddress@example.net"
+        ]
+      }
+    ]
+  }
+}`)
+	})
+
+	_, err := client.CreateEmailRoutingRule(context.Background(), AccountIdentifier(""), CreateEmailRoutingRuleParameters{})
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingZoneID, err)
+	}
+
+	res, err := client.CreateEmailRoutingRule(context.Background(), AccountIdentifier(testZoneID), CreateEmailRoutingRuleParameters{Enabled: BoolPtr(true)})
+	if assert.NoError(t, err) {
+		assert.Equal(t, testEmailRoutingRule, res)
+	}
+}
+
+func TestEmailRouting_GetRoutingRule(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/email/routing/rules/a7e6fb77503c41d8a7f3113c6918f10c", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": {
+    "tag": "a7e6fb77503c41d8a7f3113c6918f10c",
+    "name": "Rule send to user@example.net",
+    "priority": 0,
+    "enabled": true,
+    "matchers": [
+      {
+        "type": "literal",
+        "field": "to",
+        "value": "test@example.com"
+      }
+    ],
+    "actions": [
+      {
+        "type": "forward",
+        "value": [
+          "destinationaddress@example.net"
+        ]
+      }
+    ]
+  }
+}`)
+	})
+
+	_, err := client.GetEmailRoutingRule(context.Background(), ZoneIdentifier(""), "")
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingZoneID, err)
+	}
+
+	res, err := client.GetEmailRoutingRule(context.Background(), AccountIdentifier(testZoneID), "a7e6fb77503c41d8a7f3113c6918f10c")
+	if assert.NoError(t, err) {
+		assert.Equal(t, testEmailRoutingRule, res)
+	}
+}
+
+func TestEmailRouting_UpdateRoutingRule(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/email/routing/rules/a7e6fb77503c41d8a7f3113c6918f10c", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": {
+    "tag": "a7e6fb77503c41d8a7f3113c6918f10c",
+    "name": "Rule send to user@example.net",
+    "priority": 0,
+    "enabled": true,
+    "matchers": [
+      {
+        "type": "literal",
+        "field": "to",
+        "value": "test@example.com"
+      }
+    ],
+    "actions": [
+      {
+        "type": "forward",
+        "value": [
+          "destinationaddress@example.net"
+        ]
+      }
+    ]
+  }
+}`)
+	})
+
+	_, err := client.UpdateEmailRoutingRule(context.Background(), ZoneIdentifier(""), UpdateEmailRoutingRuleParameters{})
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingZoneID, err)
+	}
+	_, err = client.UpdateEmailRoutingRule(context.Background(), ZoneIdentifier(testZoneID), UpdateEmailRoutingRuleParameters{})
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingRuleID, err)
+	}
+
+	res, err := client.UpdateEmailRoutingRule(context.Background(), AccountIdentifier(testZoneID), UpdateEmailRoutingRuleParameters{RuleID: "a7e6fb77503c41d8a7f3113c6918f10c"})
+	if assert.NoError(t, err) {
+		assert.Equal(t, testEmailRoutingRule, res)
+	}
+}
+
+func TestEmailRouting_DeleteRoutingRule(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/email/routing/rules/a7e6fb77503c41d8a7f3113c6918f10c", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": {
+    "tag": "a7e6fb77503c41d8a7f3113c6918f10c",
+    "name": "Rule send to user@example.net",
+    "priority": 0,
+    "enabled": true,
+    "matchers": [
+      {
+        "type": "literal",
+        "field": "to",
+        "value": "test@example.com"
+      }
+    ],
+    "actions": [
+      {
+        "type": "forward",
+        "value": [
+          "destinationaddress@example.net"
+        ]
+      }
+    ]
+  }
+}`)
+	})
+
+	_, err := client.DeleteEmailRoutingRule(context.Background(), ZoneIdentifier(""), "")
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingZoneID, err)
+	}
+
+	res, err := client.DeleteEmailRoutingRule(context.Background(), AccountIdentifier(testZoneID), "a7e6fb77503c41d8a7f3113c6918f10c")
+	if assert.NoError(t, err) {
+		assert.Equal(t, testEmailRoutingRule, res)
+	}
+}
+
+func TestEmailRouting_GetAllRule(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/email/routing/rules/catch_all", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodGet, r.Method, "Expected method 'PUT', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "tag": "a7e6fb77503c41d8a7f3113c6918f10c",
+  "name": "Rule send to user@example.net",
+  "enabled": true,
+  "matchers": [
+    {
+      "type": "all"
+    }
+  ],
+  "actions": [
+    {
+      "type": "forward",
+      "value": [
+        "destinationaddress@example.net"
+      ]
+    }
+  ]
+}`)
+	})
+
+	_, err := client.GetEmailRoutingCatchAllRule(context.Background(), ZoneIdentifier(""))
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingZoneID, err)
+	}
+
+	want := EmailRoutingCatchAllRule{
+		Tag:     "a7e6fb77503c41d8a7f3113c6918f10c",
+		Name:    "Rule send to user@example.net",
+		Enabled: BoolPtr(true),
+		Matchers: []EmailRoutingRuleMatcher{
+			{
+				Type: "all",
+			},
+		},
+		Actions: []EmailRoutingRuleAction{
+			{
+				Type:  "forward",
+				Value: []string{"destinationaddress@example.net"},
+			},
+		},
+	}
+
+	res, err := client.GetEmailRoutingCatchAllRule(context.Background(), AccountIdentifier(testZoneID))
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
+
+func TestEmailRouting_UpdateAllRule(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/email/routing/rules/catch_all", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "tag": "a7e6fb77503c41d8a7f3113c6918f10c",
+  "name": "Rule send to user@example.net",
+  "enabled": true,
+  "matchers": [
+    {
+      "type": "all"
+    }
+  ],
+  "actions": [
+    {
+      "type": "forward",
+      "value": [
+        "destinationaddress@example.net"
+      ]
+    }
+  ]
+}`)
+	})
+
+	_, err := client.UpdateEmailRoutingCatchAllRule(context.Background(), ZoneIdentifier(""), EmailRoutingCatchAllRule{})
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingZoneID, err)
+	}
+
+	want := EmailRoutingCatchAllRule{
+		Tag:     "a7e6fb77503c41d8a7f3113c6918f10c",
+		Name:    "Rule send to user@example.net",
+		Enabled: BoolPtr(true),
+		Matchers: []EmailRoutingRuleMatcher{
+			{
+				Type: "all",
+			},
+		},
+		Actions: []EmailRoutingRuleAction{
+			{
+				Type:  "forward",
+				Value: []string{"destinationaddress@example.net"},
+			},
+		},
+	}
+
+	res, err := client.UpdateEmailRoutingCatchAllRule(context.Background(), AccountIdentifier(testZoneID), EmailRoutingCatchAllRule{})
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
diff --git a/email_routing_settings_test.go b/email_routing_settings_test.go
new file mode 100644
index 000000000..6e937a69c
--- /dev/null
+++ b/email_routing_settings_test.go
@@ -0,0 +1,178 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func createTestEmailRoutingSettings() EmailRoutingSettings {
+	created, _ := time.Parse(time.RFC3339, "2014-01-02T02:20:00Z")
+	modified, _ := time.Parse(time.RFC3339, "2014-01-02T02:20:00Z")
+	return EmailRoutingSettings{
+		Tag:        "75610dab9e69410a82cf7e400a09ecec",
+		Name:       "example.net",
+		Enabled:    true,
+		Created:    &created,
+		Modified:   &modified,
+		SkipWizard: BoolPtr(true),
+		Status:     "read",
+	}
+}
+
+func TestEmailRouting_GetSettings(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/email/routing", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": {
+    "tag": "75610dab9e69410a82cf7e400a09ecec",
+    "name": "example.net",
+    "enabled": true,
+    "created": "2014-01-02T02:20:00Z",
+    "modified": "2014-01-02T02:20:00Z",
+    "skip_wizard": true,
+    "status": "read"
+  }
+}`)
+	})
+
+	_, err := client.GetEmailRoutingSettings(context.Background(), AccountIdentifier(""))
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingZoneID, err)
+	}
+
+	want := createTestEmailRoutingSettings()
+
+	res, err := client.GetEmailRoutingSettings(context.Background(), AccountIdentifier(testZoneID))
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
+
+func TestEmailRouting_Enable(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/email/routing/enabled", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": {
+    "tag": "75610dab9e69410a82cf7e400a09ecec",
+    "name": "example.net",
+    "enabled": true,
+    "created": "2014-01-02T02:20:00Z",
+    "modified": "2014-01-02T02:20:00Z",
+    "skip_wizard": true,
+    "status": "read"
+  }
+}`)
+	})
+
+	_, err := client.EnableEmailRouting(context.Background(), AccountIdentifier(""))
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingZoneID, err)
+	}
+
+	want := createTestEmailRoutingSettings()
+
+	res, err := client.EnableEmailRouting(context.Background(), AccountIdentifier(testZoneID))
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
+
+func TestEmailRouting_Disabled(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/email/routing/disable", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": {
+    "tag": "75610dab9e69410a82cf7e400a09ecec",
+    "name": "example.net",
+    "enabled": true,
+    "created": "2014-01-02T02:20:00Z",
+    "modified": "2014-01-02T02:20:00Z",
+    "skip_wizard": true,
+    "status": "read"
+  }
+}`)
+	})
+
+	_, err := client.DisableEmailRouting(context.Background(), AccountIdentifier(""))
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingZoneID, err)
+	}
+
+	want := createTestEmailRoutingSettings()
+
+	res, err := client.DisableEmailRouting(context.Background(), AccountIdentifier(testZoneID))
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
+
+func TestEmailRouting_DNSSettings(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/email/routing/dns", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodGet, r.Method, "Expected method 'DELETE', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": [
+    {
+      "type": "A",
+      "name": "example.com",
+      "content": "192.0.2.1",
+      "ttl": 3600,
+      "priority": 10
+    }
+  ]
+}`)
+	})
+
+	_, err := client.GetEmailRoutingDNSSettings(context.Background(), AccountIdentifier(""))
+	if assert.Error(t, err) {
+		assert.Equal(t, ErrMissingZoneID, err)
+	}
+
+	want := []DNSRecord{
+		{
+			Type:     "A",
+			Name:     "example.com",
+			Content:  "192.0.2.1",
+			TTL:      3600,
+			Priority: Uint16Ptr(10),
+		},
+	}
+
+	res, err := client.GetEmailRoutingDNSSettings(context.Background(), AccountIdentifier(testZoneID))
+	if assert.NoError(t, err) {
+		assert.Len(t, res, 1)
+		assert.Equal(t, want, res)
+	}
+}
diff --git a/filter_test.go b/filter_test.go
index 425801422..b36c7cc8e 100644
--- a/filter_test.go
+++ b/filter_test.go
@@ -21,7 +21,7 @@ func TestFilter(t *testing.T) {
 				"id": "b7ff25282d394be7b945e23c7106ce8a",
 				"paused": false,
 				"description": "Login from office",
-				"expression": "ip.src eq 127.0.0.1"
+				"expression": "ip.src eq 198.51.100.1"
 			},
 			"success": true,
 			"errors": null,
@@ -35,7 +35,7 @@ func TestFilter(t *testing.T) {
 		ID:          "b7ff25282d394be7b945e23c7106ce8a",
 		Paused:      false,
 		Description: "Login from office",
-		Expression:  "ip.src eq 127.0.0.1",
+		Expression:  "ip.src eq 198.51.100.1",
 	}
 
 	actual, err := client.Filter(context.Background(), ZoneIdentifier("d56084adb405e0b7e32c52321bf07be6"), "b7ff25282d394be7b945e23c7106ce8a")
@@ -146,7 +146,7 @@ func TestCreateSingleFilter(t *testing.T) {
 					"id": "b7ff25282d394be7b945e23c7106ce8a",
 					"paused": false,
 					"description": "Login from office",
-					"expression": "ip.src eq 127.0.0.1"
+					"expression": "ip.src eq 198.51.100.1"
 				}
 			],
 			"success": true,
@@ -162,7 +162,7 @@ func TestCreateSingleFilter(t *testing.T) {
 			ID:          "b7ff25282d394be7b945e23c7106ce8a",
 			Paused:      false,
 			Description: "Login from office",
-			Expression:  "ip.src eq 127.0.0.1",
+			Expression:  "ip.src eq 198.51.100.1",
 		},
 	}
 
@@ -171,7 +171,7 @@ func TestCreateSingleFilter(t *testing.T) {
 			ID:          "b7ff25282d394be7b945e23c7106ce8a",
 			Paused:      false,
 			Description: "Login from office",
-			Expression:  "ip.src eq 127.0.0.1",
+			Expression:  "ip.src eq 198.51.100.1",
 		},
 	}
 
@@ -195,7 +195,7 @@ func TestCreateMultipleFilters(t *testing.T) {
 					"id": "b7ff25282d394be7b945e23c7106ce8a",
 					"paused": false,
 					"description": "Login from office",
-					"expression": "ip.src eq 127.0.0.1"
+					"expression": "ip.src eq 198.51.100.1"
 				},
 				{
 					"id": "b7ff25282d394be7b945e23c7106ce8a",
@@ -217,7 +217,7 @@ func TestCreateMultipleFilters(t *testing.T) {
 			ID:          "b7ff25282d394be7b945e23c7106ce8a",
 			Paused:      false,
 			Description: "Login from office",
-			Expression:  "ip.src eq 127.0.0.1",
+			Expression:  "ip.src eq 198.51.100.1",
 		},
 		{
 			ID:          "b7ff25282d394be7b945e23c7106ce8a",
@@ -232,7 +232,7 @@ func TestCreateMultipleFilters(t *testing.T) {
 			ID:          "b7ff25282d394be7b945e23c7106ce8a",
 			Paused:      false,
 			Description: "Login from office",
-			Expression:  "ip.src eq 127.0.0.1",
+			Expression:  "ip.src eq 198.51.100.1",
 		},
 		{
 			ID:          "b7ff25282d394be7b945e23c7106ce8a",
@@ -311,7 +311,7 @@ func TestUpdateMultipleFilters(t *testing.T) {
 					"id": "c218c536b2bd406f958f278cf0fa8c0f",
 					"paused": false,
 					"description": "IP of example.com",
-					"expression": "ip.src ne 127.0.0.1"
+					"expression": "ip.src ne 198.51.100.1"
 				}
 			],
 			"success": true,
@@ -333,7 +333,7 @@ func TestUpdateMultipleFilters(t *testing.T) {
 			ID:          "c218c536b2bd406f958f278cf0fa8c0f",
 			Paused:      false,
 			Description: "IP of example.com",
-			Expression:  "ip.src ne 127.0.0.1",
+			Expression:  "ip.src ne 198.51.100.1",
 		},
 	}
 
@@ -348,7 +348,7 @@ func TestUpdateMultipleFilters(t *testing.T) {
 			ID:          "c218c536b2bd406f958f278cf0fa8c0f",
 			Paused:      false,
 			Description: "IP of example.com",
-			Expression:  "ip.src ne 127.0.0.1",
+			Expression:  "ip.src ne 198.51.100.1",
 		},
 	}
 
diff --git a/firewall_example_test.go b/firewall_example_test.go
index e6f08b3dc..571b61709 100644
--- a/firewall_example_test.go
+++ b/firewall_example_test.go
@@ -41,9 +41,9 @@ func ExampleAPI_ListZoneAccessRules_filterByIP() {
 		log.Fatal(err)
 	}
 
-	// Fetch only access rules whose target is 127.0.0.1
+	// Fetch only access rules whose target is 198.51.100.1
 	localhost := cloudflare.AccessRule{
-		Configuration: cloudflare.AccessRuleConfiguration{Target: "127.0.0.1"},
+		Configuration: cloudflare.AccessRuleConfiguration{Target: "198.51.100.1"},
 	}
 	response, err := api.ListZoneAccessRules(context.Background(), zoneID, localhost, 1)
 	if err != nil {
diff --git a/firewall_rules_test.go b/firewall_rules_test.go
index 9af55e41c..42db16192 100644
--- a/firewall_rules_test.go
+++ b/firewall_rules_test.go
@@ -191,7 +191,7 @@ func TestFirewallRule(t *testing.T) {
 				"priority":null,
 				"filter":{
 					"id":"b7ff25282d394be7b945e23c7106ce8a",
-					"expression":"ip.src in {127.0.0.1} ~ \"^.*/login.php$\")",
+					"expression":"ip.src in {198.51.100.1} ~ \"^.*/login.php$\")",
 					"paused":false,
 					"description":"Login from office"
 				}
@@ -212,7 +212,7 @@ func TestFirewallRule(t *testing.T) {
 		Priority:    nil,
 		Filter: Filter{
 			ID:          "b7ff25282d394be7b945e23c7106ce8a",
-			Expression:  "ip.src in {127.0.0.1} ~ \"^.*/login.php$\")",
+			Expression:  "ip.src in {198.51.100.1} ~ \"^.*/login.php$\")",
 			Paused:      false,
 			Description: "Login from office",
 		},
@@ -242,7 +242,7 @@ func TestCreateSingleFirewallRule(t *testing.T) {
 					"priority":null,
 					"filter":{
 						"id":"b7ff25282d394be7b945e23c7106ce8a",
-						"expression":"ip.src in {127.0.0.0/24}",
+						"expression":"ip.src in {198.51.100.0/24}",
 						"paused":false,
 						"description":"Login from office"
 					}
@@ -265,7 +265,7 @@ func TestCreateSingleFirewallRule(t *testing.T) {
 			Priority:    nil,
 			Filter: Filter{
 				ID:          "b7ff25282d394be7b945e23c7106ce8a",
-				Expression:  "ip.src in {127.0.0.0/24}",
+				Expression:  "ip.src in {198.51.100.0/24}",
 				Paused:      false,
 				Description: "Login from office",
 			},
@@ -281,7 +281,7 @@ func TestCreateSingleFirewallRule(t *testing.T) {
 			Priority:    nil,
 			Filter: Filter{
 				ID:          "b7ff25282d394be7b945e23c7106ce8a",
-				Expression:  "ip.src in {127.0.0.0/24}",
+				Expression:  "ip.src in {198.51.100.0/24}",
 				Paused:      false,
 				Description: "Login from office",
 			},
@@ -312,7 +312,7 @@ func TestCreateMultipleFirewallRules(t *testing.T) {
 					"priority":null,
 					"filter":{
 						"id":"b7ff25282d394be7b945e23c7106ce8a",
-						"expression":"ip.src in {127.0.0.0/24}",
+						"expression":"ip.src in {198.51.100.0/24}",
 						"paused":false,
 						"description":"Login from office"
 					}
@@ -348,7 +348,7 @@ func TestCreateMultipleFirewallRules(t *testing.T) {
 			Priority:    nil,
 			Filter: Filter{
 				ID:          "b7ff25282d394be7b945e23c7106ce8a",
-				Expression:  "ip.src in {127.0.0.0/24}",
+				Expression:  "ip.src in {198.51.100.0/24}",
 				Paused:      false,
 				Description: "Login from office",
 			},
@@ -377,7 +377,7 @@ func TestCreateMultipleFirewallRules(t *testing.T) {
 			Priority:    nil,
 			Filter: Filter{
 				ID:          "b7ff25282d394be7b945e23c7106ce8a",
-				Expression:  "ip.src in {127.0.0.0/24}",
+				Expression:  "ip.src in {198.51.100.0/24}",
 				Paused:      false,
 				Description: "Login from office",
 			},
@@ -507,7 +507,7 @@ func TestUpdateMultipleFirewallRules(t *testing.T) {
 					"priority":null,
 					"filter":{
 						"id":"b7ff25282d394be7b945e23c7106ce8a",
-						"expression":"ip.src in {127.0.0.0/24}",
+						"expression":"ip.src in {198.51.100.0/24}",
 						"paused":false,
 						"description":"Login from office"
 					}
@@ -543,7 +543,7 @@ func TestUpdateMultipleFirewallRules(t *testing.T) {
 			Priority:    nil,
 			Filter: Filter{
 				ID:          "b7ff25282d394be7b945e23c7106ce8a",
-				Expression:  "ip.src in {127.0.0.0/24}",
+				Expression:  "ip.src in {198.51.100.0/24}",
 				Paused:      false,
 				Description: "Login from office",
 			},
@@ -572,7 +572,7 @@ func TestUpdateMultipleFirewallRules(t *testing.T) {
 			Priority:    nil,
 			Filter: Filter{
 				ID:          "b7ff25282d394be7b945e23c7106ce8a",
-				Expression:  "ip.src in {127.0.0.0/24}",
+				Expression:  "ip.src in {198.51.100.0/24}",
 				Paused:      false,
 				Description: "Login from office",
 			},
diff --git a/ips_test.go b/ips_test.go
index c73b07e34..cc3b95930 100644
--- a/ips_test.go
+++ b/ips_test.go
@@ -50,7 +50,7 @@ func Test_IPs(t *testing.T) {
   "errors": [],
   "messages": [],
   "result": {
-    "ipv4_cidrs": ["199.27.128.0/21"],
+    "ipv4_cidrs": ["198.51.100.0/24"],
     "ipv6_cidrs": ["ffff:ffff::/32"],
     "china_colos": ["42.81.6.0/25", "2408:871a:1801:7::/72"]
   }
@@ -62,7 +62,7 @@ func Test_IPs(t *testing.T) {
 	assert.NoError(t, err)
 
 	assert.Len(t, ipRanges.IPv4CIDRs, 1)
-	assert.Equal(t, "199.27.128.0/21", ipRanges.IPv4CIDRs[0])
+	assert.Equal(t, "198.51.100.0/24", ipRanges.IPv4CIDRs[0])
 	assert.Len(t, ipRanges.IPv6CIDRs, 1)
 	assert.Equal(t, "ffff:ffff::/32", ipRanges.IPv6CIDRs[0])
 	assert.Len(t, ipRanges.ChinaIPv4CIDRs, 1)
diff --git a/load_balancing_test.go b/load_balancing_test.go
index 6651ea3d3..1aef80a8c 100644
--- a/load_balancing_test.go
+++ b/load_balancing_test.go
@@ -40,7 +40,7 @@ func TestCreateLoadBalancerPool(t *testing.T) {
               "origins": [
                 {
                   "name": "app-server-1",
-                  "address": "0.0.0.0",
+                  "address": "198.51.100.1",
                   "enabled": true,
                   "weight": 1,
                   "header": {
@@ -83,7 +83,7 @@ func TestCreateLoadBalancerPool(t *testing.T) {
               "origins": [
                 {
                   "name": "app-server-1",
-                  "address": "0.0.0.0",
+                  "address": "198.51.100.1",
                   "enabled": true,
                   "weight": 1,
                   "header": {
@@ -131,7 +131,7 @@ func TestCreateLoadBalancerPool(t *testing.T) {
 		Origins: []LoadBalancerOrigin{
 			{
 				Name:    "app-server-1",
-				Address: "0.0.0.0",
+				Address: "198.51.100.1",
 				Enabled: true,
 				Weight:  1,
 				Header: map[string][]string{
@@ -163,7 +163,7 @@ func TestCreateLoadBalancerPool(t *testing.T) {
 		Origins: []LoadBalancerOrigin{
 			{
 				Name:    "app-server-1",
-				Address: "0.0.0.0",
+				Address: "198.51.100.1",
 				Enabled: true,
 				Weight:  1,
 				Header: map[string][]string{
@@ -209,7 +209,7 @@ func TestListLoadBalancerPools(t *testing.T) {
                     "origins": [
                       {
                         "name": "app-server-1",
-                        "address": "0.0.0.0",
+                        "address": "198.51.100.1",
                         "enabled": true,
                         "weight": 1
                       }
@@ -244,7 +244,7 @@ func TestListLoadBalancerPools(t *testing.T) {
 			Origins: []LoadBalancerOrigin{
 				{
 					Name:    "app-server-1",
-					Address: "0.0.0.0",
+					Address: "198.51.100.1",
 					Enabled: true,
 					Weight:  1,
 				},
@@ -284,7 +284,7 @@ func TestLoadBalancerPoolDetails(t *testing.T) {
               "origins": [
                 {
                   "name": "app-server-1",
-                  "address": "0.0.0.0",
+                  "address": "198.51.100.1",
                   "enabled": true,
                   "weight": 1
                 }
@@ -311,7 +311,7 @@ func TestLoadBalancerPoolDetails(t *testing.T) {
 		Origins: []LoadBalancerOrigin{
 			{
 				Name:    "app-server-1",
-				Address: "0.0.0.0",
+				Address: "198.51.100.1",
 				Enabled: true,
 				Weight:  1,
 			},
@@ -371,7 +371,7 @@ func TestModifyLoadBalancerPool(t *testing.T) {
               "origins": [
                 {
                   "name": "app-server-2",
-                  "address": "0.0.0.1",
+                  "address": "198.51.100.2",
                   "enabled": false,
                   "weight": 1,
                   "header": {
@@ -404,7 +404,7 @@ func TestModifyLoadBalancerPool(t *testing.T) {
               "origins": [
                 {
                   "name": "app-server-2",
-                  "address": "0.0.0.1",
+                  "address": "198.51.100.2",
                   "enabled": false,
                   "weight": 1,
                   "header": {
@@ -438,7 +438,7 @@ func TestModifyLoadBalancerPool(t *testing.T) {
 		Origins: []LoadBalancerOrigin{
 			{
 				Name:    "app-server-2",
-				Address: "0.0.0.1",
+				Address: "198.51.100.2",
 				Enabled: false,
 				Weight:  1,
 				Header: map[string][]string{
@@ -462,7 +462,7 @@ func TestModifyLoadBalancerPool(t *testing.T) {
 		Origins: []LoadBalancerOrigin{
 			{
 				Name:    "app-server-2",
-				Address: "0.0.0.1",
+				Address: "198.51.100.2",
 				Enabled: false,
 				Weight:  1,
 				Header: map[string][]string{
diff --git a/lockdown_example_test.go b/lockdown_example_test.go
index 8df05e116..77f9f2294 100644
--- a/lockdown_example_test.go
+++ b/lockdown_example_test.go
@@ -21,12 +21,12 @@ func ExampleAPI_ListZoneLockdowns_all() {
 	}
 
 	// Fetch all Zone Lockdown rules for a zone, by page.
-	rules, _, err := api.ListZoneLockdowns(context.Background(), cloudflare.ZoneIdentifier(zoneID), cloudflare.LockdownListParams{})
+	rules, err := api.ListZoneLockdowns(context.Background(), zoneID, 1)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	for _, r := range rules {
+	for _, r := range rules.Result {
 		fmt.Printf("%s: %s\n", strings.Join(r.URLs, ", "), r.Configurations)
 	}
 }
@@ -42,7 +42,7 @@ func ExampleAPI_CreateZoneLockdown() {
 		log.Fatal(err)
 	}
 
-	newZoneLockdown := cloudflare.ZoneLockdownCreateParams{
+	newZoneLockdown := cloudflare.ZoneLockdown{
 		Description: "Test Zone Lockdown Rule",
 		URLs: []string{
 			"*.example.org/test",
@@ -50,13 +50,14 @@ func ExampleAPI_CreateZoneLockdown() {
 		Configurations: []cloudflare.ZoneLockdownConfig{
 			{
 				Target: "ip",
-				Value:  "127.0.0.1",
+				Value:  "198.51.100.1",
 			},
 		},
-		Paused: false,
+		Paused:   false,
+		Priority: 1,
 	}
 
-	response, err := api.CreateZoneLockdown(context.Background(), cloudflare.ZoneIdentifier(zoneID), newZoneLockdown)
+	response, err := api.CreateZoneLockdown(context.Background(), zoneID, newZoneLockdown)
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/lockdown_test.go b/lockdown_test.go
index cd4b7c050..b5d152b86 100644
--- a/lockdown_test.go
+++ b/lockdown_test.go
@@ -16,17 +16,7 @@ func TestCreateZoneLockdown(t *testing.T) {
 	setup()
 	defer teardown()
 
-	input := ZoneLockdownCreateParams{
-		Description: "Restrict access to these endpoints to requests from a known IP address",
-		URLs:        []string{"api.mysite.com/some/endpoint*"},
-		Configurations: []ZoneLockdownConfig{{
-			Target: "ip",
-			Value:  "198.51.100.4",
-		}},
-		Paused: false,
-	}
-
-	want := ZoneLockdown{
+	input := ZoneLockdown{
 		Description: "Restrict access to these endpoints to requests from a known IP address",
 		URLs:        []string{"api.mysite.com/some/endpoint*"},
 		Configurations: []ZoneLockdownConfig{{
@@ -42,7 +32,7 @@ func TestCreateZoneLockdown(t *testing.T) {
 		var v ZoneLockdown
 		err := json.NewDecoder(r.Body).Decode(&v)
 		require.NoError(t, err)
-		assert.Equal(t, want, v)
+		assert.Equal(t, input, v)
 
 		w.Header().Set("content-type", "application/json")
 		fmt.Fprint(w, `{
@@ -70,35 +60,35 @@ func TestCreateZoneLockdown(t *testing.T) {
 
 	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns", handler)
 
-	actual, err := client.CreateZoneLockdown(context.Background(), ZoneIdentifier(testZoneID), input)
+	actual, err := client.CreateZoneLockdown(context.Background(), testZoneID, input)
 	require.NoError(t, err)
 
-	want.ID = "372e67954025e0ba6aaa6d586b9e0b59"
 	createdOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
 	modifiedOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
-	want.CreatedOn = &createdOn
-	want.ModifiedOn = &modifiedOn
-
+	want := &ZoneLockdownResponse{
+		Result: ZoneLockdown{
+			ID:             "372e67954025e0ba6aaa6d586b9e0b59",
+			Description:    input.Description,
+			URLs:           input.URLs,
+			Configurations: input.Configurations,
+			Paused:         input.Paused,
+			CreatedOn:      &createdOn,
+			ModifiedOn:     &modifiedOn,
+		},
+		Response: Response{
+			Success:  true,
+			Errors:   []ResponseInfo{},
+			Messages: []ResponseInfo{},
+		},
+	}
 	assert.Equal(t, want, actual)
 }
 
-func TestUpdateZoneLockdown(t *testing.T) {
+func TestUpdateZoneLockdownt(t *testing.T) {
 	setup()
 	defer teardown()
 
-	input := ZoneLockdownUpdateParams{
-		ID:          "372e67954025e0ba6aaa6d586b9e0b59",
-		Description: "Restrict access to these endpoints to requests from a known IP address",
-		URLs:        []string{"api.mysite.com/some/endpoint*"},
-		Configurations: []ZoneLockdownConfig{{
-			Target: "ip",
-			Value:  "198.51.100.4",
-		}},
-		Paused: false,
-	}
-
-	want := ZoneLockdown{
-		ID:          "372e67954025e0ba6aaa6d586b9e0b59",
+	input := ZoneLockdown{
 		Description: "Restrict access to these endpoints to requests from a known IP address",
 		URLs:        []string{"api.mysite.com/some/endpoint*"},
 		Configurations: []ZoneLockdownConfig{{
@@ -114,7 +104,7 @@ func TestUpdateZoneLockdown(t *testing.T) {
 		var v ZoneLockdown
 		err := json.NewDecoder(r.Body).Decode(&v)
 		require.NoError(t, err)
-		assert.Equal(t, want, v)
+		assert.Equal(t, input, v)
 
 		w.Header().Set("content-type", "application/json")
 		fmt.Fprint(w, `{
@@ -140,16 +130,31 @@ func TestUpdateZoneLockdown(t *testing.T) {
 		}`)
 	}
 
-	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns/372e67954025e0ba6aaa6d586b9e0b59", handler)
+	zoneLockdownID := "372e67954025e0ba6aaa6d586b9e0b59"
+
+	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns/"+zoneLockdownID, handler)
 
-	actual, err := client.UpdateZoneLockdown(context.Background(), ZoneIdentifier(testZoneID), input)
+	actual, err := client.UpdateZoneLockdown(context.Background(), testZoneID, zoneLockdownID, input)
 	require.NoError(t, err)
 
 	createdOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
 	modifiedOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
-	want.CreatedOn = &createdOn
-	want.ModifiedOn = &modifiedOn
-
+	want := &ZoneLockdownResponse{
+		Result: ZoneLockdown{
+			ID:             zoneLockdownID,
+			Description:    input.Description,
+			URLs:           input.URLs,
+			Configurations: input.Configurations,
+			Paused:         input.Paused,
+			CreatedOn:      &createdOn,
+			ModifiedOn:     &modifiedOn,
+		},
+		Response: Response{
+			Success:  true,
+			Errors:   []ResponseInfo{},
+			Messages: []ResponseInfo{},
+		},
+	}
 	assert.Equal(t, want, actual)
 }
 
@@ -175,11 +180,18 @@ func TestDeleteZoneLockdown(t *testing.T) {
 
 	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns/"+zoneLockdownID, handler)
 
-	actual, err := client.DeleteZoneLockdown(context.Background(), ZoneIdentifier(testZoneID), zoneLockdownID)
+	actual, err := client.DeleteZoneLockdown(context.Background(), testZoneID, zoneLockdownID)
 	require.NoError(t, err)
 
-	want := ZoneLockdown{
-		ID: zoneLockdownID,
+	want := &ZoneLockdownResponse{
+		Result: ZoneLockdown{
+			ID: zoneLockdownID,
+		},
+		Response: Response{
+			Success:  true,
+			Errors:   []ResponseInfo{},
+			Messages: []ResponseInfo{},
+		},
 	}
 	assert.Equal(t, want, actual)
 }
@@ -219,22 +231,29 @@ func TestZoneLockdown(t *testing.T) {
 
 	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns/"+zoneLockdownID, handler)
 
-	actual, err := client.ZoneLockdown(context.Background(), ZoneIdentifier(testZoneID), zoneLockdownID)
+	actual, err := client.ZoneLockdown(context.Background(), testZoneID, zoneLockdownID)
 	require.NoError(t, err)
 
 	createdOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
 	modifiedOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
-	want := ZoneLockdown{
-		ID:          zoneLockdownID,
-		Description: "Restrict access to these endpoints to requests from a known IP address",
-		URLs:        []string{"api.mysite.com/some/endpoint*"},
-		Configurations: []ZoneLockdownConfig{{
-			Target: "ip",
-			Value:  "198.51.100.4",
-		}},
-		Paused:     false,
-		CreatedOn:  &createdOn,
-		ModifiedOn: &modifiedOn,
+	want := &ZoneLockdownResponse{
+		Result: ZoneLockdown{
+			ID:          zoneLockdownID,
+			Description: "Restrict access to these endpoints to requests from a known IP address",
+			URLs:        []string{"api.mysite.com/some/endpoint*"},
+			Configurations: []ZoneLockdownConfig{{
+				Target: "ip",
+				Value:  "198.51.100.4",
+			}},
+			Paused:     false,
+			CreatedOn:  &createdOn,
+			ModifiedOn: &modifiedOn,
+		},
+		Response: Response{
+			Success:  true,
+			Errors:   []ResponseInfo{},
+			Messages: []ResponseInfo{},
+		},
 	}
 	assert.Equal(t, want, actual)
 }
@@ -245,6 +264,7 @@ func TestListZoneLockdowns(t *testing.T) {
 
 	handler := func(w http.ResponseWriter, r *http.Request) {
 		assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s")
+		assert.Equal(t, "1", r.URL.Query().Get("page"))
 
 		w.Header().Set("content-type", "application/json")
 		fmt.Fprint(w, `{
@@ -280,23 +300,35 @@ func TestListZoneLockdowns(t *testing.T) {
 
 	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns", handler)
 
-	actual, _, err := client.ListZoneLockdowns(context.Background(), ZoneIdentifier(testZoneID), LockdownListParams{})
+	actual, err := client.ListZoneLockdowns(context.Background(), testZoneID, 1)
 	require.NoError(t, err)
 
 	createdOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
 	modifiedOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
-	want := []ZoneLockdown{{
-		ID:          "372e67954025e0ba6aaa6d586b9e0b59",
-		Description: "Restrict access to these endpoints to requests from a known IP address",
-		URLs:        []string{"api.mysite.com/some/endpoint*"},
-		Configurations: []ZoneLockdownConfig{{
-			Target: "ip",
-			Value:  "198.51.100.4",
+	want := &ZoneLockdownListResponse{
+		Result: []ZoneLockdown{{
+			ID:          "372e67954025e0ba6aaa6d586b9e0b59",
+			Description: "Restrict access to these endpoints to requests from a known IP address",
+			URLs:        []string{"api.mysite.com/some/endpoint*"},
+			Configurations: []ZoneLockdownConfig{{
+				Target: "ip",
+				Value:  "198.51.100.4",
+			}},
+			Paused:     false,
+			CreatedOn:  &createdOn,
+			ModifiedOn: &modifiedOn,
 		}},
-		Paused:     false,
-		CreatedOn:  &createdOn,
-		ModifiedOn: &modifiedOn,
-	},
+		Response: Response{
+			Success:  true,
+			Errors:   []ResponseInfo{},
+			Messages: []ResponseInfo{},
+		},
+		ResultInfo: ResultInfo{
+			Page:    1,
+			PerPage: 20,
+			Count:   1,
+			Total:   2000,
+		},
 	}
 	assert.Equal(t, want, actual)
 }
diff --git a/teams_locations_test.go b/teams_locations_test.go
index 5f88026b1..d37d336f3 100644
--- a/teams_locations_test.go
+++ b/teams_locations_test.go
@@ -28,7 +28,7 @@ func TestTeamsLocations(t *testing.T) {
 					"name": "home",
 					"networks": [
 						{
-							"network": "99.151.35.207/32",
+							"network": "198.51.100.1/32",
 							"id": "8e4c7835436345f0ab395429b187a076"
 						}
 					],
@@ -53,7 +53,7 @@ func TestTeamsLocations(t *testing.T) {
 	want := []TeamsLocation{{
 		ID:                    "0f8185414dec4a5e9034f3d917c17890",
 		Name:                  "home",
-		Networks:              []TeamsLocationNetwork{{ID: "8e4c7835436345f0ab395429b187a076", Network: "99.151.35.207/32"}},
+		Networks:              []TeamsLocationNetwork{{ID: "8e4c7835436345f0ab395429b187a076", Network: "198.51.100.1/32"}},
 		PolicyIDs:             []string{},
 		Ip:                    "2a06:98c1:54::2419",
 		Subdomain:             "q15l7x2lbw",
@@ -88,7 +88,7 @@ func TestTeamsLocation(t *testing.T) {
 				"name": "home",
 				"networks": [
 					{
-						"network": "99.151.35.207/32",
+						"network": "198.51.100.1/32",
 						"id": "8e4c7835436345f0ab395429b187a076"
 					}
 				],
@@ -111,7 +111,7 @@ func TestTeamsLocation(t *testing.T) {
 	want := TeamsLocation{
 		ID:                    id,
 		Name:                  "home",
-		Networks:              []TeamsLocationNetwork{{ID: "8e4c7835436345f0ab395429b187a076", Network: "99.151.35.207/32"}},
+		Networks:              []TeamsLocationNetwork{{ID: "8e4c7835436345f0ab395429b187a076", Network: "198.51.100.1/32"}},
 		PolicyIDs:             []string{},
 		Ip:                    "2a06:98c1:54::2419",
 		Subdomain:             "q15l7x2lbw",
@@ -146,7 +146,7 @@ func TestCreateTeamsLocation(t *testing.T) {
 				"name": "test",
 				"networks": [
 					{
-						"network": "99.151.35.207/32",
+						"network": "198.51.100.1/32",
 						"id": "8e4c7835436345f0ab395429b187a076"
 					}
 				],
@@ -169,7 +169,7 @@ func TestCreateTeamsLocation(t *testing.T) {
 	want := TeamsLocation{
 		ID:                    id,
 		Name:                  "test",
-		Networks:              []TeamsLocationNetwork{{ID: "8e4c7835436345f0ab395429b187a076", Network: "99.151.35.207/32"}},
+		Networks:              []TeamsLocationNetwork{{ID: "8e4c7835436345f0ab395429b187a076", Network: "198.51.100.1/32"}},
 		PolicyIDs:             []string{},
 		Ip:                    "2a06:98c1:54::2419",
 		Subdomain:             "q15l7x2lbw",
@@ -208,7 +208,7 @@ func TestUpdateTeamsLocation(t *testing.T) {
 				"name": "new",
 				"networks": [
 					{
-						"network": "99.151.35.207/32",
+						"network": "198.51.100.1/32",
 						"id": "8e4c7835436345f0ab395429b187a076"
 					}
 				],
@@ -231,7 +231,7 @@ func TestUpdateTeamsLocation(t *testing.T) {
 	want := TeamsLocation{
 		ID:                    id,
 		Name:                  "new",
-		Networks:              []TeamsLocationNetwork{{ID: "8e4c7835436345f0ab395429b187a076", Network: "99.151.35.207/32"}},
+		Networks:              []TeamsLocationNetwork{{ID: "8e4c7835436345f0ab395429b187a076", Network: "198.51.100.1/32"}},
 		PolicyIDs:             []string{},
 		Ip:                    "2a06:98c1:54::2419",
 		Subdomain:             "q15l7x2lbw",
diff --git a/testdata/fixtures/tunnel/multiple_full.json b/testdata/fixtures/tunnel/multiple_full.json
index 524c22f35..409911a8f 100644
--- a/testdata/fixtures/tunnel/multiple_full.json
+++ b/testdata/fixtures/tunnel/multiple_full.json
@@ -16,7 +16,7 @@
           "client_id": "dc6472cc-f1ae-44a0-b795-6b8a0ce29f90",
           "client_version": "2022.2.0",
           "opened_at": "2021-01-25T18:22:34.317854Z",
-          "origin_ip": "85.12.78.6"
+          "origin_ip": "198.51.100.1"
         }
       ]
     }
diff --git a/testdata/fixtures/tunnel/single_full.json b/testdata/fixtures/tunnel/single_full.json
index b447c75c2..5408bdb56 100644
--- a/testdata/fixtures/tunnel/single_full.json
+++ b/testdata/fixtures/tunnel/single_full.json
@@ -15,7 +15,7 @@
         "client_id": "dc6472cc-f1ae-44a0-b795-6b8a0ce29f90",
         "client_version": "2022.2.0",
         "opened_at": "2021-01-25T18:22:34.317854Z",
-        "origin_ip": "85.12.78.6"
+        "origin_ip": "198.51.100.1"
       }
     ]
   }
diff --git a/tunnel_test.go b/tunnel_test.go
index ab013a694..48f8f386c 100644
--- a/tunnel_test.go
+++ b/tunnel_test.go
@@ -36,7 +36,7 @@ func TestTunnels(t *testing.T) {
 			ClientID:           "dc6472cc-f1ae-44a0-b795-6b8a0ce29f90",
 			ClientVersion:      "2022.2.0",
 			OpenedAt:           "2021-01-25T18:22:34.317854Z",
-			OriginIP:           "85.12.78.6",
+			OriginIP:           "198.51.100.1",
 		}},
 	}}
 
@@ -73,7 +73,7 @@ func TestTunnel(t *testing.T) {
 			ClientID:           "dc6472cc-f1ae-44a0-b795-6b8a0ce29f90",
 			ClientVersion:      "2022.2.0",
 			OpenedAt:           "2021-01-25T18:22:34.317854Z",
-			OriginIP:           "85.12.78.6",
+			OriginIP:           "198.51.100.1",
 		}},
 	}
 
@@ -110,7 +110,7 @@ func TestCreateTunnel(t *testing.T) {
 			ClientID:           "dc6472cc-f1ae-44a0-b795-6b8a0ce29f90",
 			ClientVersion:      "2022.2.0",
 			OpenedAt:           "2021-01-25T18:22:34.317854Z",
-			OriginIP:           "85.12.78.6",
+			OriginIP:           "198.51.100.1",
 		}},
 	}
 
@@ -249,7 +249,7 @@ func TestTunnelConnections(t *testing.T) {
 						"client_id": "dc6472cc-f1ae-44a0-b795-6b8a0ce29f90",
 						"client_version": "2022.2.0",
 						"opened_at": "2021-01-25T18:22:34.317854Z",
-						"origin_ip": "85.12.78.6"
+						"origin_ip": "198.51.100.1"
 					}
 				]
 			}]
@@ -278,7 +278,7 @@ func TestTunnelConnections(t *testing.T) {
 				ClientID:           "dc6472cc-f1ae-44a0-b795-6b8a0ce29f90",
 				ClientVersion:      "2022.2.0",
 				OpenedAt:           "2021-01-25T18:22:34.317854Z",
-				OriginIP:           "85.12.78.6",
+				OriginIP:           "198.51.100.1",
 			}},
 		},
 	}
diff --git a/workers_example_test.go b/workers_example_test.go
index 2206a3504..d3350fa81 100644
--- a/workers_example_test.go
+++ b/workers_example_test.go
@@ -23,7 +23,7 @@ func ExampleAPI_UploadWorker() {
 		log.Fatal(err)
 	}
 
-	res, err := api.UploadWorker(context.Background(), &cloudflare.WorkerRequestParams{ZoneID: zoneID}, &cloudflare.WorkerScriptParams{Script: workerScript})
+	res, err := api.UploadWorker(context.Background(), &cloudflare.WorkerRequestParams{ZoneID: zoneID}, workerScript)
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -38,7 +38,7 @@ func UploadWorkerWithName() {
 		log.Fatal(err)
 	}
 
-	res, err := api.UploadWorker(context.Background(), &cloudflare.WorkerRequestParams{ScriptName: "baz"}, &cloudflare.WorkerScriptParams{Script: workerScript})
+	res, err := api.UploadWorker(context.Background(), &cloudflare.WorkerRequestParams{ScriptName: "baz"}, workerScript)
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/workers_test.go b/workers_test.go
index d21819c0c..becb32a63 100644
--- a/workers_test.go
+++ b/workers_test.go
@@ -5,7 +5,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
-	"mime/multipart"
 	"net/http"
 	"strings"
 	"testing"
@@ -32,18 +31,6 @@ const (
     "errors": [],
     "messages": []
 }`
-
-	uploadWorkerModuleResponseData = `{
-    "result": {
-        "script": "export default {\n    async fetch(request, env, event) {\n     event.passThroughOnException()\n    return fetch(request)\n    }\n}",
-        "etag": "279cf40d86d70b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43977a",
-        "size": 191,
-        "modified_on": "2018-06-09T15:17:01.989141Z"
-    },
-    "success": true,
-    "errors": [],
-    "messages": []
-}`
 	updateWorkerRouteResponse = `{
     "result": {
         "id": "e7a57d8746e74ae49c25994dadb421b1",
@@ -184,7 +171,6 @@ const (
 var (
 	successResponse               = Response{Success: true, Errors: []ResponseInfo{}, Messages: []ResponseInfo{}}
 	workerScript                  = "addEventListener('fetch', event => {\n    event.passThroughOnException()\nevent.respondWith(handleRequest(event.request))\n})\n\nasync function handleRequest(request) {\n    return fetch(request)\n}"
-	workerModuleScript            = "export default {\n    async fetch(request, env, event) {\n     event.passThroughOnException()\n    return fetch(request)\n    }\n}"
 	deleteWorkerRouteResponseData = createWorkerRouteResponse
 )
 
@@ -217,21 +203,6 @@ func getFormValue(r *http.Request, key string) ([]byte, error) {
 	return nil, fmt.Errorf("no value found for key %v", key)
 }
 
-func getFileDetails(r *http.Request, key string) (*multipart.FileHeader, error) {
-	err := r.ParseMultipartForm(1024 * 1024)
-	if err != nil {
-		return nil, err
-	}
-
-	fileHeaders := r.MultipartForm.File[key]
-
-	if len(fileHeaders) > 0 {
-		return fileHeaders[0], nil
-	}
-
-	return nil, fmt.Errorf("no value found for key %v", key)
-}
-
 type multipartUpload struct {
 	Script      string
 	BindingMeta map[string]workerBindingMeta
@@ -245,9 +216,8 @@ func parseMultipartUpload(r *http.Request) (multipartUpload, error) {
 	}
 
 	var metadata struct {
-		BodyPart   string              `json:"body_part,omitempty"`
-		MainModule string              `json:"main_module,omitempty"`
-		Bindings   []workerBindingMeta `json:"bindings"`
+		BodyPart string              `json:"body_part"`
+		Bindings []workerBindingMeta `json:"bindings"`
 	}
 	err = json.Unmarshal(mdBytes, &metadata)
 	if err != nil {
@@ -257,11 +227,7 @@ func parseMultipartUpload(r *http.Request) (multipartUpload, error) {
 	// Get the script
 	script, err := getFormValue(r, metadata.BodyPart)
 	if err != nil {
-		script, err = getFormValue(r, metadata.MainModule)
-
-		if err != nil {
-			return multipartUpload{}, err
-		}
+		return multipartUpload{}, err
 	}
 
 	// Since bindings are specified in the Go API as a map but are uploaded as a
@@ -413,7 +379,7 @@ func TestWorkers_UploadWorker(t *testing.T) {
 		w.Header().Set("content-type", "application/json")
 		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
 	})
-	res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, &WorkerScriptParams{Script: workerScript})
+	res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, workerScript)
 	formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z")
 	want := WorkerScriptResponse{
 		successResponse,
@@ -430,44 +396,6 @@ func TestWorkers_UploadWorker(t *testing.T) {
 	}
 }
 
-func TestWorkers_UploadWorkerAsModule(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/script", func(w http.ResponseWriter, r *http.Request) {
-		mpUpload, err := parseMultipartUpload(r)
-		assert.NoError(t, err)
-
-		assert.Equal(t, workerModuleScript, mpUpload.Script)
-
-		workerFileDetails, err := getFileDetails(r, "worker.mjs")
-		if !assert.NoError(t, err) {
-			assert.FailNow(t, "worker file not found in multipart form body")
-		}
-		contentTypeHeader := workerFileDetails.Header.Get("content-type")
-		expectedContentType := "application/javascript+module"
-		assert.Equal(t, expectedContentType, contentTypeHeader, "Expected content-type request header to be %s, got %s", expectedContentType, contentTypeHeader)
-
-		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerModuleResponseData) //nolint
-	})
-	res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, &WorkerScriptParams{Script: workerModuleScript, Module: true})
-	formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z")
-	want := WorkerScriptResponse{
-		successResponse,
-		WorkerScript{
-			Script: workerModuleScript,
-			WorkerMetaData: WorkerMetaData{
-				ETAG:       "279cf40d86d70b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43977a",
-				Size:       191,
-				ModifiedOn: formattedTime,
-			},
-		}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
 func TestWorkers_UploadWorkerWithName(t *testing.T) {
 	setup(UsingAccount("foo"))
 	defer teardown()
@@ -479,7 +407,7 @@ func TestWorkers_UploadWorkerWithName(t *testing.T) {
 		w.Header().Set("content-type", "application/json")
 		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
 	})
-	res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &WorkerScriptParams{Script: workerScript})
+	res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, workerScript)
 	formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z")
 	want := WorkerScriptResponse{
 		successResponse,
@@ -507,7 +435,7 @@ func TestWorkers_UploadWorkerSingleScriptWithAccount(t *testing.T) {
 		w.Header().Set("content-type", "application/json")
 		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
 	})
-	res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, &WorkerScriptParams{Script: workerScript})
+	res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, workerScript)
 	formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z")
 	want := WorkerScriptResponse{
 		successResponse,
@@ -528,7 +456,7 @@ func TestWorkers_UploadWorkerWithNameErrorsWithoutAccountId(t *testing.T) {
 	setup()
 	defer teardown()
 
-	_, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &WorkerScriptParams{Script: workerScript})
+	_, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, workerScript)
 	assert.Error(t, err)
 }
 
@@ -754,51 +682,6 @@ func TestWorkers_UploadWorkerWithPlainTextBinding(t *testing.T) {
 	assert.NoError(t, err)
 }
 
-func TestWorkers_UploadWorkerAsModuleWithPlainTextBinding(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	mux.HandleFunc("/accounts/foo/workers/scripts/bar", func(w http.ResponseWriter, r *http.Request) {
-		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
-
-		mpUpload, err := parseMultipartUpload(r)
-		assert.NoError(t, err)
-
-		expectedBindings := map[string]workerBindingMeta{
-			"b1": {
-				"name": "b1",
-				"type": "plain_text",
-				"text": "plain text value",
-			},
-		}
-		assert.Equal(t, workerModuleScript, mpUpload.Script)
-		assert.Equal(t, expectedBindings, mpUpload.BindingMeta)
-
-		workerFileDetails, err := getFileDetails(r, "worker.mjs")
-		if !assert.NoError(t, err) {
-			assert.FailNow(t, "worker file not found in multipart form body")
-		}
-		contentDispositonHeader := workerFileDetails.Header.Get("content-disposition")
-		expectedContentDisposition := fmt.Sprintf(`form-data; name="%s"; filename="%[1]s"`, "worker.mjs")
-		assert.Equal(t, expectedContentDisposition, contentDispositonHeader, "Expected content-disposition request header to be %s, got %s", expectedContentDisposition, contentDispositonHeader)
-
-		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerModuleResponseData) //nolint
-	})
-
-	scriptParams := WorkerScriptParams{
-		Script: workerModuleScript,
-		Module: true,
-		Bindings: map[string]WorkerBinding{
-			"b1": WorkerPlainTextBinding{
-				Text: "plain text value",
-			},
-		},
-	}
-	_, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams)
-	assert.NoError(t, err)
-}
-
 func TestWorkers_UploadWorkerWithSecretTextBinding(t *testing.T) {
 	setup(UsingAccount("foo"))
 	defer teardown()