main
md 9.79 KB

title: API Reference

description: Portal relay API contract, endpoint groups, auth, and shared response rules.

API Reference

Portal relay exposes one control-plane API. Frontends, SDK clients, peer relays, and operators all talk to this API, but each group has a small owned surface. Local agent endpoints under /agent/* are not part of the relay API.

JSON Envelope

Matched JSON control endpoints return this envelope:

{
  "ok": true,
  "data": {}
}

Error responses use the same envelope with error instead of data:

{
  "ok": false,
  "error": {
    "code": "unauthorized",
    "message": "unauthorized"
  }
}

data is omitted on error. error is omitted on success.

The envelope does not apply to streaming or delegated endpoints:

Path Format
/sdk/connect HTTP/1.1 connection hijack
/v1/sign keyless TLS signer protocol
/api/x402/* relay-owned x402 facilitator response
/api/install.sh, /api/install.ps1, /api/install/bin/* script or binary bytes

Unknown routes may be handled by the frontend/proxy layer or return a normal HTTP 404 outside the envelope.

Auth Schemes

Name Used by How it is sent
None public and challenge endpoints no credential
Admin bearer admin API Authorization: Bearer <access_token>
Lease token header tunnel stream and keyless signer X-Portal-Access-Token: <access_token>
Lease token body lease renew/unregister JSON field access_token
Signed descriptor relay discovery announce signed RelayDescriptor body
Signed hop route relay overlay route signed HopRoute body

Admin auth and SDK lease auth issue different tokens and are not interchangeable. SDK lease registration uses SIWE; relay admin access uses the configured admin token.

Endpoint Groups

Public

Method Path Auth Response
GET / None service identity
GET /api/healthz None { "status": "ok" }
GET /api/state None PublicStateResponse
GET/HEAD /api/install.sh, /api/install.ps1 None install script
GET/HEAD /api/install/bin/{slug} None install binary or redirect

Frontend Presentation API

These paths are served by the TypeScript API service when the static frontend stack is enabled. They live under /ui/ and are derived from relay APIs plus frontend-owned presentation state.

Method Path Auth Response
GET /ui/state None PublicStateResponse plus landing_page_enabled
GET /ui/service/status?hostname=... None ServiceStatusResponse
GET /ui/policy/state Admin bearer PolicyStateResponse plus landing_page_enabled in policy
GET/POST /ui/policy Admin bearer PolicySettings plus landing_page_enabled
POST /ui/policy/leases, /ui/policy/ips Admin bearer relay policy update response
GET /ui/thumbnail/{hostname} None generated image

SDK

Method Path Auth Body Response
GET /sdk/domain None none DomainResponse
POST /sdk/register/challenge None RegisterChallengeRequest RegisterChallengeResponse
POST /sdk/register SIWE signature body RegisterRequest RegisterResponse
POST /sdk/renew lease token body RenewRequest RenewResponse
POST /sdk/unregister lease token body UnregisterRequest {}
GET /sdk/connect lease token header none hijacked stream

/sdk/hop is a relay-to-relay overlay route endpoint. It is not used by normal SDK clients.

Admin

Method Path Auth Body Response
POST /api/admin/auth/login None AdminAuthLoginRequest AdminAuthLoginResponse
GET /api/admin/auth/status Optional admin bearer none AdminAuthStatusResponse
POST /api/admin/auth/logout Admin bearer none {}

/admin itself is a frontend route, not a relay API endpoint.

Payments

Relay /api/x402/* endpoints are optional relay-owned control-plane facilitator endpoints. Enable them with X402_ENABLED=true when a relay operator wants to reserve support for relay resources such as future tunnel registration fees, lease renewal fees, raw TCP/UDP port allocation, or premium capacity. They are served by the embedded gosuda/x402-facilitator handler and do not use the Portal JSON envelope. Portal selects Sui mainnet by default and Sui testnet when X402_TESTNET=true. Portal accepts only USDC gasless stablecoin address-balance payments. X402_PAY_TO is the relay-owned payment recipient.

Relay x402 settings do not affect tunnel paid routes. Tunnel payment recipients and payment networks are local tunnel configuration and are not part of the relay lease API.

Paid routed HTTP tunnels additionally expose /x402/prepare and /x402/client.js on the public tunnel origin. Those are tunnel-owned helper endpoints for app frontends, not relay API routes, and they do not use the /api prefix. Tunnel paid routes use Sui mainnet by default and Sui testnet when the tunnel is exposed with --x402-testnet or configured with x402_testnet = true. /x402/client.js is browser-only; native clients call /x402/prepare directly and send X-PAYMENT on the protected request.

Method Path Auth Body Response
GET /api/x402/supported None none x402 supported kinds
POST /api/x402/verify None x402 verify request x402 verify response
POST /api/x402/settle None x402 settle request x402 settle response

Policy

Method Path Auth Body Response
GET /api/policy Admin bearer none PolicySettings
POST /api/policy Admin bearer PolicySettings PolicySettings
GET /api/policy/state Admin bearer none PolicyStateResponse
POST /api/policy/leases Admin bearer LeasePolicyUpdate {}
POST /api/policy/ips Admin bearer IPPolicyUpdate {}

Relay

Method Path Auth Response
GET /discovery None DiscoveryResponse
POST /discovery/announce Signed descriptor DiscoveryAnnounceResponse
POST /v1/sign Lease token header keyless signer response

Shared Types

Timestamps are JSON-encoded Go time.Time values.

Identity:

Field Type Notes
name string DNS label used by the lease
address string Ethereum address

LeaseMetadata:

Field Type Notes
description string optional
owner string optional
thumbnail string optional URL or data value
tags string[] optional
hide boolean hidden leases are omitted from the public state

Lease:

Field Type
name string
expires_at, first_seen_at, last_seen_at string
hostname string
udp_enabled, tcp_enabled boolean
tcp_addr string
metadata LeaseMetadata
ready number

PolicyLease extends Lease with:

Field Type
identity_key, address string
bps number
client_ip, reported_ip string
is_approved, is_banned, is_denied, is_ip_banned boolean

ServiceStatusResponse:

Field Type
hostname string
registered boolean
service_alive boolean

PolicyStateResponse:

Field Type
policy PolicySettings
leases PolicyLease[]

PolicyPortSettings:

Field Type Notes
enabled boolean enables the transport
max_leases number 0 means unlimited

PolicySettings:

Field Type
approval_mode "auto" or "manual"
udp PolicyPortSettings
tcp_port PolicyPortSettings

LeasePolicyUpdate:

Field Type Notes
identity_key string normalized name:address key
is_banned boolean optional
is_approved boolean optional
is_denied boolean optional; true also revokes approval
bps number optional; 0 removes the limit

IPPolicyUpdate:

Field Type
ip string
is_banned boolean

Common Errors

Code Meaning
invalid_json request body is not valid JSON
invalid_request request shape or value is invalid
method_not_allowed endpoint does not accept the method
unauthorized credential is missing, expired, or invalid
feature_unavailable feature is disabled or not configured
rate_limited request was throttled
hostname_conflict lease hostname is already registered
lease_not_found lease token or identity has no active lease
lease_rejected lease is not currently allowed to route
ip_banned source or reported IP is banned
invalid_address address path or body value is invalid
invalid_ip IP path value is invalid
invalid_mode approval mode is not auto or manual
http11_only endpoint requires HTTP/1.1
hijack_unsupported, hijack_failed reverse stream setup failed
udp_disabled, udp_capacity_exceeded, udp_port_exhausted UDP lease cannot be allocated
tcp_port_disabled, tcp_port_capacity_exceeded, tcp_port_exhausted TCP port lease cannot be allocated
transport_mismatch request does not match the active lease transport
internal unexpected server failure