Skip to main content

Endpoint reference

Your node exposes a small set of HTTP endpoints, split across two listeners: a public listener that carries client and relay traffic, and a separate private listener that serves operator-only health and metrics. This page is a map of those endpoints, the role each plays, and the ports they bind by default.

It's a reference, not the byte-level wire specification — the exact request and response schemas, encryption framing, and signing formats are part of the protocol your node software implements, and a full protocol specification is coming soon.

The two listeners

ListenerConfig keyDefaultTLSCarries
Publicserver.listen127.0.0.1:9090 (loopback; bind :9090 to serve)Serve HTTPS directly (tls.mode: acme/manual), or terminate TLS at a single reverse proxy — never a load balancerAll client and relay traffic: inference, reserve, discovery, attestation, relay
Privateserver.private_listen127.0.0.1:9091Never TLS-wrappedOperator-only /healthz, /livez and /metrics

The binary's default server.listen is loopback (127.0.0.1:9090) — safe, but it serves nobody. A serving node must face clients directly: bind all interfaces with :9090 (dual-stack IPv4 + IPv6 — 0.0.0.0 would be IPv4-only) and give it HTTPS with tls.mode: acme (or manual), or terminate TLS at a single reverse proxy. zs-node init writes the public bind for you. The private listener is loopback-only and never TLS-wrapped — keep it on 127.0.0.1 (or a private network you control) and never expose /healthz, /livez and /metrics to the internet.

info

One port instead of two. Set server.private_listen to the empty string ("") to colocate /healthz, /livez and /metrics on the public port — back-compat for single-port deployments. When you do specify a port in YAML, quote it (private_listen: ":9091"); a leading colon is otherwise read as a YAML mapping marker.

Public endpoints

These are served on server.listen (default port 9090).

Method · PathPurpose
POST /v1/responsesSealed, streamable. The Responses-style inference route — the sealed text path the client drives. Your node decrypts the prompt, runs the model, streams the sealed reply, and returns a signed receipt.
POST /v1/chat/completionsOpenAI-style chat-completions route, accepted as a relay-forwardable / compatibility path. The current client speaks the Responses shape, not this one, so it isn't part of the client's sealed-and-streamed text path; whether your node serves it natively or translates depends on your provider (see Serving models & pricing).
POST /v1/images/generationsSealed. Generates images directly and returns them with a signed receipt.
POST /v1/images/editsSealed (multipart). Edits a supplied image and returns the result with a signed receipt.
GET /v1/modelsPlaintext. Your live model list — what clients read to build the model picker. Always served unencrypted.
GET /v1/models/{id}Plaintext. Single-model lookup.
POST /v1/zs/reserveSealed both ways. Two-phase admission, phase 1: returns a signed ticket (price ceiling, expiry) and the wrapped response key. The request uses its own sealed content type (application/vnd.zs-reserve+json) so a relay can't read the payer's address; sealed-only (plaintext requests are rejected 415 bad_content_type). The 200 response is sealed back to the caller too (application/vnd.zs-reserve-response+json), because it carries the pre-signed open transaction, which names the payer's address — a relay would otherwise read it there. Error responses stay plaintext so the caller can see the cause.
GET /v1/zs/detailsPlaintext, unauthenticated. Your operator self-description: live catalog, capacities, protocol version, pricing, oracle/TEE status, and your current signed ephemeral encryption recipient. This is what clients probe for health and discovery and read to build the model picker (see Encryption & keys). (Your NFD name isn't carried here — it resolves from your on-chain operator record.)
GET /v1/zs/attestationTEE evidence bundle. Returns the node's attestation evidence when confidential mode is enabled; returns 404 when TEE is disabled. See Confidential compute (TEE).
POST /v1/zs/relayThe transport hop other operators' requests arrive through. Your node forwards a sealed payload to a named target node and streams the response back, without being able to read it. Registered only when a relay directory is available (escrow + algod configured). See Relays.
info

/v1/responses is the sealed text path. The current client seals and streams text over /v1/responses only. /v1/chat/completions is accepted for relay forwarding and OpenAI-shape compatibility, but it isn't the route the client drives. Which shape your upstream serves natively (versus the node translating between them) depends on your provider; see Serving models & pricing.

Private endpoints

These are served on server.private_listen (default port 9091), loopback-only and never TLS-wrapped. Point your supervisor and Prometheus scraper at this listener, not the public one. All three routes are exempt from rate limiting.

Method · PathPurpose
GET /healthzReadiness probe — should traffic go here? 200 when serving, 503 {"status":"draining"} for the whole graceful shutdown, which is what pulls the node out of your load balancer.
GET /livezLiveness probe — is the process wedged? 200 whenever the node is responsive, including while draining. Point container liveness checks here, never at /healthz. On a node build that predates it this returns 404 — run no liveness probe at all on those, rather than falling back to /healthz.
GET /metricsPrometheus exposition — provider health, discovered model count, settlement state, and the rest of the node's operational metrics.

How the public routes relate

A normal request touches the public routes in this order:

Discover — /v1/zs/details

The client probes it to discover you (continuously, in the background).

Reserve — /v1/zs/reserve

The client calls it to get a signed price ceiling and the keys for the request.

Run the model

The client calls the text route (/v1/responses) or an image route (/v1/images/generations or /v1/images/edits) to run the model.

Receipt & settlement

Your node returns a signed receipt; settlement follows on-chain (see The payment flow).

When your node is acting as a relay for someone else's request, that request arrives at /v1/zs/relay instead, and you forward it to the target's reserve / inference / image endpoints on their behalf — sealed, so you never see its contents.

Sealed envelopes

The prompt-carrying routes don't accept plaintext. A sealed request carries the content type application/vnd.zs+json; a POST to one of these routes that isn't a proper envelope is rejected with 400 bad_envelope. Sealed responses come back the same way — a application/vnd.zs+json body with the response key delivered out-of-band — and stream as sealed event: zs frames when the client requests streaming. The plaintext discovery routes (/v1/models, /v1/models/{id}, /v1/zs/details) and the private routes are the exceptions: they're served unencrypted by design.

The exact envelope framing, headers, and signing formats are the protocol's business, not something you configure — your node software implements them. See Encryption & keys for how the sealing keys are generated, signed, and rotated.

Transport requirements

  • HTTPS at a publicly reachable URL for the public listener. Clients and relays both need to reach your advertised base URL from the open internet, with TLS terminated either by your reverse proxy or by the node (tls.mode: acme / manual).
  • Loopback-only for the private listener. Keep /healthz, /livez and /metrics on 127.0.0.1 (or a trusted private interface). It is never TLS-wrapped — don't expose it.
  • Streaming. The text routes stream; relays must forward streamed responses without buffering the whole reply.
  • Current protocol version. The version you advertise in /v1/zs/details must match what clients speak, or you're flagged incompatible and skipped (see Health & compatibility).
info

Coming soon. A full protocol specification — exact paths, headers, payload schemas, encryption framing, and signing formats — will document the byte-level contract behind these tables. Until then, the node software is the reference implementation of these endpoints.