---
title: "Platform API"
description: "Build your own chat editor on Hoplite’s managed agents, workspaces, previews, and source storage."
canonical_url: "https://hoplite.sh/docs/platform"
markdown_url: "https://hoplite.sh/docs/platform.md"
---

# Platform API
URL: /docs/platform
LLM index: /llms.txt
Description: Build your own chat editor on Hoplite’s managed agents, workspaces, previews, and source storage.

# Platform API

Hoplite supplies the agent runtime, persistent source versions, isolated editing workspaces, authenticated previews, and downloadable builds. Your application owns its users, interface, release approval, and hosting. You can build a website editor without creating a GitHub repository for each customer.

The Direct API automates Hoplite’s existing product. The Platform API, under `/api/platform/v1`, gives your product a source-based workflow. Its top-level resource is an **app**: one per customer site or workspace in your product. Every source, thread, draft, preview, and build lives inside an app. The Platform contract is currently a release candidate; use the API base URL for the environment where this version is deployed.

## Get access

The Platform API is enabled per organization. Until Hoplite enables yours, every `/api/platform/v1` request answers `403` with `{"ok":false,"error":"platform_api_not_enabled"}` and changes nothing.

To request access, email [support@hoplite.sh](mailto:support@hoplite.sh). Include your workspace name and workspace ID, both shown under **Settings → Workspace → General**, and a short description of what you are building. Once Hoplite enables the organization, the same requests stop answering `platform_api_not_enabled` within a minute. Nothing changes on your side: credentials you already created keep working. The [quickstart](/docs/platform/quickstart) walks through the first app end to end.

Signed-in members of an enabled workspace can open the [Platform console](https://app.hoplite.sh/platform) to inspect and manage apps. **Overview** shows activity and failure trends; **Runs**, **Requests**, and **Events** let you investigate execution and API traffic; **Workspaces** lists the app runtimes. **Usage** breaks spend down by app, model, category, and day, with CSV export, and **Billing** shows your credits and per-app monthly budgets. On the Free plan, these pages show shares and counts instead of dollar amounts. Owners, admins, and members can stop a run or workspace and edit an app's settings. Owners and admins can also stop every run of an app, delete an app, rotate or revoke keys under **API keys**, and create webhooks, rotate their signing secrets, and redeliver events under **Webhooks**. The console is scoped to your current workspace.

## Create an app

Create a server-side service credential and keep it on your backend. Under **Settings → Workspace → API keys**, create a service account with **Projects** and **Threads** set to **Write**, every other permission scope set to **None**, and the project scope left on **All projects**. The [quickstart](/docs/platform/quickstart) lists each step.

Those levels grant the permissions that Platform operations check. Creating apps requires `project:create` with organization-wide scope; editing inside an app requires `project:read`, `project:update`, `thread:create`, `thread:read`, and `thread:update`; deleting an app requires `project:delete`. **Write** on Projects grants all four project permissions, including `project:delete`. A credential restricted to specific app IDs can read and edit only those apps and cannot create new ones.

```sh
curl "$HOPLITE_API_URL/api/platform/v1/apps" \
  -H "Authorization: Bearer $HOPLITE_SERVICE_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: site-123' \
  --data '{"name":"Customer website","externalId":"site_123","runScript":"npm install && npm run dev","previewPort":3000}'
```

The response contains `app.id`. `externalId` is your own identifier, unique within your organization, and `GET /api/platform/v1/apps?externalId=site_123` finds the app again. `metadata` stores up to 16 KiB of your own JSON. `runScript`, `previewPort`, `setupScript`, `defaultModel`, `instructions`, and [`sandboxImage`](#sandbox-images) configure every thread in the app; change them with `PATCH /api/platform/v1/apps/{appId}`. Apps do not count toward the Direct project limit. `DELETE /api/platform/v1/apps/{appId}` removes the app and everything in it.

Apps and their threads are managed only through the Platform API. They don't appear in Hoplite's project and thread lists, Direct API routes answer their IDs as unknown, and Direct integrations such as MCP, ACP, Slack, Linear, and imports can't reach them.

## Sandbox images

Workspaces boot Hoplite's standard image, and installed dependencies are not part of a saved source. A new workspace, a workspace restored after it was archived for being idle, and every build therefore install dependencies again if your `runScript`, `setupScript`, or build command does so.

`sandboxImage` is an optional app field that boots the app's workspaces and builds from an image with your dependencies already installed. Its value is an image ID, such as `im-...`, that Hoplite gives you. Set it when you create the app or with `PATCH`:

```sh
curl -X PATCH "$HOPLITE_API_URL/api/platform/v1/apps/$APP_ID" \
  -H "Authorization: Bearer $HOPLITE_SERVICE_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: app-image-1' \
  --data '{"sandboxImage":"im-..."}'
```

- Hoplite builds each image and enables it for your organization. To request one, email [support@hoplite.sh](mailto:support@hoplite.sh) with your workspace name and the dependencies or starter project the image should contain.
- An image ID that is not enabled for your organization answers `400` with `{"ok":false,"error":"sandbox_image_not_allowed"}` and leaves the app unchanged.
- A change applies when a thread's workspace is next created or restored.
- Set `sandboxImage` to `null` to return to the standard image. `null` is always accepted, and it is the default.
- An image does not follow updates to Hoplite's standard image. When the standard image changes, Hoplite has to rebuild yours, and you set the new image ID on the app.
- If Hoplite disables an image for your organization, the app's workspaces boot the standard image the next time they are created or restored.
- A workspace that cannot boot the image fails with an error that names the image.

Once an app uses an image, remove dependency installation from its `runScript`, `setupScript`, and build commands where the image already provides those dependencies.

## Start with a saved source

Import your starter files:

```sh
curl "$HOPLITE_API_URL/api/platform/v1/apps/$APP_ID/sources" \
  -H "Authorization: Bearer $HOPLITE_SERVICE_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: starter-1' \
  --data '{"files":[{"path":"index.html","content":"<h1>Hello</h1>"}]}'
```

Start an editing thread with the returned `source.id`:

```sh
curl "$HOPLITE_API_URL/api/platform/v1/apps/$APP_ID/threads" \
  -H "Authorization: Bearer $HOPLITE_SERVICE_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: edit-1' \
  --data "{\"sourceId\":\"$SOURCE_ID\",\"prompt\":\"Create a warm orange landing page\"}"
```

The response contains the thread and admitted run. Poll `GET /api/platform/v1/apps/{appId}/threads/{threadId}/runs/{runId}`. A completed run’s `resultSourceId` identifies its immutable output, even after later edits. Retrieve it with `GET /api/platform/v1/apps/{appId}/sources/{sourceId}/files`; returned file contents are base64, including text. Read the conversation with `GET /api/platform/v1/apps/{appId}/threads/{threadId}/messages`.

A successful run completes only after its source and final message are persisted. Failed runs keep the previous successful source. Send another prompt to `POST /api/platform/v1/apps/{appId}/threads/{threadId}/messages` with `{"content":"Add a contact page"}` and a fresh idempotency key.

Find an app's threads again with `GET /api/platform/v1/apps/{appId}/threads`. It returns the app's threads, newest created first, each in the shape `GET /api/platform/v1/apps/{appId}/threads/{threadId}` returns. `limit` defaults to 100, the maximum. Pass the response's `nextCursor` as `cursor` to read older threads until it is `null`. A cursor is bound to the app and the credential that received it; any other use answers `400 invalid_cursor`.

```sh
curl "$HOPLITE_API_URL/api/platform/v1/apps/$APP_ID/threads?limit=20" \
  -H "Authorization: Bearer $HOPLITE_SERVICE_KEY"
```

```json
{
  "ok": true,
  "threads": [
    { "id": "thr_…", "projectId": "app_…", "sourceId": "src_…", "latestSourceId": "src_…", "status": "ready", … }
  ],
  "nextCursor": "…"
}
```

## Add the rest of your editor

- [Embed previews](/docs/platform/previews) through your authenticated gateway, with HTTP and WebSocket support.
- [Integrate versions and builds](/docs/platform/versions) to combine concurrent edits and retrieve deployment artifacts.
- [Handle lifecycle and retries](/docs/platform/operations) to control idle time, recover uncertain requests, and retain source history.
- [Follow events](/docs/platform/events) for one app or all of them, with a live stream and streaming drafts.
- [Stay within rate limits](/docs/platform/operations#rate-limits): each credential has 600 requests per 60 seconds.

The reference separates Platform Apps, Sources, Threads, Drafts, Previews, Builds, Workspaces, and Events. Download the [Platform OpenAPI specification](/docs/platform-openapi.json), or browse [the API reference](/docs/api). Credential, cancellation, and usage operations remain in the Direct API.

[Site Studio](https://github.com/CarbonCopyInc/hoplite-platform-starter) is a runnable reference application with a preview gateway, published under the MIT licence. Its README maps each Platform concept to the code that uses it and identifies the authentication, storage, and publishing pieces you must provide for a hosted product.

## Sitemap

Sitemap discovery is not enabled for this deployment.
