From 1e658029ffde61efaf2586c227b26f3ecf42bc56 Mon Sep 17 00:00:00 2001 From: Logan Rosen Date: Fri, 10 Jul 2026 21:58:20 -0400 Subject: [PATCH 1/3] Correct Cloudflare Tunnel API reference Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: faf22a37-1023-405e-afc5-3871e8f1dab0 --- skills/cloudflare/references/tunnel/api.md | 230 ++++++++++----------- 1 file changed, 108 insertions(+), 122 deletions(-) diff --git a/skills/cloudflare/references/tunnel/api.md b/skills/cloudflare/references/tunnel/api.md index faa3013..829219d 100644 --- a/skills/cloudflare/references/tunnel/api.md +++ b/skills/cloudflare/references/tunnel/api.md @@ -1,193 +1,179 @@ -# Tunnel API +# Cloudflare Tunnel API -## Cloudflare API Access +Retrieve the current [Cloudflare Tunnel API documentation](https://developers.cloudflare.com/api/resources/zero_trust/subresources/tunnels/) before changing Tunnel resources. -**Base URL**: `https://api.cloudflare.com/client/v4` +## Authentication -**Authentication**: -```bash -Authorization: Bearer ${CF_API_TOKEN} +Base URL: `https://api.cloudflare.com/client/v4` + +Use an API token with Cloudflare Tunnel write access: + +```http +Authorization: Bearer +Content-Type: application/json ``` +Never print or persist connector tokens. + ## TypeScript SDK -Install: `npm install cloudflare` +Install the current SDK: + +```bash +npm install cloudflare +``` ```typescript import Cloudflare from 'cloudflare'; const cf = new Cloudflare({ - apiToken: process.env.CF_API_TOKEN, + apiToken: process.env.CLOUDFLARE_API_TOKEN, }); -const accountId = process.env.CF_ACCOUNT_ID; +const accountId = process.env.CLOUDFLARE_ACCOUNT_ID!; ``` -## Create Tunnel +Cloudflared tunnels are under `zeroTrust.tunnels.cloudflared`, not directly under `zeroTrust.tunnels`. -### cURL -```bash -curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" \ - -H "Content-Type: application/json" \ - --data '{ - "name": "my-tunnel", - "tunnel_secret": "" - }' +## Tunnel lifecycle + +### Create a remotely managed tunnel + +```http +POST /accounts/{account_id}/cfd_tunnel +``` + +```json +{ + "name": "my-tunnel", + "config_src": "cloudflare" +} ``` -### TypeScript ```typescript -const tunnel = await cf.zeroTrust.tunnels.create({ +const tunnel = await cf.zeroTrust.tunnels.cloudflared.create({ account_id: accountId, name: 'my-tunnel', - tunnel_secret: Buffer.from(crypto.randomBytes(32)).toString('base64'), + config_src: 'cloudflare', }); - -console.log(`Tunnel ID: ${tunnel.id}`); ``` -## List Tunnels +Use `config_src: "cloudflare"` for token-based, remotely managed tunnels. Use `config_src: "local"` and provide a base64-encoded `tunnel_secret` for locally managed tunnels. -### cURL -```bash -curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" +### List tunnels + +```http +GET /accounts/{account_id}/cfd_tunnel?is_deleted=false ``` -### TypeScript ```typescript -const tunnels = await cf.zeroTrust.tunnels.list({ +for await (const tunnel of cf.zeroTrust.tunnels.cloudflared.list({ account_id: accountId, -}); - -for (const tunnel of tunnels.result) { + is_deleted: false, +})) { console.log(`${tunnel.name}: ${tunnel.id}`); } ``` -## Get Tunnel Info +Useful response fields include `id`, `name`, `config_src`, `status`, `conns_active_at`, and `conns_inactive_at`. -### cURL -```bash -curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" +### Get or delete a tunnel + +```http +GET /accounts/{account_id}/cfd_tunnel/{tunnel_id} +DELETE /accounts/{account_id}/cfd_tunnel/{tunnel_id} ``` -### TypeScript ```typescript -const tunnel = await cf.zeroTrust.tunnels.get(tunnelId, { +const tunnel = await cf.zeroTrust.tunnels.cloudflared.get(tunnelId, { account_id: accountId, }); -console.log(`Status: ${tunnel.status}`); -console.log(`Connections: ${tunnel.connections?.length || 0}`); +await cf.zeroTrust.tunnels.cloudflared.delete(tunnelId, { + account_id: accountId, +}); ``` -## Update Tunnel Config +## Remote configuration -### cURL -```bash -curl -X PUT "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/configurations" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" \ - -H "Content-Type: application/json" \ - --data '{ - "config": { - "ingress": [ - {"hostname": "app.example.com", "service": "http://localhost:8000"}, - {"service": "http_status:404"} - ] - } - }' -``` - -### TypeScript -```typescript -const config = await cf.zeroTrust.tunnels.configurations.update( - tunnelId, - { - account_id: accountId, - config: { - ingress: [ - { hostname: 'app.example.com', service: 'http://localhost:8000' }, - { service: 'http_status:404' }, - ], - }, +### Set ingress rules + +```http +PUT /accounts/{account_id}/cfd_tunnel/{tunnel_id}/configurations +``` + +```json +{ + "config": { + "ingress": [ + { + "hostname": "app.example.com", + "service": "http://origin:8000" + }, + { + "service": "http_status:404" + } + ] } -); +} ``` -## Delete Tunnel +Always include a final catch-all ingress rule. -### cURL -```bash -curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" +### Retrieve the connector token + +```http +GET /accounts/{account_id}/cfd_tunnel/{tunnel_id}/token ``` -### TypeScript ```typescript -await cf.zeroTrust.tunnels.delete(tunnelId, { +const token = await cf.zeroTrust.tunnels.cloudflared.token.get(tunnelId, { account_id: accountId, }); ``` -## Token-Based Tunnels (Config Source: Cloudflare) +The response is the secret connector token. Feed it directly into the target secret or configuration API without logging it. -Token-based tunnels store config in Cloudflare dashboard instead of local files. +Run the connector with the token: -### Via Dashboard -1. **Zero Trust** > **Networks** > **Tunnels** -2. **Create a tunnel** > **Cloudflared** -3. Configure routes in dashboard -4. Copy token -5. Run on origin: ```bash -cloudflared service install +cloudflared tunnel --no-autoupdate run --token "$TUNNEL_TOKEN" ``` -### Via Token -```bash -# Run with token (no config file needed) -cloudflared tunnel --no-autoupdate run --token ${TUNNEL_TOKEN} +## Public-hostname DNS -# Docker -docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token ${TUNNEL_TOKEN} -``` +Public hostnames use proxied CNAME records pointing to `.cfargotunnel.com`. For a locally managed tunnel with `cert.pem` installed, create one with `cloudflared`: -### Get Tunnel Token (TypeScript) -```typescript -// Get tunnel to retrieve token -const tunnel = await cf.zeroTrust.tunnels.get(tunnelId, { - account_id: accountId, -}); - -// Token available in tunnel.token (only for config source: cloudflare) -const token = tunnel.token; +```bash +cloudflared tunnel route dns my-tunnel app.example.com ``` -## DNS Routes API +Or use the DNS Records API: -```bash -# Create DNS route -curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/connections" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" \ - --data '{"hostname": "app.example.com"}' +```http +POST /zones/{zone_id}/dns_records +``` -# Delete route -curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/connections/{route_id}" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" +```json +{ + "type": "CNAME", + "name": "app.example.com", + "content": ".cfargotunnel.com", + "proxied": true +} ``` -## Private Network Routes API +When moving an existing hostname, update its current DNS record rather than creating a duplicate. -```bash -# Add IP route -curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/routes" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" \ - --data '{"ip_network": "10.0.0.0/8"}' +## Private-network routes -# List IP routes -curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/routes" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" +Private WARP routes use the account-level Teamnet API: + +```http +GET /accounts/{account_id}/teamnet/routes +POST /accounts/{account_id}/teamnet/routes +PATCH /accounts/{account_id}/teamnet/routes/{route_id} +DELETE /accounts/{account_id}/teamnet/routes/{route_id} ``` + +Retrieve the current route schema before creating or modifying private routes. From d95975ca9812e2eb781b4cdc719ba03f9203c133 Mon Sep 17 00:00:00 2001 From: Logan Rosen Date: Fri, 10 Jul 2026 22:14:12 -0400 Subject: [PATCH 2/3] Narrow Tunnel API corrections Restore the existing reference structure and limit the change to broken REST endpoints and SDK calls. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: faf22a37-1023-405e-afc5-3871e8f1dab0 --- skills/cloudflare/references/tunnel/api.md | 218 +++++++++++---------- 1 file changed, 111 insertions(+), 107 deletions(-) diff --git a/skills/cloudflare/references/tunnel/api.md b/skills/cloudflare/references/tunnel/api.md index 829219d..54ffcdc 100644 --- a/skills/cloudflare/references/tunnel/api.md +++ b/skills/cloudflare/references/tunnel/api.md @@ -1,179 +1,183 @@ -# Cloudflare Tunnel API +# Tunnel API -Retrieve the current [Cloudflare Tunnel API documentation](https://developers.cloudflare.com/api/resources/zero_trust/subresources/tunnels/) before changing Tunnel resources. +## Cloudflare API Access -## Authentication +**Base URL**: `https://api.cloudflare.com/client/v4` -Base URL: `https://api.cloudflare.com/client/v4` - -Use an API token with Cloudflare Tunnel write access: - -```http -Authorization: Bearer -Content-Type: application/json +**Authentication**: +```bash +Authorization: Bearer ${CF_API_TOKEN} ``` -Never print or persist connector tokens. - ## TypeScript SDK -Install the current SDK: - -```bash -npm install cloudflare -``` +Install: `npm install cloudflare` ```typescript import Cloudflare from 'cloudflare'; const cf = new Cloudflare({ - apiToken: process.env.CLOUDFLARE_API_TOKEN, + apiToken: process.env.CF_API_TOKEN, }); -const accountId = process.env.CLOUDFLARE_ACCOUNT_ID!; +const accountId = process.env.CF_ACCOUNT_ID; ``` -Cloudflared tunnels are under `zeroTrust.tunnels.cloudflared`, not directly under `zeroTrust.tunnels`. - -## Tunnel lifecycle - -### Create a remotely managed tunnel +## Create Tunnel -```http -POST /accounts/{account_id}/cfd_tunnel -``` - -```json -{ - "name": "my-tunnel", - "config_src": "cloudflare" -} +### cURL +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/cfd_tunnel" \ + -H "Authorization: Bearer ${CF_API_TOKEN}" \ + -H "Content-Type: application/json" \ + --data '{ + "name": "my-tunnel", + "config_src": "cloudflare" + }' ``` +### TypeScript ```typescript const tunnel = await cf.zeroTrust.tunnels.cloudflared.create({ account_id: accountId, name: 'my-tunnel', config_src: 'cloudflare', }); -``` -Use `config_src: "cloudflare"` for token-based, remotely managed tunnels. Use `config_src: "local"` and provide a base64-encoded `tunnel_secret` for locally managed tunnels. +console.log(`Tunnel ID: ${tunnel.id}`); +``` -### List tunnels +## List Tunnels -```http -GET /accounts/{account_id}/cfd_tunnel?is_deleted=false +### cURL +```bash +curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/cfd_tunnel" \ + -H "Authorization: Bearer ${CF_API_TOKEN}" ``` +### TypeScript ```typescript for await (const tunnel of cf.zeroTrust.tunnels.cloudflared.list({ account_id: accountId, - is_deleted: false, })) { console.log(`${tunnel.name}: ${tunnel.id}`); } ``` -Useful response fields include `id`, `name`, `config_src`, `status`, `conns_active_at`, and `conns_inactive_at`. - -### Get or delete a tunnel +## Get Tunnel Info -```http -GET /accounts/{account_id}/cfd_tunnel/{tunnel_id} -DELETE /accounts/{account_id}/cfd_tunnel/{tunnel_id} +### cURL +```bash +curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/cfd_tunnel/{tunnel_id}" \ + -H "Authorization: Bearer ${CF_API_TOKEN}" ``` +### TypeScript ```typescript const tunnel = await cf.zeroTrust.tunnels.cloudflared.get(tunnelId, { account_id: accountId, }); -await cf.zeroTrust.tunnels.cloudflared.delete(tunnelId, { - account_id: accountId, -}); -``` - -## Remote configuration - -### Set ingress rules - -```http -PUT /accounts/{account_id}/cfd_tunnel/{tunnel_id}/configurations -``` - -```json -{ - "config": { - "ingress": [ - { - "hostname": "app.example.com", - "service": "http://origin:8000" - }, - { - "service": "http_status:404" - } - ] - } -} +console.log(`Status: ${tunnel.status}`); +console.log(`Connections: ${tunnel.connections?.length || 0}`); ``` -Always include a final catch-all ingress rule. - -### Retrieve the connector token +## Update Tunnel Config -```http -GET /accounts/{account_id}/cfd_tunnel/{tunnel_id}/token +### cURL +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/{account_id}/cfd_tunnel/{tunnel_id}/configurations" \ + -H "Authorization: Bearer ${CF_API_TOKEN}" \ + -H "Content-Type: application/json" \ + --data '{ + "config": { + "ingress": [ + {"hostname": "app.example.com", "service": "http://localhost:8000"}, + {"service": "http_status:404"} + ] + } + }' +``` + +## Delete Tunnel + +### cURL +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/{account_id}/cfd_tunnel/{tunnel_id}" \ + -H "Authorization: Bearer ${CF_API_TOKEN}" ``` +### TypeScript ```typescript -const token = await cf.zeroTrust.tunnels.cloudflared.token.get(tunnelId, { +await cf.zeroTrust.tunnels.cloudflared.delete(tunnelId, { account_id: accountId, }); ``` -The response is the secret connector token. Feed it directly into the target secret or configuration API without logging it. +## Token-Based Tunnels (Config Source: Cloudflare) -Run the connector with the token: +Token-based tunnels store config in Cloudflare dashboard instead of local files. +### Via Dashboard +1. **Zero Trust** > **Networks** > **Tunnels** +2. **Create a tunnel** > **Cloudflared** +3. Configure routes in dashboard +4. Copy token +5. Run on origin: ```bash -cloudflared tunnel --no-autoupdate run --token "$TUNNEL_TOKEN" +cloudflared service install ``` -## Public-hostname DNS - -Public hostnames use proxied CNAME records pointing to `.cfargotunnel.com`. For a locally managed tunnel with `cert.pem` installed, create one with `cloudflared`: - +### Via Token ```bash -cloudflared tunnel route dns my-tunnel app.example.com -``` - -Or use the DNS Records API: +# Run with token (no config file needed) +cloudflared tunnel --no-autoupdate run --token ${TUNNEL_TOKEN} -```http -POST /zones/{zone_id}/dns_records +# Docker +docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token ${TUNNEL_TOKEN} ``` -```json -{ - "type": "CNAME", - "name": "app.example.com", - "content": ".cfargotunnel.com", - "proxied": true -} +### Get Tunnel Token (TypeScript) +```typescript +const token = await cf.zeroTrust.tunnels.cloudflared.token.get(tunnelId, { + account_id: accountId, +}); ``` -When moving an existing hostname, update its current DNS record rather than creating a duplicate. +## DNS Routes API -## Private-network routes +Public hostnames use proxied CNAME records pointing to `.cfargotunnel.com`. -Private WARP routes use the account-level Teamnet API: +```bash +# Create DNS route +curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records" \ + -H "Authorization: Bearer ${CF_API_TOKEN}" \ + -H "Content-Type: application/json" \ + --data '{ + "type": "CNAME", + "name": "app.example.com", + "content": ".cfargotunnel.com", + "proxied": true + }' -```http -GET /accounts/{account_id}/teamnet/routes -POST /accounts/{account_id}/teamnet/routes -PATCH /accounts/{account_id}/teamnet/routes/{route_id} -DELETE /accounts/{account_id}/teamnet/routes/{route_id} +# Delete route +curl -X DELETE "https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{dns_record_id}" \ + -H "Authorization: Bearer ${CF_API_TOKEN}" ``` -Retrieve the current route schema before creating or modifying private routes. +## Private Network Routes API + +```bash +# Add IP route +curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/teamnet/routes" \ + -H "Authorization: Bearer ${CF_API_TOKEN}" \ + -H "Content-Type: application/json" \ + --data '{ + "network": "10.0.0.0/8", + "tunnel_id": "" + }' + +# List IP routes +curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/teamnet/routes" \ + -H "Authorization: Bearer ${CF_API_TOKEN}" +``` From e5a1094ffc6d608fa32375b91cd86c959caa2c46 Mon Sep 17 00:00:00 2001 From: Logan Rosen Date: Fri, 10 Jul 2026 22:15:20 -0400 Subject: [PATCH 3/3] Preserve existing Tunnel examples Limit the final diff to correcting broken REST paths, SDK namespaces, and route request shapes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: faf22a37-1023-405e-afc5-3871e8f1dab0 --- skills/cloudflare/references/tunnel/api.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/skills/cloudflare/references/tunnel/api.md b/skills/cloudflare/references/tunnel/api.md index 54ffcdc..3711b57 100644 --- a/skills/cloudflare/references/tunnel/api.md +++ b/skills/cloudflare/references/tunnel/api.md @@ -32,7 +32,7 @@ curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/cfd_tun -H "Content-Type: application/json" \ --data '{ "name": "my-tunnel", - "config_src": "cloudflare" + "tunnel_secret": "" }' ``` @@ -41,7 +41,7 @@ curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/cfd_tun const tunnel = await cf.zeroTrust.tunnels.cloudflared.create({ account_id: accountId, name: 'my-tunnel', - config_src: 'cloudflare', + tunnel_secret: Buffer.from(crypto.randomBytes(32)).toString('base64'), }); console.log(`Tunnel ID: ${tunnel.id}`); @@ -99,6 +99,22 @@ curl -X PUT "https://api.cloudflare.com/client/v4/accounts/{account_id}/cfd_tunn }' ``` +### TypeScript +```typescript +const config = await cf.zeroTrust.tunnels.cloudflared.configurations.update( + tunnelId, + { + account_id: accountId, + config: { + ingress: [ + { hostname: 'app.example.com', service: 'http://localhost:8000' }, + { service: 'http_status:404' }, + ], + }, + } +); +``` + ## Delete Tunnel ### cURL