PORTAL v2

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:

PathFormat
/sdk/connectHTTP/1.1 connection hijack
/v1/signkeyless 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

NameUsed byHow it is sent
Nonepublic and challenge endpointsno credential
Admin beareradmin APIAuthorization: Bearer <access_token>
Lease token headertunnel stream and keyless signerX-Portal-Access-Token: <access_token>
Lease token bodylease renew/unregisterJSON field access_token
Signed descriptorrelay discovery announcesigned RelayDescriptor body
Signed hop routerelay overlay routesigned 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

MethodPathAuthResponse
GET/Noneservice identity
GET/api/healthzNone{ "status": "ok" }
GET/api/stateNonePublicStateResponse
GET/HEAD/api/install.sh, /api/install.ps1Noneinstall script
GET/HEAD/api/install/bin/{slug}Noneinstall 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.

MethodPathAuthResponse
GET/ui/stateNonePublicStateResponse plus landing_page_enabled
GET/ui/service/status?hostname=...NoneServiceStatusResponse
GET/ui/policy/stateAdmin bearerPolicyStateResponse plus landing_page_enabled in policy
GET/POST/ui/policyAdmin bearerPolicySettings plus landing_page_enabled
POST/ui/policy/leases, /ui/policy/ipsAdmin bearerrelay policy update response
GET/ui/thumbnail/{hostname}Nonegenerated image

SDK

MethodPathAuthBodyResponse
GET/sdk/domainNonenoneDomainResponse
POST/sdk/register/challengeNoneRegisterChallengeRequestRegisterChallengeResponse
POST/sdk/registerSIWE signature bodyRegisterRequestRegisterResponse
POST/sdk/renewlease token bodyRenewRequestRenewResponse
POST/sdk/unregisterlease token bodyUnregisterRequest{}
GET/sdk/connectlease token headernonehijacked stream

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

Admin

MethodPathAuthBodyResponse
POST/api/admin/auth/loginNoneAdminAuthLoginRequestAdminAuthLoginResponse
GET/api/admin/auth/statusOptional admin bearernoneAdminAuthStatusResponse
POST/api/admin/auth/logoutAdmin bearernone{}

/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.

MethodPathAuthBodyResponse
GET/api/x402/supportedNonenonex402 supported kinds
POST/api/x402/verifyNonex402 verify requestx402 verify response
POST/api/x402/settleNonex402 settle requestx402 settle response

Policy

MethodPathAuthBodyResponse
GET/api/policyAdmin bearernonePolicySettings
POST/api/policyAdmin bearerPolicySettingsPolicySettings
GET/api/policy/stateAdmin bearernonePolicyStateResponse
POST/api/policy/leasesAdmin bearerLeasePolicyUpdate{}
POST/api/policy/ipsAdmin bearerIPPolicyUpdate{}

Relay

MethodPathAuthResponse
GET/discoveryNoneDiscoveryResponse
POST/discovery/announceSigned descriptorDiscoveryAnnounceResponse
POST/v1/signLease token headerkeyless signer response

Shared Types

Timestamps are JSON-encoded Go time.Time values.

Identity:

FieldTypeNotes
namestringDNS label used by the lease
addressstringEthereum address

LeaseMetadata:

FieldTypeNotes
descriptionstringoptional
ownerstringoptional
thumbnailstringoptional URL or data value
tagsstring[]optional
hidebooleanhidden leases are omitted from the public state

Lease:

FieldType
namestring
expires_at, first_seen_at, last_seen_atstring
hostnamestring
udp_enabled, tcp_enabledboolean
tcp_addrstring
metadataLeaseMetadata
readynumber

PolicyLease extends Lease with:

FieldType
identity_key, addressstring
bpsnumber
client_ip, reported_ipstring
is_approved, is_banned, is_denied, is_ip_bannedboolean

ServiceStatusResponse:

FieldType
hostnamestring
registeredboolean
service_aliveboolean

PolicyStateResponse:

FieldType
policyPolicySettings
leasesPolicyLease[]

PolicyPortSettings:

FieldTypeNotes
enabledbooleanenables the transport
max_leasesnumber0 means unlimited

PolicySettings:

FieldType
approval_mode"auto" or "manual"
udpPolicyPortSettings
tcp_portPolicyPortSettings

LeasePolicyUpdate:

FieldTypeNotes
identity_keystringnormalized name:address key
is_bannedbooleanoptional
is_approvedbooleanoptional
is_deniedbooleanoptional; true also revokes approval
bpsnumberoptional; 0 removes the limit

IPPolicyUpdate:

FieldType
ipstring
is_bannedboolean

Common Errors

CodeMeaning
invalid_jsonrequest body is not valid JSON
invalid_requestrequest shape or value is invalid
method_not_allowedendpoint does not accept the method
unauthorizedcredential is missing, expired, or invalid
feature_unavailablefeature is disabled or not configured
rate_limitedrequest was throttled
hostname_conflictlease hostname is already registered
lease_not_foundlease token or identity has no active lease
lease_rejectedlease is not currently allowed to route
ip_bannedsource or reported IP is banned
invalid_addressaddress path or body value is invalid
invalid_ipIP path value is invalid
invalid_modeapproval mode is not auto or manual
http11_onlyendpoint requires HTTP/1.1
hijack_unsupported, hijack_failedreverse stream setup failed
udp_disabled, udp_capacity_exceeded, udp_port_exhaustedUDP lease cannot be allocated
tcp_port_disabled, tcp_port_capacity_exceeded, tcp_port_exhaustedTCP port lease cannot be allocated
transport_mismatchrequest does not match the active lease transport
internalunexpected server failure