Preview URLs
Signed, publicly-routable URLs for ports on a running sandbox
A preview URL is a signed, publicly-routable URL for one (sandbox, port) pair. The HMAC token is baked into the hostname — any fetch() or httpx.get() reaches the sandbox with no extra headers.
URL shape: https://{port}-{hash}-{exp}-{hmac}.irisrun.io
Each call also returns a headerJwt / header_jwt carrying the same expiry, for contexts that need the auth in a header instead of the URL.
Getting a preview URL
import { IrisClient } from '@iris/sdk'
const client = new IrisClient()
const sandbox = await client.sandboxes.create({ name: 'web' })
// start a server on 0.0.0.0:8000 inside the sandbox...
const preview = await sandbox.getPreviewUrl(8000, { ttlSeconds: 600 })
console.log(preview.url) // https://8000-<hash>-<exp>-<hmac>.irisrun.io
console.log(preview.headerJwt) // RS256 JWT, same expiry
console.log(preview.expiresAt) // Unix secondsfrom iris import IrisClient
client = IrisClient() # reads IRIS_API_KEY
sandbox = client.sandboxes.create(name="web")
# start a server on 0.0.0.0:8000 inside the sandbox...
preview = sandbox.get_preview_url(8000, ttl_seconds=600)
print(preview.url) # https://8000-<hash>-<exp>-<hmac>.irisrun.io
print(preview.header_jwt) # RS256 JWT, same expiry
print(preview.expires_at) # Unix secondsThe signed URL works with a plain fetch — no credentials needed in the request:
const res = await fetch(preview.url)
const body = await res.text()import httpx
res = httpx.get(preview.url)
print(res.text)VM listener binding
Bind your server on 0.0.0.0 (IPv4). The in-VM data plane republishes IPv4 ports on the VM's IPv6 address, which the edge dials — no special bind flag is needed.
# plain IPv4 bind is enough
python -m http.server 8000 --bind 0.0.0.0TTL and expiry
Omitting ttlSeconds / ttl_seconds uses the server default of 3600 s. Values are clamped to [60, 86400] server-side.
// default TTL (3600 s)
const preview = await sandbox.getPreviewUrl(8000)
// custom TTL — server clamps to [60, 86400]
const short = await sandbox.getPreviewUrl(8000, { ttlSeconds: 300 })# default TTL (3600 s)
preview = sandbox.get_preview_url(8000)
# custom TTL — server clamps to [60, 86400]
short = sandbox.get_preview_url(8000, ttl_seconds=300)Public vs private sandboxes
The public flag on a sandbox controls access to its bare machine domain ({name}-{hash}.irisrun.io). It does not gate the preview URL — a valid signed URL fetches anonymously for both public and private sandboxes, because the embedded HMAC is the auth. You do not need public: true to use preview URLs.
Using the JWT header
When the calling context can't carry a signed hostname — for example a cross-origin fetch where the browser rewrites the Host, or a server-to-server call behind a header-only auth policy — pass headerJwt / header_jwt as a bearer token instead:
const res = await fetch(endpointUrl, {
headers: { 'Authorization': `Bearer ${preview.headerJwt}` },
})import httpx
res = httpx.get(
endpoint_url,
headers={"Authorization": f"Bearer {preview.header_jwt}"},
)Async Python
AsyncIrisClient exposes the same surface with async/await:
from iris import AsyncIrisClient
async def main():
client = AsyncIrisClient()
sandbox = await client.sandboxes.create(name="web")
preview = await sandbox.get_preview_url(8000, ttl_seconds=600)
print(preview.url)