Documentation for Jetty

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:

  1. CLI calls POST /api/cli/auth/session to initiate authentication
  2. User opens the authorize_url in their browser and logs in
  3. CLI polls GET /api/cli/auth/poll until the user completes authentication
  4. 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 ID
  • 429 - 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 credentials
  • 422 - 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 identifier
  • message (required): Human-readable message
  • context (optional): Additional structured data

Response (204 No Content): Success, no body returned

Rate Limit: Body size limited to 16 KB

Errors:

  • 403 - Invalid edge secret
  • 413 - Payload too large
  • 422 - 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 error
  • 429 - 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 token
  • 503 - 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 token
  • 503 - 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 token
  • 403 - 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 token
  • 403 - User not authorized to create teams
  • 422 - 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 token
  • 403 - 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 token
  • 403 - 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 token
  • 403 - 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 token
  • 422 - 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 token
  • 403 - Not authorized to update this tunnel
  • 404 - Tunnel not found
  • 422 - 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 region
  • routing_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 token
  • 403 - Not authorized to attach to this tunnel
  • 404 - Tunnel not found
  • 422 - 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 token
  • 403 - Not authorized to delete this tunnel
  • 404 - 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 heartbeat
  • bytes_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_at timestamp
  • Increments cumulative metrics

Errors:

  • 401 - Invalid or missing token
  • 403 - Not authorized for this tunnel
  • 404 - Tunnel not found
  • 422 - 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 token
  • 403 - Not authorized for this tunnel
  • 404 - 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 method
  • path (required): Request path
  • status_code (required): HTTP response status
  • duration_ms (optional): Request duration in milliseconds
  • request_headers (optional): Request headers object
  • response_headers (optional): Response headers object
  • request_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 token
  • 403 - Not authorized, feature not available, or sample limit reached
  • 404 - Tunnel not found
  • 422 - 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 token
  • 403 - Not authorized to view this sample
  • 404 - 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 token
  • 403 - Not authorized for this tunnel
  • 404 - 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 token
  • 403 - Not authorized for this tunnel
  • 404 - 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 method
  • path (required): Request path
  • request_headers (required): Request headers object
  • request_body (required): Request body string
  • response_status (required): HTTP response status code
  • response_headers (required): Response headers object
  • response_body (required): Response body string
  • duration_ms (required): Request duration in milliseconds

Response (201 Created):

{
  "data": {
    ...
  }
}

Errors:

  • 401 - Invalid or missing token
  • 403 - Not authorized for this tunnel
  • 404 - Tunnel not found
  • 422 - 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 token
  • 403 - Not authorized for this tunnel
  • 404 - 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 connection
  • live: Actively connected and forwarding traffic
  • disconnected: Previously connected, now offline
  • degraded: 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:

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.