curl -X POST "https://app.runtm.com/api/cloud/sessions/ses_abc123/preview-shares" \
-H "Authorization: Bearer runtm_xxx" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "port": 3000}'
runtm-api session share create ses_abc123 --email [email protected] --port 3000
import requests
response = requests.post(
"https://app.runtm.com/api/cloud/sessions/ses_abc123/preview-shares",
headers={
"Authorization": "Bearer runtm_xxx",
"Content-Type": "application/json",
},
json={"email": "[email protected]", "port": 3000},
)
body = response.json()
if not body["emailed"]:
print("Send this yourself:", body["preview_url"])
const response = await fetch(
"https://app.runtm.com/api/cloud/sessions/ses_abc123/preview-shares",
{
method: "POST",
headers: {
"Authorization": "Bearer runtm_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({ email: "[email protected]", port: 3000 }),
}
);
const { share, emailed, preview_url } = await response.json();
{
"share": {
"id": "9c1f0b7e-4b1a-4c2e-8f3d-2a5b6c7d8e9f",
"port": 3000,
"invited_email": "[email protected]",
"user_id": null,
"created_by": "user_abc",
"created_by_email": "[email protected]",
"created_at": "2026-08-19T22:10:00Z",
"last_accessed_at": null,
"has_accessed": false
},
"created": true,
"emailed": true,
"preview_url": "https://3000-iqsaph2xwcvxthiznmwro.dev.runtm.com"
}
Sharing
Share a Preview
Give someone outside your organization access to one port of a session’s live preview
POST
/
api
/
sessions
/
{session_id}
/
preview-shares
curl -X POST "https://app.runtm.com/api/cloud/sessions/ses_abc123/preview-shares" \
-H "Authorization: Bearer runtm_xxx" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "port": 3000}'
runtm-api session share create ses_abc123 --email [email protected] --port 3000
import requests
response = requests.post(
"https://app.runtm.com/api/cloud/sessions/ses_abc123/preview-shares",
headers={
"Authorization": "Bearer runtm_xxx",
"Content-Type": "application/json",
},
json={"email": "[email protected]", "port": 3000},
)
body = response.json()
if not body["emailed"]:
print("Send this yourself:", body["preview_url"])
const response = await fetch(
"https://app.runtm.com/api/cloud/sessions/ses_abc123/preview-shares",
{
method: "POST",
headers: {
"Authorization": "Bearer runtm_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({ email: "[email protected]", port: 3000 }),
}
);
const { share, emailed, preview_url } = await response.json();
{
"share": {
"id": "9c1f0b7e-4b1a-4c2e-8f3d-2a5b6c7d8e9f",
"port": 3000,
"invited_email": "[email protected]",
"user_id": null,
"created_by": "user_abc",
"created_by_email": "[email protected]",
"created_at": "2026-08-19T22:10:00Z",
"last_accessed_at": null,
"has_accessed": false
},
"created": true,
"emailed": true,
"preview_url": "https://3000-iqsaph2xwcvxthiznmwro.dev.runtm.com"
}
Grants one email address access to one port of a session’s live preview, and emails them the link.
This is the narrowest grant in Runtm. The invitee can open that preview and nothing else — no workspace, no terminal, no prompting — and they are not added to your organization. They need a Runtm account to open it, but not before you invite them: the grant binds to their user id the first time they sign in and open the link.
Paused sandboxes wake up automatically when a shared link is opened, so a share keeps working after the ~20 minute idle pause.
Re-inviting an address that already has this port is a no-op and does not send a second email.
Required scope:
sessions:write
From the CLI:
runtm-api session share create <session_id> --email [email protected] --port 3000Path Parameters
string
required
The session ID (e.g.
ses_abc123).Headers
string
required
Bearer token. Example:
Bearer runtm_xxxstring
Scope to an organization. Must match the API key’s own organization.
Body
string
required
Address to invite. Max 320 characters.
integer
required
The single port to share. Ports
22 and 2222 are rejected — they carry sandbox tooling rather than your app.string
Optional. Only the path and query are honored — the emailed link’s host is always the server-derived preview host for this session and port, so this cannot redirect the invite elsewhere. Pass it when you need a specific path (e.g.
.../index.html); omit it and the server derives the bare preview URL.Response
object
The grant:
id, port, invited_email, user_id, created_by, created_at, last_accessed_at, has_accessed.boolean
false when the address already had this port.boolean
Whether the invite email went out.
false when the grant already existed or email delivery is not configured — send preview_url yourself in that case.string
The link the invitee should open.
curl -X POST "https://app.runtm.com/api/cloud/sessions/ses_abc123/preview-shares" \
-H "Authorization: Bearer runtm_xxx" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "port": 3000}'
runtm-api session share create ses_abc123 --email [email protected] --port 3000
import requests
response = requests.post(
"https://app.runtm.com/api/cloud/sessions/ses_abc123/preview-shares",
headers={
"Authorization": "Bearer runtm_xxx",
"Content-Type": "application/json",
},
json={"email": "[email protected]", "port": 3000},
)
body = response.json()
if not body["emailed"]:
print("Send this yourself:", body["preview_url"])
const response = await fetch(
"https://app.runtm.com/api/cloud/sessions/ses_abc123/preview-shares",
{
method: "POST",
headers: {
"Authorization": "Bearer runtm_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({ email: "[email protected]", port: 3000 }),
}
);
const { share, emailed, preview_url } = await response.json();
{
"share": {
"id": "9c1f0b7e-4b1a-4c2e-8f3d-2a5b6c7d8e9f",
"port": 3000,
"invited_email": "[email protected]",
"user_id": null,
"created_by": "user_abc",
"created_by_email": "[email protected]",
"created_at": "2026-08-19T22:10:00Z",
"last_accessed_at": null,
"has_accessed": false
},
"created": true,
"emailed": true,
"preview_url": "https://3000-iqsaph2xwcvxthiznmwro.dev.runtm.com"
}