API Reference
Overview
The Jetty API provides programmatic access to tunnel management, authentication, and operational metrics. All endpoints return JSON responses and follow RESTful conventions.
Base URL: https://usejetty.online/api (or your self-hosted instance)
API Version: Current version is included in responses where applicable. The API follows semantic versioning principles.
Rate Limiting: Most endpoints implement rate limiting to prevent abuse. Limits vary by endpoint and authentication method. Rate limit responses return HTTP 429 Too Many Requests.
Content Type: All request bodies should be application/json unless otherwise specified.
Authentication
The Jetty API supports three authentication methods depending on the endpoint:
Personal Access Tokens (Sanctum)
Most tunnel management endpoints require a personal access token generated through the Jetty dashboard.
Header Format:
Authorization: Bearer YOUR_TOKEN_HERE
Tokens are created in the dashboard under Settings → API Tokens. They inherit the permissions of the user who created them.
CLI Browser Authentication
The CLI uses a browser-based OAuth flow to obtain tokens without requiring users to manually copy/paste credentials.
Flow:
- CLI calls
POST /api/cli/auth/sessionto initiate authentication - User opens the
authorize_urlin their browser and logs in - CLI polls
GET /api/cli/auth/polluntil the user completes authentication - CLI receives a token and optional configuration
Edge Authentication
The jetty-edge ingress process authenticates using a shared secret configured on both the Bridge and edge servers.
Header Format:
X-Jetty-Edge-Secret: YOUR_EDGE_SECRET
This is set via the JETTY_EDGE_SHARED_SECRET environment variable.
Fleet Operator Authentication
Operator endpoints for monitoring and metrics require a fleet operator token.
Header Formats (either works):
Authorization: Bearer YOUR_FLEET_TOKEN
or
X-Fleet-Operator-Token: YOUR_FLEET_TOKEN
Configured via FLEET_OPERATOR_TOKEN environment variable.
Common Patterns
Pagination
List endpoints that support pagination return data in this format:
{
"data": [...],
"meta": {
"current_page": 1,
"per_page": 15,
"total": 42
},
"links": {
"first": "https://usejetty.online/api/tunnels?page=1",
"last": "https://usejetty.online/api/tunnels?page=3",
"next": "https://usejetty.online/api/tunnels?page=2",
"prev": null
}
}
Error Responses
All errors follow a consistent format:
Validation Error (422):
{
"message": "The local_port field is required.",
"errors": {
"local_port": ["The local_port field is required."]
}
}
Unauthorized (401):
{
"message": "Unauthenticated."
}
Forbidden (403):
{
"message": "This action is unauthorized."
}
Not Found (404):
{
"message": "Resource not found."
}
Rate Limited (429):
{
"message": "Too many attempts."
}
Server Error (500):
{
"message": "Server error occurred."
}
Timestamps
All timestamps use ISO 8601 format: 2024-01-15T10:30:00+00:00
Endpoints
Public Endpoints
GET /api/cli/bootstrap
Returns configuration metadata for CLI initialization.
Authentication: None (public)
Response:
{
"app_name": "Jetty",
"cli_version": "0.1.19",
"servers": [
{
"name": "Jetty Cloud",
"url": "https://usejetty.online"
}
]
}
Example:
curl https://usejetty.online/api/cli/bootstrap
POST /api/cli/auth/session
Initiates a browser-based authentication session for the CLI.
Authentication: None (public)
Request Body: None required
Response (201 Created):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"authorize_url": "https://usejetty.online/cli/auth?id=550e8400-e29b-41d4-a716-446655440000",
"poll_url": "https://usejetty.online/api/cli/auth/poll?id=550e8400-e29b-41d4-a716-446655440000",
"expires_in": 600
}
Rate Limit: 20 requests per minute per IP
Errors:
429- Too many authentication attempts
Example:
curl -X POST https://usejetty.online/api/cli/auth/session
GET /api/cli/auth/poll
Polls the status of a CLI authentication session.
Authentication: None (public)
Query Parameters:
id(required): UUID from the session creation response
Response (Pending):
{
"status": "pending"
}
Response (Success):
{
"status": "success",
"token": "1|abcdef123456...",
"config": {
"organization_id": "12345",
"default_region": "us-east"
}
}
Response (Expired):
{
"status": "expired"
}
Rate Limit: 120 requests per minute per IP
Errors:
422- Invalid session ID429- Too many polling attempts
Example:
curl "https://usejetty.online/api/cli/auth/poll?id=550e8400-e29b-41d4-a716-446655440000"
POST /api/edge/tunnel/verify
Verifies a tunnel's agent token for edge connection establishment.
Authentication: Edge secret (X-Jetty-Edge-Secret header)
Request Body:
{
"tunnel_id": "abc123",
"agent_token": "plain_token_from_tunnel_creation"
}
Response (Valid):
{
"valid": true,
"subdomain": "my-site",
"allowed_ips": ["203.0.113.10", "203.0.113.20"],
"blocked_ips": []
}
Response (Invalid - 403):
{
"valid": false
}
Errors:
403- Invalid edge secret or tunnel credentials422- Validation error
Example:
curl -X POST https://usejetty.online/api/edge/tunnel/verify \
-H "X-Jetty-Edge-Secret: your_edge_secret" \
-H "Content-Type: application/json" \
-d '{
"tunnel_id": "abc123",
"agent_token": "plain_token_here"
}'
POST /api/edge/events
Receives event logs and telemetry from jetty-edge ingress processes.
Authentication: Edge secret (X-Jetty-Edge-Secret header)
Request Body:
{
"level": "error",
"event": "tunnel_connection_failed",
"message": "WebSocket connection refused",
"context": {
"tunnel_id": "abc123",
"error_code": "ECONNREFUSED"
}
}
Request Parameters:
level(required): Log level (debug, info, warning, error)event(required): Event type identifiermessage(required): Human-readable messagecontext(optional): Additional structured data
Response (204 No Content): Success, no body returned
Rate Limit: Body size limited to 16 KB
Errors:
403- Invalid edge secret413- Payload too large422- Validation error
Example:
curl -X POST https://usejetty.online/api/edge/events \
-H "X-Jetty-Edge-Secret: your_edge_secret" \
-H "Content-Type: application/json" \
-d '{
"level": "info",
"event": "tunnel_connected",
"message": "Tunnel successfully connected",
"context": {"tunnel_id": "abc123"}
}'
POST /api/telemetry/edge-ws-failure
Reports WebSocket connection failures from CLI clients for monitoring and debugging.
Authentication: None (public, rate-limited)
Request Body:
{
"http_status": 502,
"reason": "Bad Gateway: edge server unreachable",
"client": "jetty-php/0.1.19"
}
Request Parameters:
http_status(required): HTTP status code (100-599)reason(optional): Error description (max 200 chars)client(optional): Client identifier (max 64 chars)
Response (204 No Content): Success, no body returned
Rate Limit: Configured per instance (default: 30 requests per minute)
Errors:
422- Validation error429- Rate limit exceeded
Example:
curl -X POST https://usejetty.online/api/telemetry/edge-ws-failure \
-H "Content-Type: application/json" \
-d '{
"http_status": 503,
"reason": "Service Unavailable",
"client": "jetty-php/0.1.19"
}'
Operator Endpoints
GET /api/operator/summary
Returns fleet-wide metrics and statistics for monitoring.
Authentication: Fleet operator token (Bearer or X-Fleet-Operator-Token)
Response:
{
"app": "jetty",
"generated_at": "2024-01-15T10:30:00+00:00",
"environment": "production",
"users": 1250,
"organizations": 342,
"metrics": {
"teams": 89,
"tunnels": 1567
}
}
Errors:
401- Missing or invalid operator token503- Operator API not configured
Example:
curl https://usejetty.online/api/operator/summary \
-H "Authorization: Bearer your_fleet_token"
GET /api/operator/readme
Returns structured README data for operator dashboards.
Authentication: Fleet operator token (Bearer or X-Fleet-Operator-Token)
Response: JSON-formatted README content (structure varies)
Errors:
401- Missing or invalid operator token503- Operator API not configured
Example:
curl https://usejetty.online/api/operator/readme \
-H "Authorization: Bearer your_fleet_token"
Authenticated Endpoints (Sanctum)
GET /api/user
Returns the currently authenticated user's information.
Authentication: Bearer token (Sanctum)
Response:
{
"id": 1,
"name": "Jane Developer",
"email": "jane@example.com",
"email_verified_at": "2024-01-10T08:00:00+00:00",
"created_at": "2024-01-01T00:00:00+00:00",
"updated_at": "2024-01-15T10:30:00+00:00",
"current_organization_id": 5
}
Errors:
401- Invalid or missing token
Example:
curl https://usejetty.online/api/user \
-H "Authorization: Bearer 1|abcdef123456..."
GET /api/organization/teams
** Deprecated**: This endpoint is maintained for backward compatibility. Organizations are now the primary entity.
Lists teams within the user's current organization.
Authentication: Bearer token (Sanctum)
Response:
{
"data": [
{
"id": 1,
"name": "Development Team",
"slug": "development",
"created_at": "2024-01-01T00:00:00+00:00"
}
]
}
Errors:
401- Invalid or missing token403- User not authorized to view teams
Example:
curl https://usejetty.online/api/organization/teams \
-H "Authorization: Bearer 1|abcdef123456..."
POST /api/organization/teams
** Deprecated**: This endpoint is maintained for backward compatibility.
Creates a new team within the user's organization.
Authentication: Bearer token (Sanctum)
Request Body:
{
"name": "QA Team",
"slug": "qa"
}
Response (201 Created):
{
"data": {
"id": 2,
"name": "QA Team",
"slug": "qa",
"created_at": "2024-01-15T10:30:00+00:00"
}
}
Errors:
401- Invalid or missing token403- User not authorized to create teams422- Validation error
Example:
curl -X POST https://usejetty.online/api/organization/teams \
-H "Authorization: Bearer 1|abcdef123456..." \
-H "Content-Type: application/json" \
-d '{
"name": "QA Team",
"slug": "qa"
}'
GET /api/organization/members
Lists members of the user's current organization.
Authentication: Bearer token (Sanctum)
Response:
{
"data": [
{
"id": 1,
"name": "Jane Developer",
"email": "jane@example.com",
"organization_role": "owner"
},
{
"id": 2,
"name": "John Smith",
"email": "john@example.com",
"organization_role": "member"
}
]
}
Errors:
401- Invalid or missing token403- User not a member of any organization
Example:
curl https://usejetty.online/api/organization/members \
-H "Authorization: Bearer 1|abcdef123456..."
GET /api/reserved-subdomains
Lists reserved tunnel subdomains for the user's organization.
Authentication: Bearer token (Sanctum)
Response:
{
"data": [
{
"slug": "my-app",
"full_host_hint": "https://my-app.tunnels.usejetty.online"
},
{
"slug": "staging",
"full_host_hint": "https://staging.tunnels.usejetty.online"
}
],
"meta": {
"tunnel_host_suffix": "tunnels.usejetty.online"
}
}
Errors:
401- Invalid or missing token403- User not authorized
Example:
curl https://usejetty.online/api/reserved-subdomains \
-H "Authorization: Bearer 1|abcdef123456..."
GET /api/tunnels
Lists all tunnels for the user's organization.
Authentication: Bearer token (Sanctum)
Response:
{
"data": [
{
"id": "abc123",
"subdomain": "my-site",
"server": null,
"public_url": "https://my-site.tunnels.usejetty.online",
"local_target": "http://127.0.0.1:8000",
"status": "live",
"last_seen_at": "2024-01-15T10:28:00+00:00",
"created_at": "2024-01-15T09:00:00+00:00",
"requests_total": 1523,
"bytes_in_total": 2048576,
"bytes_out_total": 8192000,
"note": "Development server",
"routing_rules": []
}
]
}
Errors:
401- Invalid or missing token403- User not authorized
Example:
curl https://usejetty.online/api/tunnels \
-H "Authorization: Bearer 1|abcdef123456..."
POST /api/tunnels
Creates a new tunnel.
Authentication: Bearer token (Sanctum)
Request Body:
{
"local_host": "127.0.0.1",
"local_port": 8000,
"subdomain": "my-site",
"server": "us-east",
"routing_rules": []
}
Request Parameters:
local_host(optional): Local hostname or IP (default:127.0.0.1). Max 255 chars, alphanumeric with dots/dashes.local_port(required): Local port number (1-65535)subdomain(optional): Requested subdomain (max 63 chars). If null, auto-assigned.server(optional): Target server region identifier (max 64 chars)routing_rules(optional): Array of path-based routing rules
Response (201 Created):
{
"data": {
"id": "abc123",
"subdomain": "my-site",
"server": "us-east",
"public_url": "https://my-site.tunnels.usejetty.online",
"local_target": "http://127.0.0.1:8000",
"status": "awaiting_connection",
"last_seen_at": null,
"created_at": "2024-01-15T10:30:00+00:00",
"requests_total": 0,
"bytes_in_total": 0,
"bytes_out_total": 0,
"note": null,
"routing_rules": [],
"agent_token": "plain_token_for_websocket_auth_do_not_log",
"edge": {
"websocket_url": "wss://edge.usejetty.online/ws",
"note": "The jetty CLI connects here (WebSocket) so ingress can forward public HTTP to your local port."
}
}
}
Note: The agent_token is only returned on creation and attach operations. Store it securely—it's needed for WebSocket authentication.
Errors:
401- Invalid or missing token422- Validation error, tunnel limit reached, or local host not allowed
Example:
curl -X POST https://usejetty.online/api/tunnels \
-H "Authorization: Bearer 1|abcdef123456..." \
-H "Content-Type: application/json" \
-d '{
"local_port": 8000,
"subdomain": "my-site"
}'
PATCH /api/tunnels/{id}
Updates an existing tunnel's configuration (non-connection details).
Authentication: Bearer token (Sanctum)
URL Parameters:
id(required): Tunnel ID
Request Body (all optional):
{
"note": "Production frontend server"
}
Response:
{
"data": {
"id": "abc123",
"subdomain": "my-site",
"server": "us-east",
"public_url": "https://my-site.tunnels.usejetty.online",
"local_target": "http://127.0.0.1:8000",
"status": "live",
"last_seen_at": "2024-01-15T10:28:00+00:00",
"created_at": "2024-01-15T09:00:00+00:00",
"requests_total": 1523,
"bytes_in_total": 2048576,
"bytes_out_total": 8192000,
"note": "Production frontend server",
"routing_rules": []
}
}
Errors:
401- Invalid or missing token403- Not authorized to update this tunnel404- Tunnel not found422- Validation error
Example:
curl -X PATCH https://usejetty.online/api/tunnels/abc123 \
-H "Authorization: Bearer 1|abcdef123456..." \
-H "Content-Type: application/json" \
-d '{
"note": "Production frontend server"
}'
POST /api/tunnels/{id}/attach
Attaches a new local process to an existing tunnel, updating connection details and generating a new agent token.
Authentication: Bearer token (Sanctum)
URL Parameters:
id(required): Tunnel ID
Request Body:
{
"local_host": "localhost",
"local_port": 3000,
"server": "us-west",
"routing_rules": []
}
Request Parameters:
local_host(required): Local hostname or IP. Max 255 chars.local_port(required): Local port number (1-65535)server(optional): Target server regionrouting_rules(optional): Array of routing rules
Response:
{
"data": {
"id": "abc123",
"subdomain": "my-site",
"server": "us-west",
"public_url": "https://my-site.tunnels.usejetty.online",
"local_target": "http://localhost:3000",
"status": "awaiting_connection",
"last_seen_at": "2024-01-15T10:28:00+00:00",
"created_at": "2024-01-15T09:00:00+00:00",
"requests_total": 1523,
"bytes_in_total": 2048576,
"bytes_out_total": 8192000,
"note": null,
"routing_rules": [],
"agent_token": "new_plain_token_for_websocket_reconnection",
"edge": {
"websocket_url": "wss://edge.usejetty.online/ws",
"note": "The jetty CLI connects here (WebSocket) so ingress can forward public HTTP to your local port."
}
}
}
Use Case: When resuming jetty share for an existing tunnel, the CLI calls this endpoint to update the local target and get a fresh agent token.
Errors:
401- Invalid or missing token403- Not authorized to attach to this tunnel404- Tunnel not found422- Validation error or local host not allowed
Example:
curl -X POST https://usejetty.online/api/tunnels/abc123/attach \
-H "Authorization: Bearer 1|abcdef123456..." \
-H "Content-Type: application/json" \
-d '{
"local_host": "localhost",
"local_port": 3000
}'
DELETE /api/tunnels/{id}
Deletes a tunnel permanently.
Authentication: Bearer token (Sanctum)
URL Parameters:
id(required): Tunnel ID
Response (204 No Content): Success, no body returned
Errors:
401- Invalid or missing token403- Not authorized to delete this tunnel404- Tunnel not found
Example:
curl -X DELETE https://usejetty.online/api/tunnels/abc123 \
-H "Authorization: Bearer 1|abcdef123456..."
POST /api/tunnels/{id}/heartbeat
Sends a keepalive signal and incremental metrics for a live tunnel.
Authentication: Bearer token (Sanctum)
URL Parameters:
id(required): Tunnel ID
Request Body:
{
"requests": 42,
"bytes_in": 102400,
"bytes_out": 512000
}
Request Parameters (all optional):
requests(integer): Number of new requests since last heartbeat (0-10,000,000)bytes_in(integer): Bytes received since last heartbeatbytes_out(integer): Bytes sent since last heartbeat
Response:
{
"data": {
"id": "abc123",
"subdomain": "my-site",
"server": "us-east",
"public_url": "https://my-site.tunnels.usejetty.online",
"local_target": "http://127.0.0.1:8000",
"status": "live",
"last_seen_at": "2024-01-15T10:30:00+00:00",
"created_at": "2024-01-15T09:00:00+00:00",
"requests_total": 1565,
"bytes_in_total": 2150976,
"bytes_out_total": 8704000,
"note": null,
"routing_rules": []
},
"viewers": [
{
"id": 1,
"name": "Alice",
"avatar": null
}
]
}
Side Effects:
- Updates tunnel status to
live - Updates
last_seen_attimestamp - Increments cumulative metrics
Errors:
401- Invalid or missing token403- Not authorized for this tunnel404- Tunnel not found422- Validation error
Example:
curl -X POST https://usejetty.online/api/tunnels/abc123/heartbeat \
-H "Authorization: Bearer 1|abcdef123456..." \
-H "Content-Type: application/json" \
-d '{
"requests": 42,
"bytes_in": 102400,
"bytes_out": 512000
}'
GET /api/tunnels/{id}/request-samples
Lists captured HTTP request samples for debugging.
Authentication: Bearer token (Sanctum)
URL Parameters:
id(required): Tunnel ID
Response:
{
"data": [
{
"id": "sample123",
"tunnel_id": "abc123",
"method": "GET",
"path": "/api/users",
"status_code": 200,
"duration_ms": 145,
"captured_at": "2024-01-15T10:25:00+00:00"
},
{
"id": "sample124",
"tunnel_id": "abc123",
"method": "POST",
"path": "/api/orders",
"status_code": 201,
"duration_ms": 320,
"captured_at": "2024-01-15T10:26:00+00:00"
}
]
}
Errors:
401- Invalid or missing token403- Not authorized for this tunnel404- Tunnel not found
Example:
curl https://usejetty.online/api/tunnels/abc123/request-samples \
-H "Authorization: Bearer 1|abcdef123456..."
POST /api/tunnels/{id}/request-samples
Stores a captured request sample for debugging.
Authentication: Bearer token (Sanctum)
URL Parameters:
id(required): Tunnel ID
Request Body:
{
"method": "POST",
"path": "/api/checkout",
"status_code": 200,
"duration_ms": 432,
"request_headers": {
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0..."
},
"response_headers": {
"Content-Type": "application/json",
"X-Request-ID": "req_abc123"
},
"request_body": "{\"items\": [1, 2, 3]}",
"response_body": "{\"order_id\": \"ord_xyz\"}"
}
Request Parameters:
method(required): HTTP methodpath(required): Request pathstatus_code(required): HTTP response statusduration_ms(optional): Request duration in millisecondsrequest_headers(optional): Request headers objectresponse_headers(optional): Response headers objectrequest_body(optional): Request body (automatically redacted for sensitive data)response_body(optional): Response body
Response (201 Created):
{
"data": {
"id": "sample125",
"tunnel_id": "abc123",
"method": "POST",
"path": "/api/checkout",
"status_code": 200,
"duration_ms": 432,
"captured_at": "2024-01-15T10:30:00+00:00"
}
}
Note: Request and response bodies are automatically scanned and redacted for sensitive patterns (API keys, passwords, tokens, credit cards, etc.).
Errors:
401- Invalid or missing token403- Not authorized, feature not available, or sample limit reached404- Tunnel not found422- Validation error
Example:
curl -X POST https://usejetty.online/api/tunnels/abc123/request-samples \
-H "Authorization: Bearer 1|abcdef123456..." \
-H "Content-Type: application/json" \
-d '{
"method": "GET",
"path": "/",
"status_code": 200,
"duration_ms": 45
}'
GET /api/tunnel-request-samples/{id}
Retrieves detailed information for a specific request sample.
Authentication: Bearer token (Sanctum)
URL Parameters:
id(required): Request sample ID
Response:
{
"data": {
"id": "sample123",
"tunnel_id": "abc123",
"method": "GET",
"path": "/api/users",
"status_code": 200,
"duration_ms": 145,
"request_headers": {
"Accept": "application/json",
"User-Agent": "curl/7.64.1"
},
"response_headers": {
"Content-Type": "application/json",
"X-Request-ID": "req_abc123"
},
"request_body": null,
"response_body": "[{\"id\": 1, \"name\": \"Alice\"}]",
"captured_at": "2024-01-15T10:25:00+00:00"
}
}
Errors:
401- Invalid or missing token403- Not authorized to view this sample404- Sample not found
Example:
curl https://usejetty.online/api/tunnel-request-samples/sample123 \
-H "Authorization: Bearer 1|abcdef123456..."
GET /api/tunnels/{tunnel}/virtual-endpoints
Returns active virtual endpoints configured for a tunnel.
Authentication: Bearer token (Sanctum)
URL Parameters:
tunnel(required): Tunnel ID
Response:
{
"data": [
{
"id": 1,
"method": "GET",
"path": "/api/status",
"status_code": 200,
"response_headers": {
"Content-Type": "application/json"
},
"response_body": "{\"status\":\"ok\"}",
"is_template": false,
"priority": 0
}
]
}
Errors:
401- Invalid or missing token403- Not authorized for this tunnel404- Tunnel not found
Example:
curl https://usejetty.online/api/tunnels/abc123/virtual-endpoints \
-H "Authorization: Bearer 1|abcdef123456..."
GET /api/tunnels/{tunnel}/fault-rules
Returns active fault injection rules configured for a tunnel.
Authentication: Bearer token (Sanctum)
URL Parameters:
tunnel(required): Tunnel ID
Response:
{
"data": [
{
"id": 1,
"path_pattern": "/api/*",
"latency_ms": 500,
"error_rate": 10,
"error_status": 503,
"error_body": null,
"priority": 0
}
]
}
Errors:
401- Invalid or missing token403- Not authorized for this tunnel404- Tunnel not found
Example:
curl https://usejetty.online/api/tunnels/abc123/fault-rules \
-H "Authorization: Bearer 1|abcdef123456..."
POST /api/tunnels/{tunnel}/recordings
Stores a request/response recording for a tunnel. This endpoint is called by the CLI when recording_enabled is true on the tunnel.
Authentication: Bearer token (Sanctum)
URL Parameters:
tunnel(required): Tunnel ID
Request Body:
{
"method": "POST",
"path": "/webhooks/stripe",
"request_headers": {},
"request_body": "...",
"response_status": 200,
"response_headers": {},
"response_body": "...",
"duration_ms": 45
}
Request Parameters:
method(required): HTTP methodpath(required): Request pathrequest_headers(required): Request headers objectrequest_body(required): Request body stringresponse_status(required): HTTP response status coderesponse_headers(required): Response headers objectresponse_body(required): Response body stringduration_ms(required): Request duration in milliseconds
Response (201 Created):
{
"data": {
...
}
}
Errors:
401- Invalid or missing token403- Not authorized for this tunnel404- Tunnel not found422- Validation error, or recording is not enabled on the tunnel
Example:
curl -X POST https://usejetty.online/api/tunnels/abc123/recordings \
-H "Authorization: Bearer 1|abcdef123456..." \
-H "Content-Type: application/json" \
-d '{
"method": "POST",
"path": "/webhooks/stripe",
"request_headers": {},
"request_body": "{\"event\":\"charge.succeeded\"}",
"response_status": 200,
"response_headers": {"Content-Type": "application/json"},
"response_body": "{\"received\":true}",
"duration_ms": 45
}'
GET /api/tunnels/{tunnel}/viewers
Returns users currently viewing the tunnel via presence tracking.
Authentication: Bearer token (Sanctum)
URL Parameters:
tunnel(required): Tunnel ID
Response:
{
"viewers": [
{
"id": 1,
"name": "Alice",
"avatar": null
}
]
}
Errors:
401- Invalid or missing token403- Not authorized for this tunnel404- Tunnel not found
Example:
curl https://usejetty.online/api/tunnels/abc123/viewers \
-H "Authorization: Bearer 1|abcdef123456..."
Tunnel Status Values
Tunnels can have the following status values:
awaiting_connection: Tunnel created, waiting for WebSocket connectionlive: Actively connected and forwarding trafficdisconnected: Previously connected, now offlinedegraded: Connection issues detected
Best Practices
Token Security
- Never commit tokens to version control
- Store tokens in environment variables or secure credential stores
- Rotate tokens regularly
- Use separate tokens for different environments (dev, staging, prod)
- Revoke tokens immediately when compromised
Error Handling
Always check HTTP status codes and parse error messages:
response=$(curl -s -w "\n%{http_code}" https://usejetty.online/api/tunnels \
-H "Authorization: Bearer $TOKEN")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" -ge 400 ]; then
echo "Error: $body"
exit 1
fi
Rate Limiting
Implement exponential backoff when receiving 429 responses:
retry_delay=1
max_retries=5
for i in $(seq 1 $max_retries); do
response=$(curl -s -w "\n%{http_code}" ...)
http_code=$(echo "$response" | tail -n1)
if [ "$http_code" -eq 429 ]; then
sleep $retry_delay
retry_delay=$((retry_delay * 2))
else
break
fi
done
Pagination
When listing resources, process all pages:
page=1
while true; do
response=$(curl -s "https://usejetty.online/api/tunnels?page=$page" \
-H "Authorization: Bearer $TOKEN")
# Process data...
next_page=$(echo "$response" | jq -r '.meta.current_page + 1')
last_page=$(echo "$response" | jq -r '.meta.last_page')
if [ "$next_page" -gt "$last_page" ]; then
break
fi
page=$next_page
done
Support
For API issues or questions:
- Documentation: https://docs.usejetty.online
- GitHub Issues: https://github.com/jetty/jetty/issues
- Community: https://discord.gg/jetty
Changelog
v0.1.19 (Current)
- Added tunnel routing rules support
- Enhanced request sample redaction
- Improved WebSocket connection handling
v0.1.18
- Added CLI browser authentication flow
- Edge event reporting endpoints
- Fleet operator API
v0.1.17
- Initial public API release
- Core tunnel management endpoints
Send feedback
Found an issue or have a suggestion? Let us know.