The following examples show how to use Cloudflare's REST API and TypeScript SDK to deploy and manage Workers programmatically.
Before using these examples, you need:
- Your Account ID - Found in the Cloudflare dashboard URL or API settings
- A dispatch namespace - Created via the dashboard
- An API token with Workers permissions - Create one at API Tokens ↗
For SDK examples, install the Cloudflare SDK:
npm install cloudflareUpload a Worker script to your dispatch namespace. This is the primary operation your platform performs when customers deploy code.
# First, create the worker script file
cat > worker.mjs << 'EOF'
export default {
async fetch(request, env, ctx) {
return new Response("Hello from user Worker!");
},
};
EOF
# Deploy using multipart form (required for ES modules)
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME" \
-H "Authorization: Bearer $API_TOKEN" \
-F 'metadata={"main_module": "worker.mjs"};type=application/json' \
-F 'worker.mjs=@worker.mjs;type=application/javascript+module'import Cloudflare from "cloudflare";
const client = new Cloudflare({
apiToken: process.env.API_TOKEN,
});
async function deployUserWorker(
accountId: string,
namespace: string,
scriptName: string,
scriptContent: string,
) {
const scriptFile = new File([scriptContent], `${scriptName}.mjs`, {
type: "application/javascript+module",
});
const result =
await client.workersForPlatforms.dispatch.namespaces.scripts.update(
namespace,
scriptName,
{
account_id: accountId,
metadata: {
main_module: `${scriptName}.mjs`,
},
files: [scriptFile],
},
);
return result;
}
// Usage
await deployUserWorker(
"your-account-id",
"production",
"customer-123",
`export default {
async fetch(request, env, ctx) {
return new Response("Hello from customer 123!");
},
};`,
);Use bindings to give each user Worker its own resources like a KV store or database. Use tags to organize Workers by customer ID, project ID, or plan type for bulk operations.
The following example shows how to deploy a Worker with its own KV namespace and tags attached:
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME" \
-H "Authorization: Bearer $API_TOKEN" \
-F 'metadata={"main_module": "worker.mjs", "bindings": [{"type": "kv_namespace", "name": "MY_KV", "namespace_id": "your-kv-namespace-id"}], "tags": ["customer-123", "production", "pro-plan"], "compatibility_date": "2024-01-01"};type=application/json' \
-F 'worker.mjs=@worker.mjs;type=application/javascript+module'import Cloudflare from "cloudflare";
const client = new Cloudflare({
apiToken: process.env.API_TOKEN,
});
async function deployWorkerWithBindingsAndTags(
accountId: string,
namespace: string,
scriptName: string,
scriptContent: string,
kvNamespaceId: string,
tags: string[],
) {
const scriptFile = new File([scriptContent], `${scriptName}.mjs`, {
type: "application/javascript+module",
});
const result =
await client.workersForPlatforms.dispatch.namespaces.scripts.update(
namespace,
scriptName,
{
account_id: accountId,
metadata: {
main_module: `${scriptName}.mjs`,
compatibility_date: "2024-01-01",
bindings: [
{
type: "kv_namespace",
name: "MY_KV",
namespace_id: kvNamespaceId,
},
],
tags: tags, // e.g., ["customer-123", "production", "pro-plan"]
},
files: [scriptFile],
},
);
return result;
}
// Usage
const scriptContent = `export default {
async fetch(request, env, ctx) {
const value = await env.MY_KV.get("key") || "default";
return new Response(value);
},
};`;
await deployWorkerWithBindingsAndTags(
"your-account-id",
"production",
"customer-123-app",
scriptContent,
"kv-namespace-id",
["customer-123", "production", "pro-plan"],
);For more information, refer to Bindings and Tags.
Deploy a Worker that serves static files (HTML, CSS, JavaScript, images). This is a three-step process:
- Create an upload session with a manifest of files
- Upload the asset files
- Deploy the Worker with the assets binding
For more details on static assets configuration and options, refer to Static assets.
Step 1: Create upload session
curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME/assets-upload-session" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"manifest": {
"/index.html": {
"hash": "<sha256-hash-first-16-bytes-hex>",
"size": 1234
},
"/styles.css": {
"hash": "<sha256-hash-first-16-bytes-hex>",
"size": 567
}
}
}'The response includes a jwt token and buckets array indicating which files need uploading.
Step 2: Upload assets
curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/assets/upload?base64=true" \
-H "Authorization: Bearer $JWT_FROM_STEP_1" \
-F '<hash1>=<base64-encoded-content>' \
-F '<hash2>=<base64-encoded-content>'Step 3: Deploy Worker with assets
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME" \
-H "Authorization: Bearer $API_TOKEN" \
-F 'metadata={"main_module": "worker.mjs", "assets": {"jwt": "<completion-token>"}, "bindings": [{"type": "assets", "name": "ASSETS"}]};type=application/json' \
-F 'worker.mjs=export default { async fetch(request, env) { return env.ASSETS.fetch(request); } };type=application/javascript+module'interface AssetFile {
path: string; // e.g., "/index.html"
content: string; // base64 encoded content
size: number; // file size in bytes
}
async function hashContent(base64Content: string): Promise<string> {
const binaryString = atob(base64Content);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
const hashBuffer = await crypto.subtle.digest("SHA-256", bytes);
const hashArray = Array.from(new Uint8Array(hashBuffer));
// Use first 16 bytes (32 hex chars) per API requirement
return hashArray
.slice(0, 16)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
}
async function deployWorkerWithAssets(
accountId: string,
namespace: string,
scriptName: string,
assets: AssetFile[],
) {
const apiToken = process.env.API_TOKEN;
const baseUrl = `https://api.cloudflare.com/client/v4/accounts/${accountId}/workers`;
// Step 1: Build manifest
const manifest: Record<string, { hash: string; size: number }> = {};
const hashToAsset = new Map<string, AssetFile>();
for (const asset of assets) {
const hash = await hashContent(asset.content);
const path = asset.path.startsWith("/") ? asset.path : "/" + asset.path;
manifest[path] = { hash, size: asset.size };
hashToAsset.set(hash, asset);
}
// Step 2: Create upload session
const sessionResponse = await fetch(
`${baseUrl}/dispatch/namespaces/${namespace}/scripts/${scriptName}/assets-upload-session`,
{
method: "POST",
headers: {
Authorization: `Bearer ${apiToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ manifest }),
},
);
const sessionData = (await sessionResponse.json()) as {
success: boolean;
result?: { jwt: string; buckets?: string[][] };
};
if (!sessionData.success || !sessionData.result) {
throw new Error("Failed to create upload session");
}
let completionToken = sessionData.result.jwt;
const buckets = sessionData.result.buckets;
// Step 3: Upload assets in buckets
if (buckets && buckets.length > 0) {
for (const bucket of buckets) {
const formData = new FormData();
for (const hash of bucket) {
const asset = hashToAsset.get(hash);
if (asset) {
formData.append(hash, asset.content);
}
}
const uploadResponse = await fetch(
`${baseUrl}/assets/upload?base64=true`,
{
method: "POST",
headers: { Authorization: `Bearer ${completionToken}` },
body: formData,
},
);
const uploadData = (await uploadResponse.json()) as {
success: boolean;
result?: { jwt?: string };
};
if (uploadData.result?.jwt) {
completionToken = uploadData.result.jwt;
}
}
}
// Step 4: Deploy worker with assets binding
const workerCode = `
export default {
async fetch(request, env) {
return env.ASSETS.fetch(request);
}
};`;
const deployFormData = new FormData();
const metadata = {
main_module: `${scriptName}.mjs`,
assets: { jwt: completionToken },
bindings: [{ type: "assets", name: "ASSETS" }],
};
deployFormData.append(
"metadata",
new Blob([JSON.stringify(metadata)], { type: "application/json" }),
);
deployFormData.append(
`${scriptName}.mjs`,
new Blob([workerCode], { type: "application/javascript+module" }),
);
const deployResponse = await fetch(
`${baseUrl}/dispatch/namespaces/${namespace}/scripts/${scriptName}`,
{
method: "PUT",
headers: { Authorization: `Bearer ${apiToken}` },
body: deployFormData,
},
);
return deployResponse.json();
}
// Usage
await deployWorkerWithAssets("your-account-id", "production", "customer-site", [
{
path: "/index.html",
content: btoa("<html><body>Hello World</body></html>"),
size: 37,
},
{
path: "/styles.css",
content: btoa("body { font-family: sans-serif; }"),
size: 33,
},
]);Retrieve all user Workers deployed to a namespace.
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts" \
-H "Authorization: Bearer $API_TOKEN"async function listWorkers(accountId: string, namespace: string) {
const response = await fetch(
`https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/dispatch/namespaces/${namespace}/scripts`,
{
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
},
},
);
const data = (await response.json()) as {
success: boolean;
result: Array<{ id: string; tags?: string[] }>;
};
return data.result;
}
// Usage
const workers = await listWorkers("your-account-id", "production");
console.log(workers);Delete all Workers matching a tag filter. This is useful when a customer deletes their account and you need to remove all their Workers at once.
Delete all Workers tagged with customer-123:
curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts?tags=customer-123:yes" \
-H "Authorization: Bearer $API_TOKEN"async function deleteWorkersByTag(
accountId: string,
namespace: string,
tag: string,
) {
const response = await fetch(
`https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/dispatch/namespaces/${namespace}/scripts?tags=${tag}:yes`,
{
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
},
},
);
return response.json();
}
// Usage: Delete all Workers for a customer
await deleteWorkersByTag("your-account-id", "production", "customer-123");Delete a specific Worker by name.
curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME" \
-H "Authorization: Bearer $API_TOKEN"import Cloudflare from "cloudflare";
const client = new Cloudflare({
apiToken: process.env.API_TOKEN,
});
async function deleteWorker(
accountId: string,
namespace: string,
scriptName: string,
) {
const result =
await client.workersForPlatforms.dispatch.namespaces.scripts.delete(
namespace,
scriptName,
{ account_id: accountId },
);
return result;
}
// Usage
await deleteWorker("your-account-id", "production", "customer-123");