Public ports
Give one port of an instance a permanent, unauthenticated HTTPS URL for webhooks, embeds, and anything else that can't send an API key.
Every instance URL normally demands a credential: an X-Pusk-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.
Create one
Declare public ports when you create the instance, or add one to a running instance:
curl -X POST https://api.pusk.ai/v1/instances \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "template": "pusk-hermes", "public_ports": [{ "port": 8644 }] }'
portintegerThe container port the URL routes to.
urlstringThe 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).
domain_urlsstring[]The same URL mirrored under each of your workspace's active custom domains; empty until a domain is active.
prefixstring | nullEchoes the prefix you chose, or null for a slug URL.
createdintegerUnix seconds when the entry was created.
The entries also ride on every instance read as 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:
{ "port": 3000, "prefix": "crm" }
{
"port": 3000,
"url": "https://crm-ab12cd34ef.pusk.app",
"domain_urls": [],
"prefix": "crm",
"created": 1751928000
}
The hostname is {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 -X DELETE https://api.pusk.ai/v1/instances/ab12cd34ef/public-ports/8644 \
-H "Authorization: Bearer sk_live_..."
{ "port": 8644, "deleted": true }
Revocation propagates to the edge in seconds. The delete acts once; repeating it returns 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 port 8644 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 Pusk url. Keep Hermes's /webhooks/<subscription-name> path.
See Hermes webhooks for the short Pusk 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-Pusk-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. The flip side: any traffic to the URL keeps an auto-sleep instance awake, and auto-sleep instances bill at the 4x awake rate, so a scraped or polled public URL keeps the 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.