Repository: cloudflare/cloudflare-go
Pull request #1137 (2022-11-30 04:03:10)

Modernise Worker methods and conventions
For the 4.x release of the Terraform provider, we are removing the global `api.AccountID` that is configured on the client due to ongoing issues with it (see cloudflare/terraform-provider-cloudflare#1646). Workers are the last set of methods that rely on this behaviour 🌟

In doing this, we're also modernising the methods to match our future conventions and method signatures [outlined for the experimental client](https://github.com/cloudflare/cloudflare-go/blob/master/docs/experimental.md). 

This _should_ be pretty well 1:1 functionality wise as everything Workers related is now accessible on the account resource level instead of the older split between zone/account for non-ent and enterprise configurations. The other major change here is that now the various parts of the Workers ecosystem are in their own files so that we can start to assign specific ownership rules instead of lumping it all together.

github-actions[bot]: Oops! It looks like no changelog entry is attached to this PR. Please include a release note as described in https://github.com/cloudflare/cloudflare-go/blob/master/docs/changelog-process.md.

Example: 

~~~
```release-note:TYPE
Release note
```
~~~

If you do not require a release note to be included, please add the `workflow/skip-changelog-entry` label.

Patch:
diff --git a/.changelog/1137.txt b/.changelog/1137.txt
new file mode 100644
index 000000000..b479d14d4
--- /dev/null
+++ b/.changelog/1137.txt
@@ -0,0 +1,39 @@
+```release-note:note
+workers: all worker methods have been split into product ownership(-ish) files
+```
+
+```release-note:note
+workers: all worker methods now require an explicit `ResourceContainer` for endpoints instead of relying on the globally defined `api.AccountID`
+```
+
+```release-note:breaking-change
+workers: method signatures have been updated to align with the upcoming client conventions
+```
+
+```release-note:breaking-change
+workers: API operations now target account level resources instead of older zone level resources (these are a 1:1 now)
+```
+
+```release-note:breaking-change
+workers_bindings: method signatures have been updated to align with the upcoming client conventions
+```
+
+```release-note:breaking-change
+workers_kv: method signatures have been updated to align with the upcoming client conventions
+```
+
+```release-note:breaking-change
+workers_tails: method signatures have been updated to align with the upcoming client conventions
+```
+
+```release-note:breaking-change
+workers_secrets: method signatures have been updated to align with the upcoming client conventions
+```
+
+```release-note:breaking-change
+workers_routes: method signatures have been updated to align with the upcoming client conventions
+```
+
+```release-note:breaking-change
+workers_cron_triggers: method signatures have been updated to align with the upcoming client conventions
+```
diff --git a/errors.go b/errors.go
index 994707978..723c18bae 100644
--- a/errors.go
+++ b/errors.go
@@ -29,7 +29,8 @@ const (
 	errAPIKeysAndTokensAreMutuallyExclusive   = "API keys and tokens are mutually exclusive" //nolint:gosec
 	errMissingCredentials                     = "no credentials provided"
 
-	errInvalidResourceContainerAccess = "requested resource container (%q) is not supported for this endpoint"
+	errInvalidResourceContainerAccess        = "requested resource container (%q) is not supported for this endpoint"
+	errRequiredAccountLevelResourceContainer = "this endpoint requires using an account level resource container and identifiers"
 )
 
 var (
@@ -40,6 +41,8 @@ var (
 	ErrAccountIDOrZoneIDAreRequired           = errors.New(errMissingAccountOrZoneID)
 	ErrAccountIDAndZoneIDAreMutuallyExclusive = errors.New(errAccountIDAndZoneIDAreMutuallyExclusive)
 	ErrMissingResourceIdentifier              = errors.New(errMissingResourceIdentifier)
+
+	ErrRequiredAccountLevelResourceContainer = errors.New(errRequiredAccountLevelResourceContainer)
 )
 
 type ErrorType string
diff --git a/workers.go b/workers.go
index 7bc6e9a42..d5474bc03 100644
--- a/workers.go
+++ b/workers.go
@@ -3,10 +3,7 @@ package cloudflare
 import (
 	"bytes"
 	"context"
-	rand "crypto/rand"
-	"encoding/hex"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"io"
 	"mime"
@@ -23,9 +20,22 @@ type WorkerRequestParams struct {
 	ScriptName string
 }
 
+type CreateWorkerParams struct {
+	ScriptName string
+	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
+}
+
 // WorkerScriptParams provides a worker script and the associated bindings.
 type WorkerScriptParams struct {
-	Script string
+	ScriptName string
 
 	// Module changes the Content-Type header to specify the script is an
 	// ES Module syntax script.
@@ -40,10 +50,9 @@ type WorkerScriptParams struct {
 //
 // API reference: https://api.cloudflare.com/#worker-routes-properties
 type WorkerRoute struct {
-	ID      string `json:"id,omitempty"`
-	Pattern string `json:"pattern"`
-	Enabled bool   `json:"enabled"` // this is deprecated: https://api.cloudflare.com/#worker-filters-deprecated--properties
-	Script  string `json:"script,omitempty"`
+	ID         string `json:"id,omitempty"`
+	Pattern    string `json:"pattern"`
+	ScriptName string `json:"script,omitempty"`
 }
 
 // WorkerRoutesResponse embeds Response struct and slice of WorkerRoutes.
@@ -77,6 +86,7 @@ type WorkerMetaData struct {
 // WorkerListResponse wrapper struct for API response to worker script list API call.
 type WorkerListResponse struct {
 	Response
+	ResultInfo
 	WorkerList []WorkerMetaData `json:"result"`
 }
 
@@ -87,375 +97,54 @@ type WorkerScriptResponse struct {
 	WorkerScript `json:"result"`
 }
 
-// WorkerBindingType represents a particular type of binding.
-type WorkerBindingType string
-
-func (b WorkerBindingType) String() string {
-	return string(b)
-}
-
-const (
-	// WorkerDurableObjectBindingType is the type for Durable Object bindings.
-	WorkerDurableObjectBindingType WorkerBindingType = "durable_object_namespace"
-	// WorkerInheritBindingType is the type for inherited bindings.
-	WorkerInheritBindingType WorkerBindingType = "inherit"
-	// WorkerKvNamespaceBindingType is the type for KV Namespace bindings.
-	WorkerKvNamespaceBindingType WorkerBindingType = "kv_namespace"
-	// WorkerWebAssemblyBindingType is the type for Web Assembly module bindings.
-	WorkerWebAssemblyBindingType WorkerBindingType = "wasm_module"
-	// WorkerSecretTextBindingType is the type for secret text bindings.
-	WorkerSecretTextBindingType WorkerBindingType = "secret_text"
-	// WorkerPlainTextBindingType is the type for plain text bindings.
-	WorkerPlainTextBindingType WorkerBindingType = "plain_text"
-	// WorkerServiceBindingType is the type for service bindings.
-	WorkerServiceBindingType WorkerBindingType = "service"
-	// WorkerR2BucketBindingType is the type for R2 bucket bindings.
-	WorkerR2BucketBindingType WorkerBindingType = "r2_bucket"
-	// WorkerAnalyticsEngineBindingType is the type for Analytics Engine dataset bindings.
-	WorkerAnalyticsEngineBindingType WorkerBindingType = "analytics_engine"
-)
-
-// WorkerBindingListItem a struct representing an individual binding in a list of bindings.
-type WorkerBindingListItem struct {
-	Name    string `json:"name"`
-	Binding WorkerBinding
-}
-
-// WorkerBindingListResponse wrapper struct for API response to worker binding list API call.
-type WorkerBindingListResponse struct {
-	Response
-	BindingList []WorkerBindingListItem
-}
-
-// Workers supports multiple types of bindings, e.g. KV namespaces or WebAssembly modules, and each type
-// of binding will be represented differently in the upload request body. At a high-level, every binding
-// will specify metadata, which is a JSON object with the properties "name" and "type". Some types of bindings
-// will also have additional metadata properties. For example, KV bindings also specify the KV namespace.
-// In addition to the metadata, some binding types may need to include additional data as part of the
-// multipart form. For example, WebAssembly bindings will include the contents of the WebAssembly module.
-
-// WorkerBinding is the generic interface implemented by all of
-// the various binding types.
-type WorkerBinding interface {
-	Type() WorkerBindingType
-
-	// serialize is responsible for returning the binding metadata as well as an optionally
-	// returning a function that can modify the multipart form body. For example, this is used
-	// by WebAssembly bindings to add a new part containing the WebAssembly module contents.
-	serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error)
-}
-
-// workerBindingMeta is the metadata portion of the binding.
-type workerBindingMeta = map[string]interface{}
-
-// workerBindingBodyWriter allows for a binding to add additional parts to the multipart body.
-type workerBindingBodyWriter func(*multipart.Writer) error
-
-// WorkerInheritBinding will just persist whatever binding content was previously uploaded.
-type WorkerInheritBinding struct {
-	// Optional parameter that allows for renaming a binding without changing
-	// its contents. If `OldName` is empty, the binding name will not be changed.
-	OldName string
-}
-
-// Type returns the type of the binding.
-func (b WorkerInheritBinding) Type() WorkerBindingType {
-	return WorkerInheritBindingType
-}
-
-func (b WorkerInheritBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
-	meta := workerBindingMeta{
-		"name": bindingName,
-		"type": b.Type(),
-	}
-
-	if b.OldName != "" {
-		meta["old_name"] = b.OldName
-	}
-
-	return meta, nil, nil
-}
-
-// WorkerKvNamespaceBinding is a binding to a Workers KV Namespace
-//
-// https://developers.cloudflare.com/workers/archive/api/resource-bindings/kv-namespaces/
-type WorkerKvNamespaceBinding struct {
-	NamespaceID string
-}
+type ListWorkersParams struct{}
 
-// Type returns the type of the binding.
-func (b WorkerKvNamespaceBinding) Type() WorkerBindingType {
-	return WorkerKvNamespaceBindingType
-}
-
-func (b WorkerKvNamespaceBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
-	if b.NamespaceID == "" {
-		return nil, nil, fmt.Errorf(`NamespaceID for binding "%s" cannot be empty`, bindingName)
-	}
-
-	return workerBindingMeta{
-		"name":         bindingName,
-		"type":         b.Type(),
-		"namespace_id": b.NamespaceID,
-	}, nil, nil
-}
-
-// WorkerDurableObjectBinding is a binding to a Workers Durable Object
-//
-// https://api.cloudflare.com/#durable-objects-namespace-properties
-type WorkerDurableObjectBinding struct {
-	ClassName  string
+type DeleteWorkerParams struct {
 	ScriptName string
 }
 
-// Type returns the type of the binding.
-func (b WorkerDurableObjectBinding) Type() WorkerBindingType {
-	return WorkerDurableObjectBindingType
-}
-
-func (b WorkerDurableObjectBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
-	if b.ClassName == "" {
-		return nil, nil, fmt.Errorf(`ClassName for binding "%s" cannot be empty`, bindingName)
-	}
-
-	return workerBindingMeta{
-		"name":        bindingName,
-		"type":        b.Type(),
-		"class_name":  b.ClassName,
-		"script_name": b.ScriptName,
-	}, nil, nil
-}
-
-// WorkerWebAssemblyBinding is a binding to a WebAssembly module
+// DeleteWorker deletes a single Worker.
 //
-// https://developers.cloudflare.com/workers/archive/api/resource-bindings/webassembly-modules/
-type WorkerWebAssemblyBinding struct {
-	Module io.Reader
-}
-
-// Type returns the type of the binding.
-func (b WorkerWebAssemblyBinding) Type() WorkerBindingType {
-	return WorkerWebAssemblyBindingType
-}
-
-func (b WorkerWebAssemblyBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
-	partName := getRandomPartName()
-
-	bodyWriter := func(mpw *multipart.Writer) error {
-		var hdr = textproto.MIMEHeader{}
-		hdr.Set("content-disposition", fmt.Sprintf(`form-data; name="%s"`, partName))
-		hdr.Set("content-type", "application/wasm")
-		pw, err := mpw.CreatePart(hdr)
-		if err != nil {
-			return err
-		}
-		_, err = io.Copy(pw, b.Module)
-		return err
-	}
-
-	return workerBindingMeta{
-		"name": bindingName,
-		"type": b.Type(),
-		"part": partName,
-	}, bodyWriter, nil
-}
-
-// WorkerPlainTextBinding is a binding to plain text
-//
-// https://developers.cloudflare.com/workers/tooling/api/scripts/#add-a-plain-text-binding
-type WorkerPlainTextBinding struct {
-	Text string
-}
-
-// Type returns the type of the binding.
-func (b WorkerPlainTextBinding) Type() WorkerBindingType {
-	return WorkerPlainTextBindingType
-}
-
-func (b WorkerPlainTextBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
-	if b.Text == "" {
-		return nil, nil, fmt.Errorf(`Text for binding "%s" cannot be empty`, bindingName)
-	}
-
-	return workerBindingMeta{
-		"name": bindingName,
-		"type": b.Type(),
-		"text": b.Text,
-	}, nil, nil
-}
-
-// WorkerSecretTextBinding is a binding to secret text
-//
-// https://developers.cloudflare.com/workers/tooling/api/scripts/#add-a-secret-text-binding
-type WorkerSecretTextBinding struct {
-	Text string
-}
-
-// Type returns the type of the binding.
-func (b WorkerSecretTextBinding) Type() WorkerBindingType {
-	return WorkerSecretTextBindingType
-}
-
-func (b WorkerSecretTextBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
-	if b.Text == "" {
-		return nil, nil, fmt.Errorf(`Text for binding "%s" cannot be empty`, bindingName)
-	}
-
-	return workerBindingMeta{
-		"name": bindingName,
-		"type": b.Type(),
-		"text": b.Text,
-	}, nil, nil
-}
-
-type WorkerServiceBinding struct {
-	Service     string
-	Environment *string
-}
-
-func (b WorkerServiceBinding) Type() WorkerBindingType {
-	return WorkerServiceBindingType
-}
-
-func (b WorkerServiceBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
-	if b.Service == "" {
-		return nil, nil, fmt.Errorf(`Service for binding "%s" cannot be empty`, bindingName)
-	}
-
-	meta := workerBindingMeta{
-		"name":    bindingName,
-		"type":    b.Type(),
-		"service": b.Service,
-	}
-
-	if b.Environment != nil {
-		meta["environment"] = *b.Environment
-	}
-
-	return meta, nil, nil
-}
-
-// WorkerR2BucketBinding is a binding to an R2 bucket.
-type WorkerR2BucketBinding struct {
-	BucketName string
-}
-
-// Type returns the type of the binding.
-func (b WorkerR2BucketBinding) Type() WorkerBindingType {
-	return WorkerR2BucketBindingType
-}
-
-func (b WorkerR2BucketBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
-	if b.BucketName == "" {
-		return nil, nil, fmt.Errorf(`BucketName for binding "%s" cannot be empty`, bindingName)
+// API reference: https://api.cloudflare.com/#worker-script-delete-worker
+func (api *API) DeleteWorker(ctx context.Context, rc *ResourceContainer, params DeleteWorkerParams) error {
+	if rc.Level != AccountRouteLevel {
+		return ErrRequiredAccountLevelResourceContainer
 	}
 
-	return workerBindingMeta{
-		"name":        bindingName,
-		"type":        b.Type(),
-		"bucket_name": b.BucketName,
-	}, nil, nil
-}
-
-// WorkerAnalyticsEngineBinding is a binding to an Analytics Engine dataset.
-type WorkerAnalyticsEngineBinding struct {
-	Dataset string
-}
-
-// Type returns the type of the binding.
-func (b WorkerAnalyticsEngineBinding) Type() WorkerBindingType {
-	return WorkerAnalyticsEngineBindingType
-}
-
-func (b WorkerAnalyticsEngineBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
-	if b.Dataset == "" {
-		return nil, nil, fmt.Errorf(`Dataset for binding "%s" cannot be empty`, bindingName)
+	if rc.Identifier == "" {
+		return ErrMissingAccountID
 	}
 
-	return workerBindingMeta{
-		"name":    bindingName,
-		"type":    b.Type(),
-		"dataset": b.Dataset,
-	}, nil, nil
-}
-
-// Each binding that adds a part to the multipart form body will need
-// a unique part name so we just generate a random 128bit hex string.
-func getRandomPartName() string {
-	randBytes := make([]byte, 16)
-	rand.Read(randBytes) //nolint:errcheck
-	return hex.EncodeToString(randBytes)
-}
-
-// DeleteWorker deletes worker for a zone.
-//
-// API reference: https://api.cloudflare.com/#worker-script-delete-worker
-func (api *API) DeleteWorker(ctx context.Context, requestParams *WorkerRequestParams) (WorkerScriptResponse, error) {
-	// if ScriptName is provided we will treat as org request
-	if requestParams.ScriptName != "" {
-		return api.deleteWorkerWithName(ctx, requestParams.ScriptName)
-	}
-	uri := fmt.Sprintf("/zones/%s/workers/script", requestParams.ZoneID)
+	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", rc.Identifier, params.ScriptName)
 	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
+
 	var r WorkerScriptResponse
 	if err != nil {
-		return r, err
+		return err
 	}
+
 	err = json.Unmarshal(res, &r)
 	if err != nil {
-		return r, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		return fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
-	return r, nil
+
+	return nil
 }
 
-// DeleteWorkerWithName deletes worker for a zone.
-// Sccount must be specified as api option https://godoc.org/github.com/cloudflare/cloudflare-go#UsingAccount
+// GetWorker fetch raw script content for your worker returns string containing
+// worker code js.
 //
 // API reference: https://developers.cloudflare.com/workers/tooling/api/scripts/
-func (api *API) deleteWorkerWithName(ctx context.Context, scriptName string) (WorkerScriptResponse, error) {
-	if api.AccountID == "" {
-		return WorkerScriptResponse{}, errors.New("account ID required")
+func (api *API) GetWorker(ctx context.Context, rc *ResourceContainer, scriptName string) (WorkerScriptResponse, error) {
+	if rc.Level != AccountRouteLevel {
+		return WorkerScriptResponse{}, ErrRequiredAccountLevelResourceContainer
 	}
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", api.AccountID, scriptName)
-	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
-	var r WorkerScriptResponse
-	if err != nil {
-		return r, err
-	}
-	err = json.Unmarshal(res, &r)
-	if err != nil {
-		return r, fmt.Errorf("%s: %w", errUnmarshalError, err)
-	}
-	return r, nil
-}
 
-// DownloadWorker fetch raw script content for your worker returns []byte containing worker code js
-//
-// API reference: https://api.cloudflare.com/#worker-script-download-worker
-func (api *API) DownloadWorker(ctx context.Context, requestParams *WorkerRequestParams) (WorkerScriptResponse, error) {
-	if requestParams.ScriptName != "" {
-		return api.downloadWorkerWithName(ctx, requestParams.ScriptName)
+	if rc.Identifier == "" {
+		return WorkerScriptResponse{}, ErrMissingAccountID
 	}
-	uri := fmt.Sprintf("/zones/%s/workers/script", requestParams.ZoneID)
-	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
-	var r WorkerScriptResponse
-	if err != nil {
-		return r, err
-	}
-	r.Script = string(res)
-	r.Module = false
-	r.Success = true
-	return r, nil
-}
 
-// DownloadWorkerWithName fetch raw script content for your worker returns string containing worker code js
-//
-// API reference: https://developers.cloudflare.com/workers/tooling/api/scripts/
-func (api *API) downloadWorkerWithName(ctx context.Context, scriptName string) (WorkerScriptResponse, error) {
-	if api.AccountID == "" {
-		return WorkerScriptResponse{}, errors.New("account ID required")
-	}
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", api.AccountID, scriptName)
+	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", rc.Identifier, scriptName)
 	res, err := api.makeRequestContextWithHeadersComplete(ctx, http.MethodGet, uri, nil, nil)
 	var r WorkerScriptResponse
 	if err != nil {
@@ -486,233 +175,78 @@ func (api *API) downloadWorkerWithName(ctx context.Context, scriptName string) (
 	return r, nil
 }
 
-// ListWorkerBindings returns all the bindings for a particular worker.
-func (api *API) ListWorkerBindings(ctx context.Context, requestParams *WorkerRequestParams) (WorkerBindingListResponse, error) {
-	if requestParams.ScriptName == "" {
-		return WorkerBindingListResponse{}, errors.New("ScriptName is required")
-	}
-	if api.AccountID == "" {
-		return WorkerBindingListResponse{}, errors.New("account ID required")
-	}
-
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/bindings", api.AccountID, requestParams.ScriptName)
-
-	var jsonRes struct {
-		Response
-		Bindings []workerBindingMeta `json:"result"`
-	}
-	var r WorkerBindingListResponse
-	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
-	if err != nil {
-		return r, err
-	}
-	err = json.Unmarshal(res, &jsonRes)
-	if err != nil {
-		return r, fmt.Errorf("%s: %w", errUnmarshalError, err)
-	}
-
-	r = WorkerBindingListResponse{
-		Response:    jsonRes.Response,
-		BindingList: make([]WorkerBindingListItem, 0, len(jsonRes.Bindings)),
-	}
-	for _, jsonBinding := range jsonRes.Bindings {
-		name, ok := jsonBinding["name"].(string)
-		if !ok {
-			return r, fmt.Errorf("Binding missing name %v", jsonBinding)
-		}
-		bType, ok := jsonBinding["type"].(string)
-		if !ok {
-			return r, fmt.Errorf("Binding missing type %v", jsonBinding)
-		}
-		bindingListItem := WorkerBindingListItem{
-			Name: name,
-		}
-
-		switch WorkerBindingType(bType) {
-		case WorkerDurableObjectBindingType:
-			class_name := jsonBinding["class_name"].(string)
-			script_name := jsonBinding["script_name"].(string)
-			bindingListItem.Binding = WorkerDurableObjectBinding{
-				ClassName:  class_name,
-				ScriptName: script_name,
-			}
-		case WorkerKvNamespaceBindingType:
-			namespaceID := jsonBinding["namespace_id"].(string)
-			bindingListItem.Binding = WorkerKvNamespaceBinding{
-				NamespaceID: namespaceID,
-			}
-		case WorkerWebAssemblyBindingType:
-			bindingListItem.Binding = WorkerWebAssemblyBinding{
-				Module: &bindingContentReader{
-					ctx:           ctx,
-					api:           api,
-					requestParams: requestParams,
-					bindingName:   name,
-				},
-			}
-		case WorkerPlainTextBindingType:
-			text := jsonBinding["text"].(string)
-			bindingListItem.Binding = WorkerPlainTextBinding{
-				Text: text,
-			}
-		case WorkerServiceBindingType:
-			service := jsonBinding["service"].(string)
-			environment := jsonBinding["environment"].(string)
-			bindingListItem.Binding = WorkerServiceBinding{
-				Service:     service,
-				Environment: &environment,
-			}
-		case WorkerSecretTextBindingType:
-			bindingListItem.Binding = WorkerSecretTextBinding{}
-		case WorkerR2BucketBindingType:
-			bucketName := jsonBinding["bucket_name"].(string)
-			bindingListItem.Binding = WorkerR2BucketBinding{
-				BucketName: bucketName,
-			}
-		case WorkerAnalyticsEngineBindingType:
-			dataset := jsonBinding["dataset"].(string)
-			bindingListItem.Binding = WorkerAnalyticsEngineBinding{
-				Dataset: dataset,
-			}
-		default:
-			bindingListItem.Binding = WorkerInheritBinding{}
-		}
-		r.BindingList = append(r.BindingList, bindingListItem)
-	}
-
-	return r, nil
-}
-
-// bindingContentReader is an io.Reader that will lazily load the
-// raw bytes for a binding from the API when the Read() method
-// is first called. This is only useful for binding types
-// that store raw bytes, like WebAssembly modules.
-type bindingContentReader struct {
-	api           *API
-	requestParams *WorkerRequestParams
-	ctx           context.Context
-	bindingName   string
-	content       []byte
-	position      int
-}
-
-func (b *bindingContentReader) Read(p []byte) (n int, err error) {
-	// Lazily load the content when Read() is first called
-	if b.content == nil {
-		uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/bindings/%s/content", b.api.AccountID, b.requestParams.ScriptName, b.bindingName)
-		res, err := b.api.makeRequestContext(b.ctx, http.MethodGet, uri, nil)
-		if err != nil {
-			return 0, err
-		}
-		b.content = res
-	}
-
-	if b.position >= len(b.content) {
-		return 0, io.EOF
-	}
-
-	bytesRemaining := len(b.content) - b.position
-	bytesToProcess := 0
-	if len(p) < bytesRemaining {
-		bytesToProcess = len(p)
-	} else {
-		bytesToProcess = bytesRemaining
+// ListWorkers returns list of Workers for given account.
+//
+// API reference: https://developers.cloudflare.com/workers/tooling/api/scripts/
+func (api *API) ListWorkers(ctx context.Context, rc *ResourceContainer, params ListWorkersParams) (WorkerListResponse, *ResultInfo, error) {
+	if rc.Level != AccountRouteLevel {
+		return WorkerListResponse{}, &ResultInfo{}, ErrRequiredAccountLevelResourceContainer
 	}
 
-	for i := 0; i < bytesToProcess; i++ {
-		p[i] = b.content[b.position]
-		b.position = b.position + 1
+	if rc.Identifier == "" {
+		return WorkerListResponse{}, &ResultInfo{}, ErrMissingAccountID
 	}
 
-	return bytesToProcess, nil
-}
-
-// ListWorkerScripts returns list of worker scripts for given account.
-//
-// API reference: https://developers.cloudflare.com/workers/tooling/api/scripts/
-func (api *API) ListWorkerScripts(ctx context.Context) (WorkerListResponse, error) {
-	if api.AccountID == "" {
-		return WorkerListResponse{}, errors.New("account ID required")
-	}
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts", api.AccountID)
+	uri := fmt.Sprintf("/accounts/%s/workers/scripts", rc.Identifier)
 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
 	if err != nil {
-		return WorkerListResponse{}, err
+		return WorkerListResponse{}, &ResultInfo{}, err
 	}
+
 	var r WorkerListResponse
 	err = json.Unmarshal(res, &r)
 	if err != nil {
-		return WorkerListResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		return WorkerListResponse{}, &ResultInfo{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
-	return r, nil
+
+	return r, &r.ResultInfo, nil
 }
 
-// UploadWorker push raw script content for your worker.
+// UploadWorker pushes 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)
+func (api *API) UploadWorker(ctx context.Context, rc *ResourceContainer, params CreateWorkerParams) (WorkerScriptResponse, error) {
+	if rc.Level != AccountRouteLevel {
+		return WorkerScriptResponse{}, ErrRequiredAccountLevelResourceContainer
 	}
 
-	contentType := "application/javascript"
-	if requestParams.ScriptName != "" {
-		return api.uploadWorkerWithName(ctx, requestParams.ScriptName, contentType, []byte(params.Script))
+	if rc.Identifier == "" {
+		return WorkerScriptResponse{}, ErrMissingAccountID
 	}
-	return api.uploadWorkerForZone(ctx, requestParams.ZoneID, contentType, []byte(params.Script))
-}
 
-// UploadWorkerWithBindings push raw script content and bindings for your worker
-//
-// API reference: https://api.cloudflare.com/#worker-script-upload-worker
-func (api *API) UploadWorkerWithBindings(ctx context.Context, requestParams *WorkerRequestParams, data *WorkerScriptParams) (WorkerScriptResponse, error) {
-	contentType, body, err := formatMultipartBody(data)
-	if err != nil {
-		return WorkerScriptResponse{}, err
-	}
-	if requestParams.ScriptName != "" {
-		return api.uploadWorkerWithName(ctx, requestParams.ScriptName, contentType, body)
-	}
-	return api.uploadWorkerForZone(ctx, requestParams.ZoneID, contentType, body)
-}
+	var (
+		contentType = "application/javascript"
+		err         error
+		body        []byte
+	)
 
-func (api *API) uploadWorkerForZone(ctx context.Context, zoneID, contentType string, body []byte) (WorkerScriptResponse, error) {
-	uri := fmt.Sprintf("/zones/%s/workers/script", zoneID)
-	headers := make(http.Header)
-	headers.Set("Content-Type", contentType)
-	res, err := api.makeRequestContextWithHeaders(ctx, http.MethodPut, uri, body, headers)
-	var r WorkerScriptResponse
-	if err != nil {
-		return r, err
-	}
-	err = json.Unmarshal(res, &r)
-	if err != nil {
-		return r, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	if params.Module || len(params.Bindings) > 0 {
+		contentType, body, err = formatMultipartBody(params)
+		if err != nil {
+			return WorkerScriptResponse{}, err
+		}
 	}
-	return r, nil
-}
 
-func (api *API) uploadWorkerWithName(ctx context.Context, scriptName, contentType string, body []byte) (WorkerScriptResponse, error) {
-	if api.AccountID == "" {
-		return WorkerScriptResponse{}, errors.New("account ID required")
-	}
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", api.AccountID, scriptName)
+	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", rc.Identifier, params.ScriptName)
 	headers := make(http.Header)
 	headers.Set("Content-Type", contentType)
 	res, err := api.makeRequestContextWithHeaders(ctx, http.MethodPut, uri, body, headers)
+
 	var r WorkerScriptResponse
 	if err != nil {
 		return r, err
 	}
+
 	err = json.Unmarshal(res, &r)
 	if err != nil {
 		return r, fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
+
 	return r, nil
 }
 
 // Returns content-type, body, error.
-func formatMultipartBody(params *WorkerScriptParams) (string, []byte, error) {
+func formatMultipartBody(params CreateWorkerParams) (string, []byte, error) {
 	var buf = &bytes.Buffer{}
 	var mpw = multipart.NewWriter(buf)
 	defer mpw.Close()
@@ -797,174 +331,3 @@ func formatMultipartBody(params *WorkerScriptParams) (string, []byte, error) {
 
 	return mpw.FormDataContentType(), buf.Bytes(), nil
 }
-
-// CreateWorkerRoute creates worker route for a zone
-//
-// API reference: https://api.cloudflare.com/#worker-filters-create-filter, https://api.cloudflare.com/#worker-routes-create-route
-func (api *API) CreateWorkerRoute(ctx context.Context, zoneID string, route WorkerRoute) (WorkerRouteResponse, error) {
-	pathComponent, err := getRouteEndpoint(route)
-	if err != nil {
-		return WorkerRouteResponse{}, err
-	}
-
-	uri := fmt.Sprintf("/zones/%s/workers/%s", zoneID, pathComponent)
-	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, route)
-	if err != nil {
-		return WorkerRouteResponse{}, err
-	}
-	var r WorkerRouteResponse
-	err = json.Unmarshal(res, &r)
-	if err != nil {
-		return WorkerRouteResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
-	}
-	return r, nil
-}
-
-// DeleteWorkerRoute deletes worker route for a zone
-//
-// API reference: https://api.cloudflare.com/#worker-routes-delete-route
-func (api *API) DeleteWorkerRoute(ctx context.Context, zoneID string, routeID string) (WorkerRouteResponse, error) {
-	uri := fmt.Sprintf("/zones/%s/workers/routes/%s", zoneID, routeID)
-	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
-	if err != nil {
-		return WorkerRouteResponse{}, err
-	}
-	var r WorkerRouteResponse
-	err = json.Unmarshal(res, &r)
-	if err != nil {
-		return WorkerRouteResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
-	}
-	return r, nil
-}
-
-// ListWorkerRoutes returns list of worker routes
-//
-// API reference: https://api.cloudflare.com/#worker-filters-list-filters, https://api.cloudflare.com/#worker-routes-list-routes
-func (api *API) ListWorkerRoutes(ctx context.Context, zoneID string) (WorkerRoutesResponse, error) {
-	pathComponent := "filters"
-	// Unfortunately we don't have a good signal of whether the user is wanting
-	// to use the deprecated filters endpoint (https://api.cloudflare.com/#worker-filters-list-filters)
-	// or the multi-script routes endpoint (https://api.cloudflare.com/#worker-script-list-workers)
-	//
-	// The filters endpoint does not support API tokens, so if an API token is specified we need to use
-	// the routes endpoint. Otherwise, since the multi-script API endpoints that operate on a script
-	// require an AccountID, we assume that anyone specifying an AccountID is using the routes endpoint.
-	// This is likely too presumptuous. In the next major version, we should just remove the deprecated
-	// filter endpoints entirely to avoid this ambiguity.
-	if api.AccountID != "" || api.APIToken != "" {
-		pathComponent = "routes"
-	}
-	uri := fmt.Sprintf("/zones/%s/workers/%s", zoneID, pathComponent)
-	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
-	if err != nil {
-		return WorkerRoutesResponse{}, err
-	}
-	var r WorkerRoutesResponse
-	err = json.Unmarshal(res, &r)
-	if err != nil {
-		return WorkerRoutesResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
-	}
-	for i := range r.Routes {
-		route := &r.Routes[i]
-		// The Enabled flag will not be set in the multi-script API response
-		// so we manually set it to true if the script name is not empty
-		// in case any multi-script customers rely on the Enabled field
-		if route.Script != "" {
-			route.Enabled = true
-		}
-	}
-	return r, nil
-}
-
-// GetWorkerRoute returns a worker route.
-//
-// API reference: https://api.cloudflare.com/#worker-routes-get-route
-func (api *API) GetWorkerRoute(ctx context.Context, zoneID string, routeID string) (WorkerRouteResponse, error) {
-	uri := fmt.Sprintf("/zones/%s/workers/routes/%s", zoneID, routeID)
-	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
-	if err != nil {
-		return WorkerRouteResponse{}, err
-	}
-	var r WorkerRouteResponse
-	err = json.Unmarshal(res, &r)
-	if err != nil {
-		return WorkerRouteResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
-	}
-	return r, nil
-}
-
-// UpdateWorkerRoute updates worker route for a zone.
-//
-// API reference: https://api.cloudflare.com/#worker-filters-update-filter, https://api.cloudflare.com/#worker-routes-update-route
-func (api *API) UpdateWorkerRoute(ctx context.Context, zoneID string, routeID string, route WorkerRoute) (WorkerRouteResponse, error) {
-	pathComponent, err := getRouteEndpoint(route)
-	if err != nil {
-		return WorkerRouteResponse{}, err
-	}
-	uri := fmt.Sprintf("/zones/%s/workers/%s/%s", zoneID, pathComponent, routeID)
-	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, route)
-	if err != nil {
-		return WorkerRouteResponse{}, err
-	}
-	var r WorkerRouteResponse
-	err = json.Unmarshal(res, &r)
-	if err != nil {
-		return WorkerRouteResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
-	}
-	return r, nil
-}
-
-func getRouteEndpoint(route WorkerRoute) (string, error) {
-	if route.Script != "" && route.Enabled {
-		return "", errors.New("Only `Script` or `Enabled` may be specified for a WorkerRoute, not both")
-	}
-
-	// For backwards-compatibility, fallback to the deprecated filter
-	// endpoint if Enabled == true
-	// https://api.cloudflare.com/#worker-filters-deprecated--properties
-	if route.Enabled {
-		return "filters", nil
-	}
-
-	return "routes", nil
-}
-
-type WorkerDomainParams struct {
-	ZoneID      string `json:"zone_id"`
-	Hostname    string `json:"hostname"`
-	Service     string `json:"service"`
-	Environment string `json:"environment,omitempty"`
-}
-
-type WorkerDomainResult struct {
-	ID          string `json:"id"`
-	ZoneID      string `json:"zone_id"`
-	ZoneName    string `json:"zone_name"`
-	Hostname    string `json:"hostname"`
-	Service     string `json:"service"`
-	Environment string `json:"environment"`
-}
-
-type WorkerDomainResponse struct {
-	Response
-	WorkerDomainResult `json:"result"`
-}
-
-// AttachWorkerToDomain attaches a worker to a zone and hostname
-//
-// API reference: https://api.cloudflare.com/#worker-domain-attach-to-domain
-func (api *API) AttachWorkerToDomain(ctx context.Context, rc *ResourceContainer, params *WorkerDomainParams) (WorkerDomainResponse, error) {
-	uri := fmt.Sprintf("/accounts/%s/workers/domains", rc.Identifier)
-	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
-	if err != nil {
-		return WorkerDomainResponse{}, err
-	}
-
-	var r WorkerDomainResponse
-	err = json.Unmarshal(res, &r)
-	if err != nil {
-		return WorkerDomainResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
-	}
-
-	return r, nil
-}
diff --git a/workers_account_settings.go b/workers_account_settings.go
index d8e58516a..6b3b65d6d 100644
--- a/workers_account_settings.go
+++ b/workers_account_settings.go
@@ -37,6 +37,10 @@ func (api *API) CreateWorkersAccountSettings(ctx context.Context, rc *ResourceCo
 		return WorkersAccountSettings{}, ErrMissingAccountID
 	}
 
+	if rc.Level != AccountRouteLevel {
+		return WorkersAccountSettings{}, ErrRequiredAccountLevelResourceContainer
+	}
+
 	uri := fmt.Sprintf("/accounts/%s/workers/account-settings", rc.Identifier)
 	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
 	if err != nil {
@@ -59,6 +63,10 @@ func (api *API) WorkersAccountSettings(ctx context.Context, rc *ResourceContaine
 		return WorkersAccountSettings{}, ErrMissingAccountID
 	}
 
+	if rc.Level != AccountRouteLevel {
+		return WorkersAccountSettings{}, ErrRequiredAccountLevelResourceContainer
+	}
+
 	uri := fmt.Sprintf("/accounts/%s/workers/account-settings", rc.Identifier)
 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, params)
 	if err != nil {
diff --git a/workers_bindings.go b/workers_bindings.go
new file mode 100644
index 000000000..963106589
--- /dev/null
+++ b/workers_bindings.go
@@ -0,0 +1,466 @@
+package cloudflare
+
+import (
+	"context"
+	rand "crypto/rand"
+	"encoding/hex"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io"
+	"mime/multipart"
+	"net/http"
+	"net/textproto"
+)
+
+// WorkerBindingType represents a particular type of binding.
+type WorkerBindingType string
+
+func (b WorkerBindingType) String() string {
+	return string(b)
+}
+
+const (
+	// WorkerDurableObjectBindingType is the type for Durable Object bindings.
+	WorkerDurableObjectBindingType WorkerBindingType = "durable_object_namespace"
+	// WorkerInheritBindingType is the type for inherited bindings.
+	WorkerInheritBindingType WorkerBindingType = "inherit"
+	// WorkerKvNamespaceBindingType is the type for KV Namespace bindings.
+	WorkerKvNamespaceBindingType WorkerBindingType = "kv_namespace"
+	// WorkerWebAssemblyBindingType is the type for Web Assembly module bindings.
+	WorkerWebAssemblyBindingType WorkerBindingType = "wasm_module"
+	// WorkerSecretTextBindingType is the type for secret text bindings.
+	WorkerSecretTextBindingType WorkerBindingType = "secret_text"
+	// WorkerPlainTextBindingType is the type for plain text bindings.
+	WorkerPlainTextBindingType WorkerBindingType = "plain_text"
+	// WorkerServiceBindingType is the type for service bindings.
+	WorkerServiceBindingType WorkerBindingType = "service"
+	// WorkerR2BucketBindingType is the type for R2 bucket bindings.
+	WorkerR2BucketBindingType WorkerBindingType = "r2_bucket"
+	// WorkerAnalyticsEngineBindingType is the type for Analytics Engine dataset bindings.
+	WorkerAnalyticsEngineBindingType WorkerBindingType = "analytics_engine"
+)
+
+type ListWorkerBindingsParams struct {
+	ScriptName string
+}
+
+// WorkerBindingListItem a struct representing an individual binding in a list of bindings.
+type WorkerBindingListItem struct {
+	Name    string `json:"name"`
+	Binding WorkerBinding
+}
+
+// WorkerBindingListResponse wrapper struct for API response to worker binding list API call.
+type WorkerBindingListResponse struct {
+	Response
+	BindingList []WorkerBindingListItem
+}
+
+// Workers supports multiple types of bindings, e.g. KV namespaces or WebAssembly modules, and each type
+// of binding will be represented differently in the upload request body. At a high-level, every binding
+// will specify metadata, which is a JSON object with the properties "name" and "type". Some types of bindings
+// will also have additional metadata properties. For example, KV bindings also specify the KV namespace.
+// In addition to the metadata, some binding types may need to include additional data as part of the
+// multipart form. For example, WebAssembly bindings will include the contents of the WebAssembly module.
+
+// WorkerBinding is the generic interface implemented by all of
+// the various binding types.
+type WorkerBinding interface {
+	Type() WorkerBindingType
+
+	// serialize is responsible for returning the binding metadata as well as an optionally
+	// returning a function that can modify the multipart form body. For example, this is used
+	// by WebAssembly bindings to add a new part containing the WebAssembly module contents.
+	serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error)
+}
+
+// workerBindingMeta is the metadata portion of the binding.
+type workerBindingMeta = map[string]interface{}
+
+// workerBindingBodyWriter allows for a binding to add additional parts to the multipart body.
+type workerBindingBodyWriter func(*multipart.Writer) error
+
+// WorkerInheritBinding will just persist whatever binding content was previously uploaded.
+type WorkerInheritBinding struct {
+	// Optional parameter that allows for renaming a binding without changing
+	// its contents. If `OldName` is empty, the binding name will not be changed.
+	OldName string
+}
+
+// Type returns the type of the binding.
+func (b WorkerInheritBinding) Type() WorkerBindingType {
+	return WorkerInheritBindingType
+}
+
+func (b WorkerInheritBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	meta := workerBindingMeta{
+		"name": bindingName,
+		"type": b.Type(),
+	}
+
+	if b.OldName != "" {
+		meta["old_name"] = b.OldName
+	}
+
+	return meta, nil, nil
+}
+
+// WorkerKvNamespaceBinding is a binding to a Workers KV Namespace.
+//
+// https://developers.cloudflare.com/workers/archive/api/resource-bindings/kv-namespaces/
+type WorkerKvNamespaceBinding struct {
+	NamespaceID string
+}
+
+// Type returns the type of the binding.
+func (b WorkerKvNamespaceBinding) Type() WorkerBindingType {
+	return WorkerKvNamespaceBindingType
+}
+
+func (b WorkerKvNamespaceBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	if b.NamespaceID == "" {
+		return nil, nil, fmt.Errorf(`NamespaceID for binding "%s" cannot be empty`, bindingName)
+	}
+
+	return workerBindingMeta{
+		"name":         bindingName,
+		"type":         b.Type(),
+		"namespace_id": b.NamespaceID,
+	}, nil, nil
+}
+
+// WorkerDurableObjectBinding is a binding to a Workers Durable Object.
+//
+// https://api.cloudflare.com/#durable-objects-namespace-properties
+type WorkerDurableObjectBinding struct {
+	ClassName  string
+	ScriptName string
+}
+
+// Type returns the type of the binding.
+func (b WorkerDurableObjectBinding) Type() WorkerBindingType {
+	return WorkerDurableObjectBindingType
+}
+
+func (b WorkerDurableObjectBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	if b.ClassName == "" {
+		return nil, nil, fmt.Errorf(`ClassName for binding "%s" cannot be empty`, bindingName)
+	}
+
+	return workerBindingMeta{
+		"name":        bindingName,
+		"type":        b.Type(),
+		"class_name":  b.ClassName,
+		"script_name": b.ScriptName,
+	}, nil, nil
+}
+
+// WorkerWebAssemblyBinding is a binding to a WebAssembly module.
+//
+// https://developers.cloudflare.com/workers/archive/api/resource-bindings/webassembly-modules/
+type WorkerWebAssemblyBinding struct {
+	Module io.Reader
+}
+
+// Type returns the type of the binding.
+func (b WorkerWebAssemblyBinding) Type() WorkerBindingType {
+	return WorkerWebAssemblyBindingType
+}
+
+func (b WorkerWebAssemblyBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	partName := getRandomPartName()
+
+	bodyWriter := func(mpw *multipart.Writer) error {
+		var hdr = textproto.MIMEHeader{}
+		hdr.Set("content-disposition", fmt.Sprintf(`form-data; name="%s"`, partName))
+		hdr.Set("content-type", "application/wasm")
+		pw, err := mpw.CreatePart(hdr)
+		if err != nil {
+			return err
+		}
+		_, err = io.Copy(pw, b.Module)
+		return err
+	}
+
+	return workerBindingMeta{
+		"name": bindingName,
+		"type": b.Type(),
+		"part": partName,
+	}, bodyWriter, nil
+}
+
+// WorkerPlainTextBinding is a binding to plain text.
+//
+// https://developers.cloudflare.com/workers/tooling/api/scripts/#add-a-plain-text-binding
+type WorkerPlainTextBinding struct {
+	Text string
+}
+
+// Type returns the type of the binding.
+func (b WorkerPlainTextBinding) Type() WorkerBindingType {
+	return WorkerPlainTextBindingType
+}
+
+func (b WorkerPlainTextBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	if b.Text == "" {
+		return nil, nil, fmt.Errorf(`Text for binding "%s" cannot be empty`, bindingName)
+	}
+
+	return workerBindingMeta{
+		"name": bindingName,
+		"type": b.Type(),
+		"text": b.Text,
+	}, nil, nil
+}
+
+// WorkerSecretTextBinding is a binding to secret text.
+//
+// https://developers.cloudflare.com/workers/tooling/api/scripts/#add-a-secret-text-binding
+type WorkerSecretTextBinding struct {
+	Text string
+}
+
+// Type returns the type of the binding.
+func (b WorkerSecretTextBinding) Type() WorkerBindingType {
+	return WorkerSecretTextBindingType
+}
+
+func (b WorkerSecretTextBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	if b.Text == "" {
+		return nil, nil, fmt.Errorf(`Text for binding "%s" cannot be empty`, bindingName)
+	}
+
+	return workerBindingMeta{
+		"name": bindingName,
+		"type": b.Type(),
+		"text": b.Text,
+	}, nil, nil
+}
+
+type WorkerServiceBinding struct {
+	Service     string
+	Environment *string
+}
+
+func (b WorkerServiceBinding) Type() WorkerBindingType {
+	return WorkerServiceBindingType
+}
+
+func (b WorkerServiceBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	if b.Service == "" {
+		return nil, nil, fmt.Errorf(`Service for binding "%s" cannot be empty`, bindingName)
+	}
+
+	meta := workerBindingMeta{
+		"name":    bindingName,
+		"type":    b.Type(),
+		"service": b.Service,
+	}
+
+	if b.Environment != nil {
+		meta["environment"] = *b.Environment
+	}
+
+	return meta, nil, nil
+}
+
+// WorkerR2BucketBinding is a binding to an R2 bucket.
+type WorkerR2BucketBinding struct {
+	BucketName string
+}
+
+// Type returns the type of the binding.
+func (b WorkerR2BucketBinding) Type() WorkerBindingType {
+	return WorkerR2BucketBindingType
+}
+
+func (b WorkerR2BucketBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	if b.BucketName == "" {
+		return nil, nil, fmt.Errorf(`BucketName for binding "%s" cannot be empty`, bindingName)
+	}
+
+	return workerBindingMeta{
+		"name":        bindingName,
+		"type":        b.Type(),
+		"bucket_name": b.BucketName,
+	}, nil, nil
+}
+
+// WorkerAnalyticsEngineBinding is a binding to an Analytics Engine dataset.
+type WorkerAnalyticsEngineBinding struct {
+	Dataset string
+}
+
+// Type returns the type of the binding.
+func (b WorkerAnalyticsEngineBinding) Type() WorkerBindingType {
+	return WorkerAnalyticsEngineBindingType
+}
+
+func (b WorkerAnalyticsEngineBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	if b.Dataset == "" {
+		return nil, nil, fmt.Errorf(`Dataset for binding "%s" cannot be empty`, bindingName)
+	}
+
+	return workerBindingMeta{
+		"name":    bindingName,
+		"type":    b.Type(),
+		"dataset": b.Dataset,
+	}, nil, nil
+}
+
+// Each binding that adds a part to the multipart form body will need
+// a unique part name so we just generate a random 128bit hex string.
+func getRandomPartName() string {
+	randBytes := make([]byte, 16)
+	rand.Read(randBytes) //nolint:errcheck
+	return hex.EncodeToString(randBytes)
+}
+
+// ListWorkerBindings returns all the bindings for a particular worker.
+func (api *API) ListWorkerBindings(ctx context.Context, rc *ResourceContainer, params ListWorkerBindingsParams) (WorkerBindingListResponse, error) {
+	if params.ScriptName == "" {
+		return WorkerBindingListResponse{}, errors.New("ScriptName is required")
+	}
+
+	if rc.Level != AccountRouteLevel {
+		return WorkerBindingListResponse{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return WorkerBindingListResponse{}, ErrMissingAccountID
+	}
+
+	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/bindings", rc.Identifier, params.ScriptName)
+
+	var jsonRes struct {
+		Response
+		Bindings []workerBindingMeta `json:"result"`
+	}
+	var r WorkerBindingListResponse
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return r, err
+	}
+	err = json.Unmarshal(res, &jsonRes)
+	if err != nil {
+		return r, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	r = WorkerBindingListResponse{
+		Response:    jsonRes.Response,
+		BindingList: make([]WorkerBindingListItem, 0, len(jsonRes.Bindings)),
+	}
+	for _, jsonBinding := range jsonRes.Bindings {
+		name, ok := jsonBinding["name"].(string)
+		if !ok {
+			return r, fmt.Errorf("Binding missing name %v", jsonBinding)
+		}
+		bType, ok := jsonBinding["type"].(string)
+		if !ok {
+			return r, fmt.Errorf("Binding missing type %v", jsonBinding)
+		}
+		bindingListItem := WorkerBindingListItem{
+			Name: name,
+		}
+
+		switch WorkerBindingType(bType) {
+		case WorkerDurableObjectBindingType:
+			class_name := jsonBinding["class_name"].(string)
+			script_name := jsonBinding["script_name"].(string)
+			bindingListItem.Binding = WorkerDurableObjectBinding{
+				ClassName:  class_name,
+				ScriptName: script_name,
+			}
+		case WorkerKvNamespaceBindingType:
+			namespaceID := jsonBinding["namespace_id"].(string)
+			bindingListItem.Binding = WorkerKvNamespaceBinding{
+				NamespaceID: namespaceID,
+			}
+		case WorkerWebAssemblyBindingType:
+			bindingListItem.Binding = WorkerWebAssemblyBinding{
+				Module: &bindingContentReader{
+					api:         api,
+					ctx:         ctx,
+					accountID:   rc.Identifier,
+					params:      &params,
+					bindingName: name,
+				},
+			}
+		case WorkerPlainTextBindingType:
+			text := jsonBinding["text"].(string)
+			bindingListItem.Binding = WorkerPlainTextBinding{
+				Text: text,
+			}
+		case WorkerServiceBindingType:
+			service := jsonBinding["service"].(string)
+			environment := jsonBinding["environment"].(string)
+			bindingListItem.Binding = WorkerServiceBinding{
+				Service:     service,
+				Environment: &environment,
+			}
+		case WorkerSecretTextBindingType:
+			bindingListItem.Binding = WorkerSecretTextBinding{}
+		case WorkerR2BucketBindingType:
+			bucketName := jsonBinding["bucket_name"].(string)
+			bindingListItem.Binding = WorkerR2BucketBinding{
+				BucketName: bucketName,
+			}
+		case WorkerAnalyticsEngineBindingType:
+			dataset := jsonBinding["dataset"].(string)
+			bindingListItem.Binding = WorkerAnalyticsEngineBinding{
+				Dataset: dataset,
+			}
+		default:
+			bindingListItem.Binding = WorkerInheritBinding{}
+		}
+		r.BindingList = append(r.BindingList, bindingListItem)
+	}
+
+	return r, nil
+}
+
+// bindingContentReader is an io.Reader that will lazily load the
+// raw bytes for a binding from the API when the Read() method
+// is first called. This is only useful for binding types
+// that store raw bytes, like WebAssembly modules.
+type bindingContentReader struct {
+	api         *API
+	accountID   string
+	params      *ListWorkerBindingsParams
+	ctx         context.Context
+	bindingName string
+	content     []byte
+	position    int
+}
+
+func (b *bindingContentReader) Read(p []byte) (n int, err error) {
+	// Lazily load the content when Read() is first called
+	if b.content == nil {
+		uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/bindings/%s/content", b.accountID, b.params.ScriptName, b.bindingName)
+		res, err := b.api.makeRequestContext(b.ctx, http.MethodGet, uri, nil)
+		if err != nil {
+			return 0, err
+		}
+		b.content = res
+	}
+
+	if b.position >= len(b.content) {
+		return 0, io.EOF
+	}
+
+	bytesRemaining := len(b.content) - b.position
+	bytesToProcess := 0
+	if len(p) < bytesRemaining {
+		bytesToProcess = len(p)
+	} else {
+		bytesToProcess = bytesRemaining
+	}
+
+	for i := 0; i < bytesToProcess; i++ {
+		p[i] = b.content[b.position]
+		b.position = b.position + 1
+	}
+
+	return bytesToProcess, nil
+}
diff --git a/workers_cron_triggers.go b/workers_cron_triggers.go
index 8938862ae..2322a5d68 100644
--- a/workers_cron_triggers.go
+++ b/workers_cron_triggers.go
@@ -27,12 +27,29 @@ type WorkerCronTrigger struct {
 	ModifiedOn *time.Time `json:"modified_on,omitempty"`
 }
 
+type ListWorkerCronTriggersParams struct {
+	ScriptName string
+}
+
+type UpdateWorkerCronTriggersParams struct {
+	ScriptName string
+	Crons      []WorkerCronTrigger
+}
+
 // ListWorkerCronTriggers fetches all available cron triggers for a single Worker
 // script.
 //
 // API reference: https://api.cloudflare.com/#worker-cron-trigger-get-cron-triggers
-func (api *API) ListWorkerCronTriggers(ctx context.Context, accountID, scriptName string) ([]WorkerCronTrigger, error) {
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/schedules", accountID, scriptName)
+func (api *API) ListWorkerCronTriggers(ctx context.Context, rc *ResourceContainer, params ListWorkerCronTriggersParams) ([]WorkerCronTrigger, error) {
+	if rc.Level != AccountRouteLevel {
+		return []WorkerCronTrigger{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return []WorkerCronTrigger{}, ErrMissingIdentifier
+	}
+
+	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/schedules", rc.Identifier, params.ScriptName)
 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
 	if err != nil {
 		return []WorkerCronTrigger{}, err
@@ -49,9 +66,17 @@ func (api *API) ListWorkerCronTriggers(ctx context.Context, accountID, scriptNam
 // UpdateWorkerCronTriggers updates a single schedule for a Worker cron trigger.
 //
 // API reference: https://api.cloudflare.com/#worker-cron-trigger-update-cron-triggers
-func (api *API) UpdateWorkerCronTriggers(ctx context.Context, accountID, scriptName string, crons []WorkerCronTrigger) ([]WorkerCronTrigger, error) {
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/schedules", accountID, scriptName)
-	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, crons)
+func (api *API) UpdateWorkerCronTriggers(ctx context.Context, rc *ResourceContainer, params UpdateWorkerCronTriggersParams) ([]WorkerCronTrigger, error) {
+	if rc.Level != AccountRouteLevel {
+		return []WorkerCronTrigger{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return []WorkerCronTrigger{}, ErrMissingIdentifier
+	}
+
+	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/schedules", rc.Identifier, params.ScriptName)
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params.Crons)
 	if err != nil {
 		return []WorkerCronTrigger{}, err
 	}
diff --git a/workers_kv.go b/workers_kv.go
index f7a01bc06..7cdf30e3c 100644
--- a/workers_kv.go
+++ b/workers_kv.go
@@ -106,6 +106,13 @@ type ListWorkersKVsParams struct {
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-create-a-namespace
 func (api *API) CreateWorkersKVNamespace(ctx context.Context, rc *ResourceContainer, params CreateWorkersKVNamespaceParams) (WorkersKVNamespaceResponse, error) {
+	if rc.Level != AccountRouteLevel {
+		return WorkersKVNamespaceResponse{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return WorkersKVNamespaceResponse{}, ErrMissingIdentifier
+	}
 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces", rc.Identifier)
 	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params)
 	if err != nil {
@@ -124,6 +131,14 @@ func (api *API) CreateWorkersKVNamespace(ctx context.Context, rc *ResourceContai
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-list-namespaces
 func (api *API) ListWorkersKVNamespaces(ctx context.Context, rc *ResourceContainer, params ListWorkersKVNamespacesParams) ([]WorkersKVNamespace, *ResultInfo, error) {
+	if rc.Level != AccountRouteLevel {
+		return []WorkersKVNamespace{}, &ResultInfo{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return []WorkersKVNamespace{}, &ResultInfo{}, ErrMissingIdentifier
+	}
+
 	autoPaginate := true
 	if params.PerPage >= 1 || params.Page >= 1 {
 		autoPaginate = false
@@ -183,6 +198,14 @@ func (api *API) DeleteWorkersKVNamespace(ctx context.Context, rc *ResourceContai
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-rename-a-namespace
 func (api *API) UpdateWorkersKVNamespace(ctx context.Context, rc *ResourceContainer, params UpdateWorkersKVNamespaceParams) (Response, error) {
+	if rc.Level != AccountRouteLevel {
+		return Response{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return Response{}, ErrMissingIdentifier
+	}
+
 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s", rc.Identifier, params.NamespaceID)
 	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
 	if err != nil {
@@ -201,6 +224,14 @@ func (api *API) UpdateWorkersKVNamespace(ctx context.Context, rc *ResourceContai
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-write-key-value-pair
 func (api *API) WriteWorkersKVEntry(ctx context.Context, rc *ResourceContainer, params WriteWorkersKVEntryParams) (Response, error) {
+	if rc.Level != AccountRouteLevel {
+		return Response{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return Response{}, ErrMissingIdentifier
+	}
+
 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", rc.Identifier, params.NamespaceID, url.PathEscape(params.Key))
 	res, err := api.makeRequestContextWithHeaders(
 		ctx, http.MethodPut, uri, params.Value, http.Header{"Content-Type": []string{"application/octet-stream"}},
@@ -221,6 +252,14 @@ func (api *API) WriteWorkersKVEntry(ctx context.Context, rc *ResourceContainer,
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-write-multiple-key-value-pairs
 func (api *API) WriteWorkersKVEntries(ctx context.Context, rc *ResourceContainer, params WriteWorkersKVEntriesParams) (Response, error) {
+	if rc.Level != AccountRouteLevel {
+		return Response{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return Response{}, ErrMissingIdentifier
+	}
+
 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/bulk", rc.Identifier, params.NamespaceID)
 	res, err := api.makeRequestContextWithHeaders(
 		ctx, http.MethodPut, uri, params.KVs, http.Header{"Content-Type": []string{"application/json"}},
@@ -242,6 +281,13 @@ func (api *API) WriteWorkersKVEntries(ctx context.Context, rc *ResourceContainer
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-read-key-value-pair
 func (api API) GetWorkersKV(ctx context.Context, rc *ResourceContainer, params GetWorkersKVParams) ([]byte, error) {
+	if rc.Level != AccountRouteLevel {
+		return []byte(``), ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return []byte(``), ErrMissingIdentifier
+	}
 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", rc.Identifier, params.NamespaceID, url.PathEscape(params.Key))
 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
 	if err != nil {
@@ -254,6 +300,13 @@ func (api API) GetWorkersKV(ctx context.Context, rc *ResourceContainer, params G
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-delete-key-value-pair
 func (api API) DeleteWorkersKVEntry(ctx context.Context, rc *ResourceContainer, params DeleteWorkersKVEntryParams) (Response, error) {
+	if rc.Level != AccountRouteLevel {
+		return Response{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return Response{}, ErrMissingIdentifier
+	}
 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", rc.Identifier, params.NamespaceID, url.PathEscape(params.Key))
 	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
 	if err != nil {
@@ -271,6 +324,13 @@ func (api API) DeleteWorkersKVEntry(ctx context.Context, rc *ResourceContainer,
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-delete-multiple-key-value-pairs
 func (api *API) DeleteWorkersKVEntries(ctx context.Context, rc *ResourceContainer, params DeleteWorkersKVEntriesParams) (Response, error) {
+	if rc.Level != AccountRouteLevel {
+		return Response{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return Response{}, ErrMissingIdentifier
+	}
 	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/bulk", rc.Identifier, params.NamespaceID)
 	res, err := api.makeRequestContextWithHeaders(
 		ctx, http.MethodDelete, uri, params.Keys, http.Header{"Content-Type": []string{"application/json"}},
@@ -291,6 +351,14 @@ func (api *API) DeleteWorkersKVEntries(ctx context.Context, rc *ResourceContaine
 //
 // API Reference: https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys
 func (api API) ListWorkersKVKeys(ctx context.Context, rc *ResourceContainer, params ListWorkersKVsParams) (ListStorageKeysResponse, error) {
+	if rc.Level != AccountRouteLevel {
+		return ListStorageKeysResponse{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return ListStorageKeysResponse{}, ErrMissingIdentifier
+	}
+
 	uri := buildURI(
 		fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/keys", rc.Identifier, params.NamespaceID),
 		params,
diff --git a/workers_routes.go b/workers_routes.go
new file mode 100644
index 000000000..d6dc1c2a7
--- /dev/null
+++ b/workers_routes.go
@@ -0,0 +1,161 @@
+package cloudflare
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+)
+
+var ErrMissingWorkerRouteID = errors.New("missing required route ID")
+
+type ListWorkerRoutes struct{}
+
+type CreateWorkerRouteParams struct {
+	Pattern string `json:"pattern"`
+	Script  string `json:"script,omitempty"`
+}
+
+type ListWorkerRoutesParams struct{}
+
+type UpdateWorkerRouteParams struct {
+	ID      string `json:"id,omitempty"`
+	Pattern string `json:"pattern"`
+	Script  string `json:"script,omitempty"`
+}
+
+// CreateWorkerRoute creates worker route for a script.
+//
+// API reference: https://api.cloudflare.com/#worker-routes-create-route
+func (api *API) CreateWorkerRoute(ctx context.Context, rc *ResourceContainer, params CreateWorkerRouteParams) (WorkerRouteResponse, error) {
+	if rc.Level != ZoneRouteLevel {
+		return WorkerRouteResponse{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
+	}
+
+	if rc.Identifier == "" {
+		return WorkerRouteResponse{}, ErrMissingIdentifier
+	}
+
+	uri := fmt.Sprintf("/zones/%s/workers/routes", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params)
+	if err != nil {
+		return WorkerRouteResponse{}, err
+	}
+
+	var r WorkerRouteResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return WorkerRouteResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+	return r, nil
+}
+
+// DeleteWorkerRoute deletes worker route for a script.
+//
+// API reference: https://api.cloudflare.com/#worker-routes-delete-route
+func (api *API) DeleteWorkerRoute(ctx context.Context, rc *ResourceContainer, routeID string) (WorkerRouteResponse, error) {
+	if rc.Level != ZoneRouteLevel {
+		return WorkerRouteResponse{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
+	}
+
+	if rc.Identifier == "" {
+		return WorkerRouteResponse{}, ErrMissingIdentifier
+	}
+
+	if routeID == "" {
+		return WorkerRouteResponse{}, errors.New("missing required route ID")
+	}
+
+	uri := fmt.Sprintf("/zones/%s/workers/routes/%s", rc.Identifier, routeID)
+	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
+	if err != nil {
+		return WorkerRouteResponse{}, err
+	}
+	var r WorkerRouteResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return WorkerRouteResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+	return r, nil
+}
+
+// ListWorkerRoutes returns list of Worker routes.
+//
+// API reference: https://api.cloudflare.com/#worker-routes-list-routes
+func (api *API) ListWorkerRoutes(ctx context.Context, rc *ResourceContainer, params ListWorkerRoutesParams) (WorkerRoutesResponse, error) {
+	if rc.Level != ZoneRouteLevel {
+		return WorkerRoutesResponse{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
+	}
+
+	if rc.Identifier == "" {
+		return WorkerRoutesResponse{}, ErrMissingIdentifier
+	}
+
+	uri := fmt.Sprintf("/zones/%s/workers/routes", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return WorkerRoutesResponse{}, err
+	}
+	var r WorkerRoutesResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return WorkerRoutesResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r, nil
+}
+
+// GetWorkerRoute returns a Workers route.
+//
+// API reference: https://api.cloudflare.com/#worker-routes-get-route
+func (api *API) GetWorkerRoute(ctx context.Context, rc *ResourceContainer, routeID string) (WorkerRouteResponse, error) {
+	if rc.Level != ZoneRouteLevel {
+		return WorkerRouteResponse{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
+	}
+
+	if rc.Identifier == "" {
+		return WorkerRouteResponse{}, ErrMissingIdentifier
+	}
+
+	uri := fmt.Sprintf("/zones/%s/workers/routes/%s", rc.Identifier, routeID)
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return WorkerRouteResponse{}, err
+	}
+	var r WorkerRouteResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return WorkerRouteResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+	return r, nil
+}
+
+// UpdateWorkerRoute updates worker route for a script.
+//
+// API reference: https://api.cloudflare.com/#worker-routes-update-route
+func (api *API) UpdateWorkerRoute(ctx context.Context, rc *ResourceContainer, params UpdateWorkerRouteParams) (WorkerRouteResponse, error) {
+	if rc.Level != ZoneRouteLevel {
+		return WorkerRouteResponse{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
+	}
+
+	if rc.Identifier == "" {
+		return WorkerRouteResponse{}, ErrMissingIdentifier
+	}
+
+	if params.ID == "" {
+		return WorkerRouteResponse{}, ErrMissingWorkerRouteID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/workers/routes/%s", rc.Identifier, params.ID)
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
+	if err != nil {
+		return WorkerRouteResponse{}, err
+	}
+	var r WorkerRouteResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return WorkerRouteResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+	return r, nil
+}
diff --git a/workers_secrets.go b/workers_secrets.go
index 09d8ba337..458cd1a7c 100644
--- a/workers_secrets.go
+++ b/workers_secrets.go
@@ -32,11 +32,34 @@ type WorkersListSecretsResponse struct {
 	Result []WorkersSecret `json:"result"`
 }
 
-// SetWorkersSecret creates or updates a secret
+type SetWorkersSecretParams struct {
+	ScriptName string
+	Secret     *WorkersPutSecretRequest
+}
+
+type DeleteWorkersSecretParams struct {
+	ScriptName string
+	SecretName string
+}
+
+type ListWorkersSecretsParams struct {
+	ScriptName string
+}
+
+// SetWorkersSecret creates or updates a secret.
+//
 // API reference: https://api.cloudflare.com/
-func (api *API) SetWorkersSecret(ctx context.Context, script string, req *WorkersPutSecretRequest) (WorkersPutSecretResponse, error) {
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets", api.AccountID, script)
-	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, req)
+func (api *API) SetWorkersSecret(ctx context.Context, rc *ResourceContainer, params SetWorkersSecretParams) (WorkersPutSecretResponse, error) {
+	if rc.Level != AccountRouteLevel {
+		return WorkersPutSecretResponse{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return WorkersPutSecretResponse{}, ErrMissingAccountID
+	}
+
+	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets", rc.Identifier, params.ScriptName)
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params.Secret)
 	if err != nil {
 		return WorkersPutSecretResponse{}, err
 	}
@@ -49,10 +72,19 @@ func (api *API) SetWorkersSecret(ctx context.Context, script string, req *Worker
 	return result, err
 }
 
-// DeleteWorkersSecret deletes a secret
+// DeleteWorkersSecret deletes a secret.
+//
 // API reference: https://api.cloudflare.com/
-func (api *API) DeleteWorkersSecret(ctx context.Context, script, secretName string) (Response, error) {
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets/%s", api.AccountID, script, secretName)
+func (api *API) DeleteWorkersSecret(ctx context.Context, rc *ResourceContainer, params DeleteWorkersSecretParams) (Response, error) {
+	if rc.Level != AccountRouteLevel {
+		return Response{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return Response{}, ErrMissingAccountID
+	}
+
+	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets/%s", rc.Identifier, params.ScriptName, params.SecretName)
 	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
 	if err != nil {
 		return Response{}, err
@@ -68,8 +100,16 @@ func (api *API) DeleteWorkersSecret(ctx context.Context, script, secretName stri
 
 // ListWorkersSecrets lists secrets for a given worker
 // API reference: https://api.cloudflare.com/
-func (api *API) ListWorkersSecrets(ctx context.Context, script string) (WorkersListSecretsResponse, error) {
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets", api.AccountID, script)
+func (api *API) ListWorkersSecrets(ctx context.Context, rc *ResourceContainer, params ListWorkersSecretsParams) (WorkersListSecretsResponse, error) {
+	if rc.Level != AccountRouteLevel {
+		return WorkersListSecretsResponse{}, ErrRequiredAccountLevelResourceContainer
+	}
+
+	if rc.Identifier == "" {
+		return WorkersListSecretsResponse{}, ErrMissingAccountID
+	}
+
+	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets", rc.Identifier, params.ScriptName)
 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
 	if err != nil {
 		return WorkersListSecretsResponse{}, err
diff --git a/workers_tail.go b/workers_tail.go
index 478112b4a..0f074b0b6 100644
--- a/workers_tail.go
+++ b/workers_tail.go
@@ -35,13 +35,14 @@ type ListWorkersTailResponse struct {
 	Result WorkersTail
 }
 
-// StartWorkersTail Starts a tail that receives logs and exception from a Worker
+// StartWorkersTail Starts a tail that receives logs and exception from a Worker.
 //
 // API reference: https://api.cloudflare.com/#worker-tail-logs-start-tail
 func (api *API) StartWorkersTail(ctx context.Context, rc *ResourceContainer, scriptName string) (WorkersTail, error) {
 	if rc.Identifier == "" {
 		return WorkersTail{}, ErrMissingAccountID
 	}
+
 	if scriptName == "" {
 		return WorkersTail{}, ErrMissingScriptName
 	}
@@ -60,7 +61,7 @@ func (api *API) StartWorkersTail(ctx context.Context, rc *ResourceContainer, scr
 	return workerstailResponse.Result, nil
 }
 
-// ListWorkersTail Get list of tails currently deployed on a worker
+// ListWorkersTail Get list of tails currently deployed on a Worker.
 //
 // API reference: https://api.cloudflare.com/#worker-tail-logs-list-tails
 func (api *API) ListWorkersTail(ctx context.Context, rc *ResourceContainer, params ListWorkersTailParameters) (WorkersTail, error) {
@@ -86,7 +87,7 @@ func (api *API) ListWorkersTail(ctx context.Context, rc *ResourceContainer, para
 	return workerstailResponse.Result, nil
 }
 
-// DeleteWorkersTail Deletes a tail from a Worker
+// DeleteWorkersTail Deletes a tail from a Worker.
 //
 // API reference: https://api.cloudflare.com/#worker-tail-logs-delete-tail
 func (api *API) DeleteWorkersTail(ctx context.Context, rc *ResourceContainer, scriptName, tailID string) error {


Test patch:
diff --git a/workers_bindings_test.go b/workers_bindings_test.go
new file mode 100644
index 000000000..f8dea25ad
--- /dev/null
+++ b/workers_bindings_test.go
@@ -0,0 +1,97 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestListWorkerBindings(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/my-script/bindings", 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, listBindingsResponseData)
+	})
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/my-script/bindings/MY_WASM/content", 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/wasm")
+		_, _ = w.Write([]byte("mock multi-script wasm"))
+	})
+
+	res, err := client.ListWorkerBindings(context.Background(), AccountIdentifier(testAccountID), ListWorkerBindingsParams{
+		ScriptName: "my-script",
+	})
+	assert.NoError(t, err)
+
+	assert.Equal(t, successResponse, res.Response)
+	assert.Equal(t, 8, len(res.BindingList))
+
+	assert.Equal(t, res.BindingList[0], WorkerBindingListItem{
+		Name: "MY_KV",
+		Binding: WorkerKvNamespaceBinding{
+			NamespaceID: "89f5f8fd93f94cb98473f6f421aa3b65",
+		},
+	})
+	assert.Equal(t, WorkerKvNamespaceBindingType, res.BindingList[0].Binding.Type())
+
+	assert.Equal(t, "MY_WASM", res.BindingList[1].Name)
+	wasmBinding := res.BindingList[1].Binding.(WorkerWebAssemblyBinding)
+	wasmModuleContent, err := io.ReadAll(wasmBinding.Module)
+	assert.NoError(t, err)
+	assert.Equal(t, []byte("mock multi-script wasm"), wasmModuleContent)
+	assert.Equal(t, WorkerWebAssemblyBindingType, res.BindingList[1].Binding.Type())
+
+	assert.Equal(t, res.BindingList[2], WorkerBindingListItem{
+		Name: "MY_PLAIN_TEXT",
+		Binding: WorkerPlainTextBinding{
+			Text: "text",
+		},
+	})
+	assert.Equal(t, WorkerPlainTextBindingType, res.BindingList[2].Binding.Type())
+
+	assert.Equal(t, res.BindingList[3], WorkerBindingListItem{
+		Name:    "MY_SECRET_TEXT",
+		Binding: WorkerSecretTextBinding{},
+	})
+	assert.Equal(t, WorkerSecretTextBindingType, res.BindingList[3].Binding.Type())
+
+	environment := "MY_ENVIRONMENT"
+	assert.Equal(t, res.BindingList[4], WorkerBindingListItem{
+		Name: "MY_SERVICE_BINDING",
+		Binding: WorkerServiceBinding{
+			Service:     "MY_SERVICE",
+			Environment: &environment,
+		},
+	})
+	assert.Equal(t, WorkerServiceBindingType, res.BindingList[4].Binding.Type())
+
+	assert.Equal(t, res.BindingList[5], WorkerBindingListItem{
+		Name:    "MY_NEW_BINDING",
+		Binding: WorkerInheritBinding{},
+	})
+	assert.Equal(t, WorkerInheritBindingType, res.BindingList[5].Binding.Type())
+
+	assert.Equal(t, res.BindingList[6], WorkerBindingListItem{
+		Name: "MY_BUCKET",
+		Binding: WorkerR2BucketBinding{
+			BucketName: "bucket",
+		},
+	})
+	assert.Equal(t, WorkerR2BucketBindingType, res.BindingList[6].Binding.Type())
+
+	assert.Equal(t, res.BindingList[7], WorkerBindingListItem{
+		Name: "MY_DATASET",
+		Binding: WorkerAnalyticsEngineBinding{
+			Dataset: "my_dataset",
+		},
+	})
+	assert.Equal(t, WorkerAnalyticsEngineBindingType, res.BindingList[7].Binding.Type())
+}
diff --git a/workers_cron_triggers_test.go b/workers_cron_triggers_test.go
index d2acba1e2..0194d75ef 100644
--- a/workers_cron_triggers_test.go
+++ b/workers_cron_triggers_test.go
@@ -11,7 +11,7 @@ import (
 )
 
 func TestListWorkerCronTriggers(t *testing.T) {
-	setup(UsingAccount("9a7806061c88ada191ed06f989cc3dac"))
+	setup()
 	defer teardown()
 
 	handler := func(w http.ResponseWriter, r *http.Request) {
@@ -42,14 +42,14 @@ func TestListWorkerCronTriggers(t *testing.T) {
 		CreatedOn:  &createdOn,
 	}}
 
-	actual, err := client.ListWorkerCronTriggers(context.Background(), testAccountID, "example-script")
+	actual, err := client.ListWorkerCronTriggers(context.Background(), AccountIdentifier(testAccountID), ListWorkerCronTriggersParams{ScriptName: "example-script"})
 	if assert.NoError(t, err) {
 		assert.Equal(t, want, actual)
 	}
 }
 
 func TestUpdateWorkerCronTriggers(t *testing.T) {
-	setup(UsingAccount("9a7806061c88ada191ed06f989cc3dac"))
+	setup()
 	defer teardown()
 
 	handler := func(w http.ResponseWriter, r *http.Request) {
@@ -80,7 +80,7 @@ func TestUpdateWorkerCronTriggers(t *testing.T) {
 		CreatedOn:  &createdOn,
 	}}
 
-	actual, err := client.UpdateWorkerCronTriggers(context.Background(), testAccountID, "example-script", want)
+	actual, err := client.UpdateWorkerCronTriggers(context.Background(), AccountIdentifier(testAccountID), UpdateWorkerCronTriggersParams{ScriptName: "example-script", Crons: want})
 	if assert.NoError(t, err) {
 		assert.Equal(t, want, actual)
 	}
diff --git a/workers_example_test.go b/workers_example_test.go
deleted file mode 100644
index 2206a3504..000000000
--- a/workers_example_test.go
+++ /dev/null
@@ -1,206 +0,0 @@
-package cloudflare_test
-
-import (
-	"context"
-	"fmt"
-	"log"
-
-	cloudflare "github.com/cloudflare/cloudflare-go"
-)
-
-var (
-	workerScript = "addEventListener('fetch', event => {\n    event.passThroughOnException()\nevent.respondWith(handleRequest(event.request))\n})\n\nasync function handleRequest(request) {\n    return fetch(request)\n}"
-)
-
-func ExampleAPI_UploadWorker() {
-	api, err := cloudflare.New(apiKey, user)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	zoneID, err := api.ZoneIDByName(domain)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	res, err := api.UploadWorker(context.Background(), &cloudflare.WorkerRequestParams{ZoneID: zoneID}, &cloudflare.WorkerScriptParams{Script: workerScript})
-	if err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%+v", res)
-
-	UploadWorkerWithName()
-}
-
-func UploadWorkerWithName() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount("foo"))
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	res, err := api.UploadWorker(context.Background(), &cloudflare.WorkerRequestParams{ScriptName: "baz"}, &cloudflare.WorkerScriptParams{Script: workerScript})
-	if err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%+v", res)
-}
-
-func ExampleAPI_DownloadWorker() {
-	api, err := cloudflare.New(apiKey, user)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	zoneID, err := api.ZoneIDByName(domain)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	res, err := api.DownloadWorker(context.Background(), &cloudflare.WorkerRequestParams{ZoneID: zoneID})
-	if err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%+v", res)
-
-	DownloadWorkerWithName()
-}
-
-func DownloadWorkerWithName() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount("foo"))
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	res, err := api.DownloadWorker(context.Background(), &cloudflare.WorkerRequestParams{ScriptName: "baz"})
-	if err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%+v", res)
-}
-
-func ExampleAPI_DeleteWorker() {
-	api, err := cloudflare.New(apiKey, user)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	zoneID, err := api.ZoneIDByName(domain)
-	if err != nil {
-		log.Fatal(err)
-	}
-	res, err := api.DeleteWorker(context.Background(), &cloudflare.WorkerRequestParams{ZoneID: zoneID})
-	if err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%+v", res)
-
-	DeleteWorkerWithName()
-}
-
-func DeleteWorkerWithName() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount("foo"))
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	res, err := api.DeleteWorker(context.Background(), &cloudflare.WorkerRequestParams{ScriptName: "baz"})
-	if err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%+v", res)
-}
-
-func ExampleAPI_ListWorkerScripts() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount("foo"))
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	res, err := api.ListWorkerScripts(context.Background())
-	if err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%+v", res.WorkerList)
-}
-
-func ExampleAPI_CreateWorkerRoute() {
-	api, err := cloudflare.New(apiKey, user)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	zoneID, err := api.ZoneIDByName(domain)
-	if err != nil {
-		log.Fatal(err)
-	}
-	route := cloudflare.WorkerRoute{Pattern: "app1.example.com/*", Enabled: true}
-	res, err := api.CreateWorkerRoute(context.Background(), zoneID, route)
-	if err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%+v", res)
-}
-
-func ExampleAPI_UpdateWorkerRoute() {
-	api, err := cloudflare.New(apiKey, user)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	zoneID, err := api.ZoneIDByName(domain)
-	if err != nil {
-		log.Fatal(err)
-	}
-	// pull from existing list of routes to perform update on
-	routesResponse, err := api.ListWorkerRoutes(context.Background(), zoneID)
-	if err != nil {
-		log.Fatal(err)
-	}
-	route := cloudflare.WorkerRoute{Pattern: "app2.example.com/*", Enabled: true}
-	// update first route retrieved from the listWorkerRoutes call with details above
-	res, err := api.UpdateWorkerRoute(context.Background(), zoneID, routesResponse.Routes[0].ID, route)
-	if err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%+v", res)
-}
-
-func ExampleAPI_ListWorkerRoutes() {
-	api, err := cloudflare.New(apiKey, user)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	zoneID, err := api.ZoneIDByName(domain)
-	if err != nil {
-		log.Fatal(err)
-	}
-	res, err := api.ListWorkerRoutes(context.Background(), zoneID)
-	if err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%+v", res)
-}
-
-func ExampleAPI_DeleteWorkerRoute() {
-	api, err := cloudflare.New(apiKey, user)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	zoneID, err := api.ZoneIDByName(domain)
-	if err != nil {
-		log.Fatal(err)
-	}
-	// pull from existing list of routes to perform delete on
-	routesResponse, err := api.ListWorkerRoutes(context.Background(), zoneID)
-	if err != nil {
-		log.Fatal(err)
-	}
-	// delete first route retrieved from the listWorkerRoutes call
-	res, err := api.DeleteWorkerRoute(context.Background(), zoneID, routesResponse.Routes[0].ID)
-	if err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%+v", res)
-}
diff --git a/workers_kv_test.go b/workers_kv_test.go
index 1ad7ae091..80d7b140b 100644
--- a/workers_kv_test.go
+++ b/workers_kv_test.go
@@ -29,7 +29,7 @@ func TestWorkersKV_CreateWorkersKVNamespace(t *testing.T) {
 	mux.HandleFunc("/accounts/"+testAccountID+"/storage/kv/namespaces", 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/javascript")
-		fmt.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
 	res, err := client.CreateWorkersKVNamespace(context.Background(), AccountIdentifier(testAccountID), CreateWorkersKVNamespaceParams{Title: "Namespace"})
@@ -60,7 +60,7 @@ func TestWorkersKV_DeleteWorkersKVNamespace(t *testing.T) {
 	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s", namespace), 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/javascript")
-		fmt.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
 	res, err := client.DeleteWorkersKVNamespace(context.Background(), AccountIdentifier(testAccountID), namespace)
@@ -99,7 +99,7 @@ func TestWorkersKV_ListWorkersKVNamespaces(t *testing.T) {
 	mux.HandleFunc("/accounts/"+testAccountID+"/storage/kv/namespaces", 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/javascript")
-		fmt.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
 	res, _, err := client.ListWorkersKVNamespaces(context.Background(), AccountIdentifier(testAccountID), ListWorkersKVNamespacesParams{})
@@ -170,10 +170,10 @@ func TestWorkersKV_ListWorkersKVNamespaceMultiplePages(t *testing.T) {
 		w.Header().Set("content-type", "application/javascript")
 
 		if r.URL.Query().Get("page") == "1" {
-			fmt.Fprintf(w, response1) //nolint
+			fmt.Fprint(w, response1)
 			return
 		} else if r.URL.Query().Get("page") == "2" {
-			fmt.Fprintf(w, response2) //nolint
+			fmt.Fprint(w, response2)
 			return
 		} else {
 			panic(errors.New("Got a request for an unexpected page"))
@@ -218,7 +218,7 @@ func TestWorkersKV_UpdateWorkersKVNamespace(t *testing.T) {
 	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s", namespace), 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/javascript")
-		fmt.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
 	res, err := client.UpdateWorkersKVNamespace(context.Background(), AccountIdentifier(testAccountID), UpdateWorkersKVNamespaceParams{Title: "Namespace", NamespaceID: namespace})
@@ -246,7 +246,7 @@ func TestWorkersKV_WriteWorkersKVEntry(t *testing.T) {
 	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/values/%s", namespace, key), 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/octet-stream")
-		fmt.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
 	want := successResponse
@@ -278,7 +278,7 @@ func TestWorkersKV_WriteWorkersKVEntries(t *testing.T) {
 	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/bulk", namespace), 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.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
 	want := successResponse
@@ -297,7 +297,7 @@ func TestWorkersKV_ReadWorkersKV(t *testing.T) {
 	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/values/%s", namespace, key), 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", "text/plain")
-		fmt.Fprintf(w, "test_value")
+		fmt.Fprint(w, "test_value")
 	})
 
 	res, err := client.GetWorkersKV(context.Background(), AccountIdentifier(testAccountID), GetWorkersKVParams{NamespaceID: namespace, Key: key})
@@ -324,7 +324,7 @@ func TestWorkersKV_DeleteWorkersKVEntry(t *testing.T) {
 	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/values/%s", namespace, key), 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/javascript")
-		fmt.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
 	res, err := client.DeleteWorkersKVEntry(context.Background(), AccountIdentifier(testAccountID), DeleteWorkersKVEntryParams{NamespaceID: namespace, Key: key})
@@ -352,7 +352,7 @@ func TestWorkersKV_DeleteWorkersKVBulk(t *testing.T) {
 	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/bulk", namespace), 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.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
 	want := successResponse
@@ -386,7 +386,7 @@ func TestWorkersKV_ListKeys(t *testing.T) {
 	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/keys", namespace), 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/javascript")
-		fmt.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
 	res, err := client.ListWorkersKVKeys(context.Background(), AccountIdentifier(testAccountID), ListWorkersKVsParams{NamespaceID: namespace})
@@ -456,7 +456,7 @@ func TestWorkersKV_ListKeysWithParameters(t *testing.T) {
 	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/keys", namespace), 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/javascript")
-		fmt.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
 	limit, prefix := 25, "test-prefix"
diff --git a/workers_routes_test.go b/workers_routes_test.go
new file mode 100644
index 000000000..8d36fdc9d
--- /dev/null
+++ b/workers_routes_test.go
@@ -0,0 +1,121 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestCreateWorkersRoute(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/workers/routes", 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, createWorkerRouteResponse)
+	})
+
+	res, err := client.CreateWorkerRoute(context.Background(), ZoneIdentifier(testZoneID), CreateWorkerRouteParams{
+		Pattern: "app1.example.com/*",
+		Script:  "example",
+	})
+
+	want := WorkerRouteResponse{successResponse, WorkerRoute{ID: "e7a57d8746e74ae49c25994dadb421b1"}}
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
+
+func TestDeleteWorkersRoute(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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, deleteWorkerRouteResponseData)
+	})
+	res, err := client.DeleteWorkerRoute(context.Background(), ZoneIdentifier(testZoneID), "e7a57d8746e74ae49c25994dadb421b1")
+	want := WorkerRouteResponse{successResponse,
+		WorkerRoute{
+			ID: "e7a57d8746e74ae49c25994dadb421b1",
+		}}
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
+
+func TestListWorkersRoute(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/workers/routes", 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, listWorkerRouteResponse)
+	})
+
+	res, err := client.ListWorkerRoutes(context.Background(), ZoneIdentifier(testZoneID), ListWorkerRoutesParams{})
+	want := WorkerRoutesResponse{successResponse,
+		[]WorkerRoute{
+			{ID: "e7a57d8746e74ae49c25994dadb421b1", Pattern: "app1.example.com/*", ScriptName: "test_script_1"},
+			{ID: "f8b68e9857f85bf59c25994dadb421b1", Pattern: "app2.example.com/*", ScriptName: "test_script_2"},
+			{ID: "2b5bf4240cd34c77852fac70b1bf745a", Pattern: "app3.example.com/*", ScriptName: "test_script_3"},
+		},
+	}
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
+
+func TestGetWorkersRoute(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/workers/routes/1234", 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, getRouteResponseData)
+	})
+
+	res, err := client.GetWorkerRoute(context.Background(), ZoneIdentifier(testZoneID), "1234")
+	want := WorkerRouteResponse{successResponse,
+		WorkerRoute{
+			ID:         "e7a57d8746e74ae49c25994dadb421b1",
+			Pattern:    "app1.example.com/*",
+			ScriptName: "script-name"},
+	}
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
+
+func TestUpdateWorkersRoute(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/"+testZoneID+"/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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, updateWorkerRouteEntResponse)
+	})
+
+	res, err := client.UpdateWorkerRoute(context.Background(), ZoneIdentifier(testZoneID), UpdateWorkerRouteParams{
+		ID:      "e7a57d8746e74ae49c25994dadb421b1",
+		Pattern: "app3.example.com/*",
+		Script:  "test_script_1",
+	})
+	want := WorkerRouteResponse{successResponse,
+		WorkerRoute{
+			ID:         "e7a57d8746e74ae49c25994dadb421b1",
+			Pattern:    "app3.example.com/*",
+			ScriptName: "test_script_1",
+		}}
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, res)
+	}
+}
diff --git a/workers_secrets_test.go b/workers_secrets_test.go
index c08ef71dd..dc3ac99fa 100644
--- a/workers_secrets_test.go
+++ b/workers_secrets_test.go
@@ -9,8 +9,8 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestWorkers_SetWorkersSecret(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestSetWorkersSecret(t *testing.T) {
+	setup()
 	defer teardown()
 
 	response := `{
@@ -23,16 +23,16 @@ func TestWorkers_SetWorkersSecret(t *testing.T) {
 		"messages": []
 	}`
 
-	mux.HandleFunc("/accounts/foo/workers/scripts/test-script/secrets", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/test-script/secrets", 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/javascript")
-		fmt.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 	req := &WorkersPutSecretRequest{
 		Name: "my-secret",
 		Text: "super-secret",
 	}
-	res, err := client.SetWorkersSecret(context.Background(), "test-script", req)
+	res, err := client.SetWorkersSecret(context.Background(), AccountIdentifier(testAccountID), SetWorkersSecretParams{ScriptName: "test-script", Secret: req})
 	want := WorkersPutSecretResponse{
 		successResponse,
 		WorkersSecret{
@@ -46,8 +46,8 @@ func TestWorkers_SetWorkersSecret(t *testing.T) {
 	}
 }
 
-func TestWorkers_DeleteWorkersSecret(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestDeleteWorkersSecret(t *testing.T) {
+	setup()
 	defer teardown()
 
 	response := `{
@@ -60,13 +60,13 @@ func TestWorkers_DeleteWorkersSecret(t *testing.T) {
 		"messages": []
 	}`
 
-	mux.HandleFunc("/accounts/foo/workers/scripts/test-script/secrets/my-secret", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/test-script/secrets/my-secret", 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/javascript")
-		fmt.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
-	res, err := client.DeleteWorkersSecret(context.Background(), "test-script", "my-secret")
+	res, err := client.DeleteWorkersSecret(context.Background(), AccountIdentifier(testAccountID), DeleteWorkersSecretParams{ScriptName: "test-script", SecretName: "my-secret"})
 	want := successResponse
 
 	if assert.NoError(t, err) {
@@ -74,8 +74,8 @@ func TestWorkers_DeleteWorkersSecret(t *testing.T) {
 	}
 }
 
-func TestWorkers_ListWorkersSecret(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestListWorkersSecret(t *testing.T) {
+	setup()
 	defer teardown()
 
 	response := `{
@@ -88,13 +88,13 @@ func TestWorkers_ListWorkersSecret(t *testing.T) {
 		"messages": []
 	}`
 
-	mux.HandleFunc("/accounts/foo/workers/scripts/test-script/secrets", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/test-script/secrets", 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/javascript")
-		fmt.Fprintf(w, response) //nolint
+		fmt.Fprint(w, response)
 	})
 
-	res, err := client.ListWorkersSecrets(context.Background(), "test-script")
+	res, err := client.ListWorkersSecrets(context.Background(), AccountIdentifier(testAccountID), ListWorkersSecretsParams{ScriptName: "test-script"})
 	want := WorkersListSecretsResponse{
 		successResponse,
 		[]WorkersSecret{
diff --git a/workers_test.go b/workers_test.go
index 1f5dbfbc0..647f1af32 100644
--- a/workers_test.go
+++ b/workers_test.go
@@ -89,7 +89,7 @@ const (
     "errors": [],
     "messages": []
 }`
-	listRouteEntResponseData = `{
+	listWorkerRouteResponse = `{
     "result": [
         {
             "id": "e7a57d8746e74ae49c25994dadb421b1",
@@ -103,7 +103,8 @@ const (
         },
         {
             "id": "2b5bf4240cd34c77852fac70b1bf745a",
-            "pattern": "app3.example.com/*"
+            "pattern": "app3.example.com/*",
+			"script": "test_script_3"
         }
     ],
     "success": true,
@@ -320,61 +321,30 @@ func parseMultipartUpload(r *http.Request) (multipartUpload, error) {
 	}, nil
 }
 
-func TestWorkers_DeleteWorker(t *testing.T) {
+func TestDeleteWorker(t *testing.T) {
 	setup()
 	defer teardown()
 
-	mux.HandleFunc("/zones/foo/workers/script", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", 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/javascript")
-		fmt.Fprintf(w, deleteWorkerResponseData) //nolint
+		fmt.Fprint(w, deleteWorkerResponseData)
 	})
-	res, err := client.DeleteWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"})
-	want := WorkerScriptResponse{
-		Response: successResponse,
-	}
-
-	if assert.NoError(t, err) {
-		assert.Equal(t, want.Response, res.Response)
-	}
-}
 
-func TestWorkers_DeleteWorkerWithName(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.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method)
-		w.Header().Set("content-type", "application/javascript")
-		fmt.Fprintf(w, deleteWorkerResponseData) //nolint
-	})
-	res, err := client.DeleteWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"})
-	want := WorkerScriptResponse{
-		Response: successResponse,
-	}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want.Response, res.Response)
-	}
-}
-
-func TestWorkers_DeleteWorkerWithNameErrorsWithoutAccountId(t *testing.T) {
-	setup()
-	defer teardown()
-
-	_, err := client.DeleteWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"})
-	assert.Error(t, err)
+	err := client.DeleteWorker(context.Background(), AccountIdentifier(testAccountID), DeleteWorkerParams{ScriptName: "bar"})
+	assert.NoError(t, err)
 }
 
-func TestWorkers_DownloadWorker(t *testing.T) {
+func TestGetWorker(t *testing.T) {
 	setup()
 	defer teardown()
 
-	mux.HandleFunc("/zones/foo/workers/script", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/foo", 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/javascript")
-		fmt.Fprintf(w, workerScript) //nolint
+		fmt.Fprint(w, workerScript)
 	})
-	res, err := client.DownloadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"})
+	res, err := client.GetWorker(context.Background(), AccountIdentifier(testAccountID), "foo")
 	want := WorkerScriptResponse{
 		successResponse,
 		false,
@@ -386,67 +356,41 @@ func TestWorkers_DownloadWorker(t *testing.T) {
 	}
 }
 
-func TestWorkers_DownloadWorkerWithName(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.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method)
-		w.Header().Set("content-type", "application/javascript")
-		fmt.Fprintf(w, workerScript) //nolint
-	})
-	res, err := client.DownloadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"})
-	want := WorkerScriptResponse{
-		successResponse,
-		false,
-		WorkerScript{
-			Script: workerScript,
-		}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want.Script, res.Script)
-	}
-}
-
-func TestWorkers_DownloadWorkerWithNameErrorsWithoutAccountId(t *testing.T) {
+func TestGetWorker_Module(t *testing.T) {
 	setup()
 	defer teardown()
 
-	_, err := client.DownloadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"})
-	assert.Error(t, err)
-}
-
-func TestWorkers_DownloadWorkerModule(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	mux.HandleFunc("/accounts/foo/workers/scripts/bar", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/foo", 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", "multipart/form-data; boundary=workermodulescriptdownload")
-		fmt.Fprintf(w, workerModuleScriptDownloadResponse) //nolint
+		fmt.Fprint(w, workerModuleScriptDownloadResponse)
 	})
-	res, err := client.DownloadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"})
+
+	res, err := client.GetWorker(context.Background(), AccountIdentifier(testAccountID), "foo")
 	want := WorkerScriptResponse{
 		successResponse,
 		true,
 		WorkerScript{
 			Script: workerModuleScript,
-		}}
+		},
+	}
+
 	if assert.NoError(t, err) {
 		assert.Equal(t, want.Script, res.Script)
 	}
 }
 
-func TestWorkers_ListWorkerScripts(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestListWorkers(t *testing.T) {
+	setup()
 	defer teardown()
 
-	mux.HandleFunc("/accounts/foo/workers/scripts", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts", 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.Fprintf(w, listWorkersResponseData) //nolint
+		fmt.Fprint(w, listWorkersResponseData)
 	})
 
-	res, err := client.ListWorkerScripts(context.Background())
+	res, _, err := client.ListWorkers(context.Background(), AccountIdentifier(testAccountID), ListWorkersParams{})
 	sampleDate, _ := time.Parse(time.RFC3339Nano, "2018-04-22T17:10:48.938097Z")
 	want := []WorkerMetaData{
 		{
@@ -467,18 +411,18 @@ func TestWorkers_ListWorkerScripts(t *testing.T) {
 	}
 }
 
-func TestWorkers_UploadWorker(t *testing.T) {
+func TestUploadWorker_Basic(t *testing.T) {
 	setup()
 	defer teardown()
 
-	mux.HandleFunc("/zones/foo/workers/script", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/foo", func(w http.ResponseWriter, r *http.Request) {
 		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
 		contentTypeHeader := r.Header.Get("content-type")
 		assert.Equal(t, "application/javascript", contentTypeHeader, "Expected content-type request header to be 'application/javascript', got %s", contentTypeHeader)
 		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
+		fmt.Fprint(w, uploadWorkerResponseData)
 	})
-	res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, &WorkerScriptParams{Script: workerScript})
+	res, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ScriptName: "foo", Script: workerScript})
 	formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z")
 	want := WorkerScriptResponse{
 		successResponse,
@@ -496,11 +440,11 @@ func TestWorkers_UploadWorker(t *testing.T) {
 	}
 }
 
-func TestWorkers_UploadWorkerAsModule(t *testing.T) {
+func TestUploadWorker_Module(t *testing.T) {
 	setup()
 	defer teardown()
 
-	mux.HandleFunc("/zones/foo/workers/script", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/foo", func(w http.ResponseWriter, r *http.Request) {
 		mpUpload, err := parseMultipartUpload(r)
 		assert.NoError(t, err)
 
@@ -515,9 +459,9 @@ func TestWorkers_UploadWorkerAsModule(t *testing.T) {
 		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
+		fmt.Fprint(w, uploadWorkerModuleResponseData)
 	})
-	res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, &WorkerScriptParams{Script: workerModuleScript, Module: true})
+	res, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ScriptName: "foo", Script: workerModuleScript, Module: true})
 	formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z")
 	want := WorkerScriptResponse{
 		successResponse,
@@ -535,76 +479,10 @@ func TestWorkers_UploadWorkerAsModule(t *testing.T) {
 	}
 }
 
-func TestWorkers_UploadWorkerWithName(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)
-		contentTypeHeader := r.Header.Get("content-type")
-		assert.Equal(t, "application/javascript", contentTypeHeader, "Expected content-type request header to be 'application/javascript', got %s", contentTypeHeader)
-		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
-	})
-	res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &WorkerScriptParams{Script: workerScript})
-	formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z")
-	want := WorkerScriptResponse{
-		successResponse,
-		false,
-		WorkerScript{
-			Script: workerScript,
-			WorkerMetaData: WorkerMetaData{
-				ETAG:       "279cf40d86d70b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43977a",
-				Size:       191,
-				ModifiedOn: formattedTime,
-			},
-		}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_UploadWorkerSingleScriptWithAccount(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/script", func(w http.ResponseWriter, r *http.Request) {
-		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
-		contentTypeHeader := r.Header.Get("content-type")
-		assert.Equal(t, "application/javascript", contentTypeHeader, "Expected content-type request header to be 'application/javascript', got %s", contentTypeHeader)
-		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
-	})
-	res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, &WorkerScriptParams{Script: workerScript})
-	formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z")
-	want := WorkerScriptResponse{
-		successResponse,
-		false,
-		WorkerScript{
-			Script: workerScript,
-			WorkerMetaData: WorkerMetaData{
-				ETAG:       "279cf40d86d70b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43977a",
-				Size:       191,
-				ModifiedOn: formattedTime,
-			},
-		}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_UploadWorkerWithNameErrorsWithoutAccountId(t *testing.T) {
+func TestUploadWorker_WithDurableObjectBinding(t *testing.T) {
 	setup()
 	defer teardown()
 
-	_, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &WorkerScriptParams{Script: workerScript})
-	assert.Error(t, err)
-}
-
-func TestWorkers_UploadWorkerWithDurableObjectBinding(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
 	handler := func(w http.ResponseWriter, r *http.Request) {
 		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
 
@@ -623,25 +501,27 @@ func TestWorkers_UploadWorkerWithDurableObjectBinding(t *testing.T) {
 		assert.Equal(t, expectedBindings, mpUpload.BindingMeta)
 
 		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
+		fmt.Fprint(w, uploadWorkerResponseData)
 	}
-	mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler)
 
-	scriptParams := WorkerScriptParams{
-		Script: workerScript,
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler)
+
+	_, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{
+		ScriptName: "bar",
+		Script:     workerScript,
 		Bindings: map[string]WorkerBinding{
 			"b1": WorkerDurableObjectBinding{
 				ClassName:  "TheClass",
 				ScriptName: "the_script",
 			},
 		},
-	}
-	_, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams)
+	})
+
 	assert.NoError(t, err)
 }
 
-func TestWorkers_UploadWorkerWithInheritBinding(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestUploadWorker_WithInheritBinding(t *testing.T) {
+	setup()
 	defer teardown()
 
 	// Setup route handler for both single-script and multi-script
@@ -666,22 +546,10 @@ func TestWorkers_UploadWorkerWithInheritBinding(t *testing.T) {
 		assert.Equal(t, expectedBindings, mpUpload.BindingMeta)
 
 		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
-	}
-	mux.HandleFunc("/zones/foo/workers/script", handler)
-	mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler)
-
-	scriptParams := WorkerScriptParams{
-		Script: workerScript,
-		Bindings: map[string]WorkerBinding{
-			"b1": WorkerInheritBinding{},
-			"b2": WorkerInheritBinding{
-				OldName: "old_binding_name",
-			},
-		},
+		fmt.Fprint(w, uploadWorkerResponseData)
 	}
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler)
 
-	// Expected response
 	formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z")
 	want := WorkerScriptResponse{
 		successResponse,
@@ -695,21 +563,22 @@ func TestWorkers_UploadWorkerWithInheritBinding(t *testing.T) {
 			},
 		}}
 
-	// Test single-script
-	res, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, &scriptParams)
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-
-	// Test multi-script
-	res, err = client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams)
+	res, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{
+		ScriptName: "bar",
+		Script:     workerScript,
+		Bindings: map[string]WorkerBinding{
+			"b1": WorkerInheritBinding{},
+			"b2": WorkerInheritBinding{
+				OldName: "old_binding_name",
+			},
+		}})
 	if assert.NoError(t, err) {
 		assert.Equal(t, want, res)
 	}
 }
 
-func TestWorkers_UploadWorkerWithKVBinding(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestUploadWorker_WithKVBinding(t *testing.T) {
+	setup()
 	defer teardown()
 
 	handler := func(w http.ResponseWriter, r *http.Request) {
@@ -729,24 +598,23 @@ func TestWorkers_UploadWorkerWithKVBinding(t *testing.T) {
 		assert.Equal(t, expectedBindings, mpUpload.BindingMeta)
 
 		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
+		fmt.Fprint(w, uploadWorkerResponseData)
 	}
-	mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler)
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler)
 
-	scriptParams := WorkerScriptParams{
-		Script: workerScript,
+	_, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{
+		ScriptName: "bar",
+		Script:     workerScript,
 		Bindings: map[string]WorkerBinding{
 			"b1": WorkerKvNamespaceBinding{
 				NamespaceID: "test-namespace",
 			},
-		},
-	}
-	_, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams)
+		}})
 	assert.NoError(t, err)
 }
 
-func TestWorkers_UploadWorkerWithWasmBinding(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestUploadWorker_WithWasmBinding(t *testing.T) {
+	setup()
 	defer teardown()
 
 	handler := func(w http.ResponseWriter, r *http.Request) {
@@ -771,24 +639,25 @@ func TestWorkers_UploadWorkerWithWasmBinding(t *testing.T) {
 		assert.Equal(t, []byte("fake-wasm"), wasmContent)
 
 		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
+		fmt.Fprint(w, uploadWorkerResponseData)
 	}
-	mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler)
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler)
 
-	scriptParams := WorkerScriptParams{
-		Script: workerScript,
+	_, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{
+		ScriptName: "bar",
+		Script:     workerScript,
 		Bindings: map[string]WorkerBinding{
 			"b1": WorkerWebAssemblyBinding{
 				Module: strings.NewReader("fake-wasm"),
 			},
 		},
-	}
-	_, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams)
+	})
+
 	assert.NoError(t, err)
 }
 
-func TestWorkers_UploadWorkerWithPlainTextBinding(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestUploadWorker_WithPlainTextBinding(t *testing.T) {
+	setup()
 	defer teardown()
 
 	handler := func(w http.ResponseWriter, r *http.Request) {
@@ -808,27 +677,28 @@ func TestWorkers_UploadWorkerWithPlainTextBinding(t *testing.T) {
 		assert.Equal(t, expectedBindings, mpUpload.BindingMeta)
 
 		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
+		fmt.Fprint(w, uploadWorkerResponseData)
 	}
-	mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler)
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler)
 
-	scriptParams := WorkerScriptParams{
-		Script: workerScript,
+	_, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{
+		ScriptName: "bar",
+		Script:     workerScript,
 		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_UploadWorkerAsModuleWithPlainTextBinding(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestUploadWorker_ModuleWithPlainTextBinding(t *testing.T) {
+	setup()
 	defer teardown()
 
-	mux.HandleFunc("/accounts/foo/workers/scripts/bar", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/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)
@@ -853,24 +723,25 @@ func TestWorkers_UploadWorkerAsModuleWithPlainTextBinding(t *testing.T) {
 		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
+		fmt.Fprint(w, uploadWorkerModuleResponseData)
 	})
 
-	scriptParams := WorkerScriptParams{
-		Script: workerModuleScript,
-		Module: true,
+	_, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{
+		ScriptName: "bar",
+		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"))
+func TestUploadWorker_WithSecretTextBinding(t *testing.T) {
+	setup()
 	defer teardown()
 
 	handler := func(w http.ResponseWriter, r *http.Request) {
@@ -890,24 +761,24 @@ func TestWorkers_UploadWorkerWithSecretTextBinding(t *testing.T) {
 		assert.Equal(t, expectedBindings, mpUpload.BindingMeta)
 
 		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
+		fmt.Fprint(w, uploadWorkerResponseData)
 	}
-	mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler)
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler)
 
-	scriptParams := WorkerScriptParams{
-		Script: workerScript,
+	_, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{
+		ScriptName: "bar",
+		Script:     workerScript,
 		Bindings: map[string]WorkerBinding{
 			"b1": WorkerSecretTextBinding{
 				Text: "secret text value",
 			},
 		},
-	}
-	_, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams)
+	})
 	assert.NoError(t, err)
 }
 
-func TestWorkers_UploadWorkerWithServiceBinding(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestUploadWorker_WithServiceBinding(t *testing.T) {
+	setup()
 	defer teardown()
 
 	handler := func(w http.ResponseWriter, r *http.Request) {
@@ -933,407 +804,22 @@ func TestWorkers_UploadWorkerWithServiceBinding(t *testing.T) {
 		assert.Equal(t, expectedBindings, mpUpload.BindingMeta)
 
 		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
+		fmt.Fprint(w, uploadWorkerResponseData)
 	}
-	mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler)
+	mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler)
 
-	environment := "the_environment"
-	scriptParams := WorkerScriptParams{
-		Script: workerScript,
+	_, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{
+		ScriptName: "bar",
+		Script:     workerScript,
 		Bindings: map[string]WorkerBinding{
 			"b1": WorkerServiceBinding{
 				Service: "the_service",
 			},
 			"b2": WorkerServiceBinding{
 				Service:     "the_service",
-				Environment: &environment,
+				Environment: StringPtr("the_environment"),
 			},
 		},
-	}
-	_, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams)
-	assert.NoError(t, err)
-}
-
-func TestWorkers_CreateWorkerRoute(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/filters", 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.Fprintf(w, createWorkerRouteResponse) //nolint
 	})
-	route := WorkerRoute{Pattern: "app1.example.com/*", Enabled: true}
-	res, err := client.CreateWorkerRoute(context.Background(), "foo", route)
-	want := WorkerRouteResponse{successResponse, WorkerRoute{ID: "e7a57d8746e74ae49c25994dadb421b1"}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_CreateWorkerRouteEnt(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/routes", 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.Fprintf(w, createWorkerRouteResponse) //nolint
-	})
-	route := WorkerRoute{Pattern: "app1.example.com/*", Script: "test_script"}
-	res, err := client.CreateWorkerRoute(context.Background(), "foo", route)
-	want := WorkerRouteResponse{successResponse, WorkerRoute{ID: "e7a57d8746e74ae49c25994dadb421b1"}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_CreateWorkerRouteSingleScriptWithAccount(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/filters", 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.Fprintf(w, createWorkerRouteResponse) //nolint
-	})
-	route := WorkerRoute{Pattern: "app1.example.com/*", Enabled: true}
-	res, err := client.CreateWorkerRoute(context.Background(), "foo", route)
-	want := WorkerRouteResponse{successResponse, WorkerRoute{ID: "e7a57d8746e74ae49c25994dadb421b1"}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_CreateWorkerRouteErrorsWhenMixingSingleAndMultiScriptProperties(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	route := WorkerRoute{Pattern: "app1.example.com/*", Script: "test_script", Enabled: true}
-	_, err := client.CreateWorkerRoute(context.Background(), "foo", route)
-	assert.EqualError(t, err, "Only `Script` or `Enabled` may be specified for a WorkerRoute, not both")
-}
-
-func TestWorkers_CreateWorkerRouteWithNoScript(t *testing.T) {
-	setup(UsingAccount("foo"))
-
-	mux.HandleFunc("/zones/foo/workers/routes", 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.Fprintf(w, createWorkerRouteResponse) //nolint
-	})
-
-	route := WorkerRoute{Pattern: "app1.example.com/*"}
-	_, err := client.CreateWorkerRoute(context.Background(), "foo", route)
 	assert.NoError(t, err)
 }
-
-func TestWorkers_DeleteWorkerRoute(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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.Fprintf(w, deleteWorkerRouteResponseData) //nolint
-	})
-	res, err := client.DeleteWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1")
-	want := WorkerRouteResponse{successResponse,
-		WorkerRoute{
-			ID: "e7a57d8746e74ae49c25994dadb421b1",
-		}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_DeleteWorkerRouteEnt(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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.Fprintf(w, deleteWorkerRouteResponseData) //nolint
-	})
-	res, err := client.DeleteWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1")
-	want := WorkerRouteResponse{successResponse,
-		WorkerRoute{
-			ID: "e7a57d8746e74ae49c25994dadb421b1",
-		}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_ListWorkerRoutes(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/filters", 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.Fprintf(w, listRouteResponseData) //nolint
-	})
-
-	res, err := client.ListWorkerRoutes(context.Background(), "foo")
-	want := WorkerRoutesResponse{successResponse,
-		[]WorkerRoute{
-			{ID: "e7a57d8746e74ae49c25994dadb421b1", Pattern: "app1.example.com/*", Enabled: true},
-			{ID: "f8b68e9857f85bf59c25994dadb421b1", Pattern: "app2.example.com/*", Enabled: false},
-		},
-	}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_ListWorkerRoutesEnt(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/routes", 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.Fprintf(w, listRouteEntResponseData) //nolint
-	})
-
-	res, err := client.ListWorkerRoutes(context.Background(), "foo")
-	want := WorkerRoutesResponse{successResponse,
-		[]WorkerRoute{
-			{ID: "e7a57d8746e74ae49c25994dadb421b1", Pattern: "app1.example.com/*", Script: "test_script_1", Enabled: true},
-			{ID: "f8b68e9857f85bf59c25994dadb421b1", Pattern: "app2.example.com/*", Script: "test_script_2", Enabled: true},
-			{ID: "2b5bf4240cd34c77852fac70b1bf745a", Pattern: "app3.example.com/*", Script: "", Enabled: false},
-		},
-	}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_GetWorkerRoute(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/routes/1234", 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.Fprintf(w, getRouteResponseData) //nolint
-	})
-
-	res, err := client.GetWorkerRoute(context.Background(), "foo", "1234")
-	want := WorkerRouteResponse{successResponse,
-		WorkerRoute{
-			ID:      "e7a57d8746e74ae49c25994dadb421b1",
-			Pattern: "app1.example.com/*",
-			Script:  "script-name"},
-	}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_UpdateWorkerRoute(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/filters/e7a57d8746e74ae49c25994dadb421b1", 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.Fprintf(w, updateWorkerRouteResponse) //nolint
-	})
-	route := WorkerRoute{Pattern: "app3.example.com/*", Enabled: true}
-	res, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route)
-	want := WorkerRouteResponse{successResponse,
-		WorkerRoute{
-			ID:      "e7a57d8746e74ae49c25994dadb421b1",
-			Pattern: "app3.example.com/*",
-			Enabled: true,
-		}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_UpdateWorkerRouteEnt(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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.Fprintf(w, updateWorkerRouteEntResponse) //nolint
-	})
-	route := WorkerRoute{Pattern: "app3.example.com/*", Script: "test_script_1"}
-	res, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route)
-	want := WorkerRouteResponse{successResponse,
-		WorkerRoute{
-			ID:      "e7a57d8746e74ae49c25994dadb421b1",
-			Pattern: "app3.example.com/*",
-			Script:  "test_script_1",
-		}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_UpdateWorkerRouteSingleScriptWithAccount(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/filters/e7a57d8746e74ae49c25994dadb421b1", 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.Fprintf(w, updateWorkerRouteEntResponse) //nolint
-	})
-	route := WorkerRoute{Pattern: "app3.example.com/*", Enabled: true}
-	res, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route)
-	want := WorkerRouteResponse{successResponse,
-		WorkerRoute{
-			ID:      "e7a57d8746e74ae49c25994dadb421b1",
-			Pattern: "app3.example.com/*",
-			Script:  "test_script_1",
-		}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkers_ListWorkerBindingsMultiScript(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	mux.HandleFunc("/accounts/foo/workers/scripts/my-script/bindings", 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.Fprintf(w, listBindingsResponseData) //nolint
-	})
-
-	mux.HandleFunc("/accounts/foo/workers/scripts/my-script/bindings/MY_WASM/content", 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/wasm")
-		_, _ = w.Write([]byte("mock multi-script wasm"))
-	})
-
-	res, err := client.ListWorkerBindings(context.Background(), &WorkerRequestParams{
-		ScriptName: "my-script",
-	})
-	assert.NoError(t, err)
-
-	assert.Equal(t, successResponse, res.Response)
-	assert.Equal(t, 8, len(res.BindingList))
-
-	assert.Equal(t, res.BindingList[0], WorkerBindingListItem{
-		Name: "MY_KV",
-		Binding: WorkerKvNamespaceBinding{
-			NamespaceID: "89f5f8fd93f94cb98473f6f421aa3b65",
-		},
-	})
-	assert.Equal(t, WorkerKvNamespaceBindingType, res.BindingList[0].Binding.Type())
-
-	assert.Equal(t, "MY_WASM", res.BindingList[1].Name)
-	wasmBinding := res.BindingList[1].Binding.(WorkerWebAssemblyBinding)
-	wasmModuleContent, err := io.ReadAll(wasmBinding.Module)
-	assert.NoError(t, err)
-	assert.Equal(t, []byte("mock multi-script wasm"), wasmModuleContent)
-	assert.Equal(t, WorkerWebAssemblyBindingType, res.BindingList[1].Binding.Type())
-
-	assert.Equal(t, res.BindingList[2], WorkerBindingListItem{
-		Name: "MY_PLAIN_TEXT",
-		Binding: WorkerPlainTextBinding{
-			Text: "text",
-		},
-	})
-	assert.Equal(t, WorkerPlainTextBindingType, res.BindingList[2].Binding.Type())
-
-	assert.Equal(t, res.BindingList[3], WorkerBindingListItem{
-		Name:    "MY_SECRET_TEXT",
-		Binding: WorkerSecretTextBinding{},
-	})
-	assert.Equal(t, WorkerSecretTextBindingType, res.BindingList[3].Binding.Type())
-
-	environment := "MY_ENVIRONMENT"
-	assert.Equal(t, res.BindingList[4], WorkerBindingListItem{
-		Name: "MY_SERVICE_BINDING",
-		Binding: WorkerServiceBinding{
-			Service:     "MY_SERVICE",
-			Environment: &environment,
-		},
-	})
-	assert.Equal(t, WorkerServiceBindingType, res.BindingList[4].Binding.Type())
-
-	assert.Equal(t, res.BindingList[5], WorkerBindingListItem{
-		Name:    "MY_NEW_BINDING",
-		Binding: WorkerInheritBinding{},
-	})
-	assert.Equal(t, WorkerInheritBindingType, res.BindingList[5].Binding.Type())
-
-	assert.Equal(t, res.BindingList[6], WorkerBindingListItem{
-		Name: "MY_BUCKET",
-		Binding: WorkerR2BucketBinding{
-			BucketName: "bucket",
-		},
-	})
-	assert.Equal(t, WorkerR2BucketBindingType, res.BindingList[6].Binding.Type())
-
-	assert.Equal(t, res.BindingList[7], WorkerBindingListItem{
-		Name: "MY_DATASET",
-		Binding: WorkerAnalyticsEngineBinding{
-			Dataset: "my_dataset",
-		},
-	})
-	assert.Equal(t, WorkerAnalyticsEngineBindingType, res.BindingList[7].Binding.Type())
-}
-
-func TestWorkers_UpdateWorkerRouteErrorsWhenMixingSingleAndMultiScriptProperties(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	route := WorkerRoute{Pattern: "app1.example.com/*", Script: "test_script", Enabled: true}
-	_, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route)
-	assert.EqualError(t, err, "Only `Script` or `Enabled` may be specified for a WorkerRoute, not both")
-}
-
-func TestWorkers_UpdateWorkerRouteWithNoScript(t *testing.T) {
-	setup(UsingAccount("foo"))
-	defer teardown()
-
-	mux.HandleFunc("/zones/foo/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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.Fprintf(w, updateWorkerRouteEntResponse) //nolint
-	})
-
-	route := WorkerRoute{Pattern: "app1.example.com/*"}
-	_, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route)
-	assert.NoError(t, err)
-}
-
-func TestWorkers_AttachWorkerToDomain(t *testing.T) {
-	setup(UsingAccount(testAccountID))
-	defer teardown()
-
-	mux.HandleFunc("/accounts/"+testAccountID+"/workers/domains", 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.Fprintf(w, attachWorkerToDomainResponse) //nolint
-	})
-	res, err := client.AttachWorkerToDomain(context.Background(), AccountIdentifier(testAccountID), &WorkerDomainParams{
-		ZoneID:      testZoneID,
-		Hostname:    "app4.example.com",
-		Service:     "test_script_1",
-		Environment: "production",
-	})
-	want := WorkerDomainResponse{
-		successResponse,
-		WorkerDomainResult{
-			ID:          "e7a57d8746e74ae49c25994dadb421b1",
-			ZoneID:      testZoneID,
-			Service:     "test_script_1",
-			Hostname:    "api4.example.com",
-			Environment: "production",
-		}}
-	if assert.NoError(t, err) {
-		assert.Equal(t, want.Response, res.Response)
-	}
-}