Published ports
A guest port at its own https hostname — private by default, and the credential shown once.
A published port answers at its own https hostname, which reaches exactly that one port on exactly that one machine. There is no path, query or header a caller can send that reaches a different port, a different machine, or the host underneath: the hostname is the whole routing decision, and it is resolved server-side.
GET /v1/machines/{id}/ports published ports on a machine
POST /v1/machines/{id}/ports publish one
GET /v1/machines/{id}/ports/{preview_id}
DELETE /v1/machines/{id}/ports/{preview_id} stop publishing
GET /v1/ports every published port in the organization
Publishing
const preview = await machine.ports.expose(3000, { name: "dev-server" });
preview.url; // https://k3f9x2q7m1.preview.example.com
preview.access_url; // the one-shot credentialed link
preview.secret; // the raw secret
port is the port inside the machine, not a host port. Nothing has to be listening yet:
exposing before starting the server is the ordinary order, and the URL reports the machine as
unavailable until something answers.
Exposing a port that is already published returns the preview that exists rather than a second hostname for one service, so a retry converges.
Private by default
access is private unless you say otherwise, and public is a deliberate act with a
deliberate name. An agent that exposes a port so a person can look at it should have to say that
is what it meant, and someone reading a list should be able to see which of their services are on
the open internet without opening each one.
await machine.ports.expose(3000, { access: "public" }); // no credential at all
The secret is shown once
secret and access_url are present only in the response to the call that created the
preview. There is no route that returns them afterwards — a value the product can hand back on
request is a value that lives in every log and cache the response passes through, exactly like an
API key.
access_url is a one-shot link that trades the secret for a host-scoped cookie. That is what
makes a private preview openable in a browser at all: a person cannot set an Authorization
header on an address bar, and putting the secret in the preview’s own query string would leave it
in browser history, in referers, and in the guest application’s own access log.
Unpublishing
await machine.ports.unexpose(preview.id);
The URL stops answering immediately, and that hostname is never issued again. A hostname that came back would resurrect every link and bookmark pointing at it, aimed at whatever that port serves next.
Fields
| Field | Meaning |
|---|---|
port |
the port inside the machine |
name |
your label, or null |
access |
private or public |
url |
the https URL it answers on |
secret |
at creation only; null on every later read |
access_url |
at creation only; null on every later read |
last_request_at |
when it was last reached, or null |
Deployment requirement
Publishing needs the deployment to have a preview domain configured, with a wildcard DNS record
and a wildcard certificate under it. Without one the API refuses with unsupported_operation
rather than handing back a URL that resolves nowhere — a far better failure than a customer
debugging a dev server that was never reachable.
Bytes served through a preview count as egress. See billing.