X-Agent37-Key header for API calls, a signed URL for a browser. A public port is the third option: an HTTPS URL for one port of one instance that anyone can reach with no credential at all, and that keeps working until you delete it.
The canonical use is webhooks. An external service (a telephony provider, a payment processor, a Git host) needs a permanent URL it can POST to, and it will never attach your sk_live_ key. Signed URLs expire, so they are wrong for a webhook registration; a public port is exactly right.
Anyone who has the URL reaches the port. Put your app’s own authentication in front of anything sensitive. Hermes, for example, rejects webhook deliveries without a valid subscription signature: the public URL supplies reachability, not trust.
Hermes templates come with one. Every agent37-hermes and agent37-hermes-small instance is created with a public port on 8443, labelled telegram, and the container sees it as TELEGRAM_WEBHOOK_URL alongside a TELEGRAM_WEBHOOK_SECRET. A Telegram bot connected in the Hermes dashboard therefore receives messages by webhook rather than by polling, so Telegram can wake an instance that sleeps when idle. Declare 8443 yourself in public_ports to choose the hostname, or set your own TELEGRAM_WEBHOOK_URL in env to override it.
Create one
Declare public ports when you create the instance, or add one to a running instance:integer
The container port the URL routes to.
string
The public HTTPS URL. With no
prefix, the hostname is a server-minted 20-character random slug: unguessable, so the URL itself is the only credential. Treat it like one: it leaks the way any URL leaks (Referer headers, browser history, logs).string[]
The same URL mirrored under each of your workspace’s active custom domains; empty until a domain is active.
string | null
Echoes the prefix you chose, or
null for a slug URL.string | null
A display name for the link, set with an optional
label on create (up to 64 characters). Purely cosmetic: it appears in listings and the dashboard, and never affects the URL.integer
Unix seconds when the entry was created.
public_ports, and GET /v1/instances/{id}/public-ports lists them on their own.
You can serve these on your own domain too: register a custom domain and every public-port hostname is mirrored, still credential-free, at {hostname}.yourdomain.com. Once a domain is active, each entry’s domain_urls field lists the mirrored URLs.
Named URLs with prefix
Pass a prefix to get a deterministic hostname instead of a random slug:
request
response
{prefix}-{instanceId}: readable, stable across delete and re-create, and guessable by design. Anyone who knows your instance id can derive it, so a prefix URL is for things that are meant to be found. The instance-id suffix is the namespace, so prefixes never collide across instances and nobody can squat a bare name. A prefix is 1–30 lowercase letters, digits, or hyphens, with no leading or trailing hyphen.
Delete (revoke)
curl
response
404. To rotate a leaked slug URL, delete the entry and re-create it; the new entry gets a fresh slug. (A prefix entry re-created with the same prefix comes back at the same hostname; that stability is the point of a prefix.)
Recipe: Hermes webhooks
Hermes listens on port8644 and authenticates deliveries with a per-subscription secret. Create a named public port such as { "port": 8644, "prefix": "webhooks" }, then replace the http://localhost:8644 origin that Hermes shows with the returned Agent37 url. Keep Hermes’s /webhooks/<subscription-name> path.
See Hermes webhooks for the short Agent37 setup.
Rules and limits
- One URL per port, at most 20 public ports per instance. Creating a second entry for the same port returns
409 public_port_exists(delete first to rotate). - Platform ports can’t be made public.
3737(the gateway; a public gateway would be a keyless agent API),9119,7681(terminal),8080(file browser),6080,7890, and22022(SSH) are rejected with400. They stay reachable withX-Agent37-Keyor a signed URL, except22022, which takes an authenticated SSH tunnel and nothing else. - Public traffic is ordinary instance traffic: no separate meter, billed to the workspace like any other request.
- A request to a public URL wakes a sleeping instance, which for a webhook endpoint is the feature working. A delivery to a sleeping instance is held until the wake completes, so a sender with a short timeout records that delivery as a failure and retries on its own schedule. The flip side: any traffic to the URL keeps an auto-sleep instance awake, so a scraped or polled public URL keeps the compute meter running. If that happens, delete the entry.
- While the instance is stopped or suspended, the URL answers
503 { "error": "unavailable" }, so visitors never see your billing state. - The path
/healthis answered by the platform edge itself and never reaches your app on any instance URL, public ports included. Serve health checks on another path. - Public ports and signed URLs are orthogonal: deleting a public port does not invalidate signed URLs for that port, and vice versa.