Skip to main content
POST
/
v1
/
tunnels
Create Tunnel
curl --request POST \
  --url https://api.example.com/v1/tunnels \
  --header 'Content-Type: application/json' \
  --data '
{
  "port": 123,
  "requestedSlug": "<string>"
}
'
{
  "tunnelId": "<string>",
  "hostname": "<string>",
  "cloudflaredToken": "<string>",
  "heartbeatIntervalSec": 123,
  "INVALID_INPUT": {},
  "INVALID_PORT": {},
  "TUNNEL_SLUG_CONFLICT": {},
  "TUNNEL_LIMIT_EXCEEDED": {},
  "SLUG_EXHAUSTED": {}
}
Creates a new tunnel that exposes a local port to the internet via a public hostname.

Authentication

Requires a valid access token in the Authorization header:
Authorization: Bearer <access_token>

Rate Limiting

This endpoint is rate-limited to 20 requests per minute per user.

Request Body

port
number
required
The local port number to expose. Must be an integer between 1 and 65535.
requestedSlug
string
Optional custom subdomain slug for your tunnel. If not provided, a random slug will be generated.

Response

tunnelId
string
UUID identifier for the created tunnel.
hostname
string
The public hostname assigned to this tunnel (e.g., my-app.example.com).
cloudflaredToken
string
Authentication token for the cloudflared client. Use this to establish the tunnel connection.
This token is only returned once during tunnel creation. Store it securely.
heartbeatIntervalSec
number
The interval (in seconds) at which the client must send heartbeat requests. Always 20.

Error Responses

INVALID_INPUT
400
The request body is invalid or missing required fields.
INVALID_PORT
400
Port number is not between 1 and 65535.
TUNNEL_SLUG_CONFLICT
409
The requested slug is already in use by another active tunnel.
TUNNEL_LIMIT_EXCEEDED
429
You have reached the maximum number of active tunnels (5 per user).
SLUG_EXHAUSTED
503
Unable to generate a unique random slug after multiple attempts.

Example Request

curl -X POST https://api.example.com/v1/tunnels \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "port": 3000,
    "requestedSlug": "my-app"
  }'

Example Response

{
  "tunnelId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "hostname": "my-app.example.com",
  "cloudflaredToken": "eyJhIjoiYTFiMmMzZDRlNWY2Nzg5MGFiY2RlZjEyMzQ1Njc4OTAiLCJ0IjoiYTFiMmMzZDQtZTVmNi03ODkwLWFiY2QtZWYxMjM0NTY3ODkwIiwicyI6IlltRnpaVFkwWlc1amIyUmxaRFJsZUdGdGNHeGxkRzlyWlc0PSJ9",
  "heartbeatIntervalSec": 20
}

Next Steps

After creating a tunnel:
  1. Use the cloudflaredToken to start the cloudflared daemon
  2. Send heartbeat requests every 20 seconds to keep the tunnel alive
  3. Your local application on the specified port will be accessible via the public hostname
Tunnels will automatically expire if heartbeats are not received within 60 seconds.

Build docs developers (and LLMs) love