---
title: "Sandbox SDK app preview URLs"
canonical: https://trickest.com/docs/developer-tools/sandbox-sdk/app-previews
description: "Expose a running sandbox application through a declared port and a preview URL."
---

# Sandbox SDK app preview URLs

Use **`@trickest/sandbox`** to expose an app running inside a sandbox. Declare the port when creating the environment, start a server listening on that port, and ask for its preview URL.

## Start an app and check the page

This function creates a small HTTP server, waits for a registered route, and checks the response. It returns the sandbox handle so your application can keep the preview running and delete it when the user finishes.

```typescript
import { Sandbox, SandboxApiError } from "@trickest/sandbox";

export async function createPreview(token: string) {
  const sandbox = await Sandbox.create({
    token,
    runtime: "node24",
    ports: [3000],
  });
  try {
    await sandbox.writeFile(
      "server.cjs",
      [
        'const http = require("node:http")',
        "http.createServer((req, res) => {",
        '  res.end("Sandbox preview ready")',
        '}).listen(3000, "0.0.0.0")',
      ].join("\n"),
    );
    await sandbox.runCommand({
      cmd: "node",
      args: ["server.cjs"],
      detached: true,
    });

    for (let attempt = 0; attempt < 20; attempt++) {
      try {
        const url = await sandbox.getPortDomain(3000);
        const response = await fetch(url, {
          signal: AbortSignal.timeout(5_000),
        });
        const text = await response.text();
        if (response.ok && text === "Sandbox preview ready")
          return { sandbox, url };
      } catch (error) {
        if (error instanceof SandboxApiError) {
          if (error.status !== 404 || error.code !== "port_not_mapped")
            throw error;
        } else if (
          !(error instanceof TypeError) &&
          !(error instanceof Error && error.name === "TimeoutError")
        ) {
          throw error;
        }
      }
      await new Promise((resolve) => setTimeout(resolve, 250));
    }
    throw new Error("The preview did not become available");
  } catch (error) {
    await sandbox.delete();
    throw error;
  }
}
```

Call `await sandbox.delete()` on the returned handle when the preview is no longer needed. Stopping or deleting its environment interrupts the app.

The example retries temporary route-registration failures and app connection failures. Each HTTP page check has a five-second timeout. `getPortDomain()` has no timeout or abort parameter, so the retry count does not guarantee an overall deadline for this function.

## Choose the URL method

| Method                              | What it does                         | Requirement                                                                                       |
| ----------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------- |
| `await sandbox.getPortDomain(3000)` | Ask the server for the canonical URL | The port must be published and its route registered                                               |
| `sandbox.domain(3000)`              | Construct a URL locally              | The port must be published and `TRICKEST_SANDBOX_PUBLIC_DOMAIN` configured in the calling process |

Neither method starts the application. The synchronous method does not verify that its constructed URL works. A registered route also does not prove the application serves the expected response; check the page as the example does.

## Troubleshoot a preview

- **`404 port_not_mapped`:** route registration may still be pending, or the port was not declared at creation.
- **Connection failure or unexpected response:** check the command's status and logs, then confirm the server listens on the declared port and on `0.0.0.0`.
- **Missing public-domain configuration:** use `getPortDomain()` or configure the synchronous `domain()` method's environment.

Treat a preview URL as an application endpoint. Keep platform tokens out of pages and client JavaScript, and apply your app's access controls before exposing sensitive results. See [Commands & output](/docs/developer-tools/sandbox-sdk/commands) for detached command controls and [Lifecycle](/docs/developer-tools/sandbox-sdk/lifecycle) for cleanup.

---
_Markdown source of https://trickest.com/docs/developer-tools/sandbox-sdk/app-previews._
