> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thinkplatform.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Choose User guide for browser tasks, Developers for CLI, SDK and external agents, and Administration for organization management.
> Confirm identity and organization/project context before operations. Documentation access does not grant ThinkPlatform permissions.
> Review side effects, verify changes with a read, and do not blindly retry an operation with an uncertain outcome.
> Use documented UI steps for the web app and documented public CLI or SDK interfaces for automation. Follow each page’s prerequisites and authentication requirements.

# App development commands

> Publish container apps, connect a Canvas application page, and use request-bound SDK access.

These examples assume an authenticated active saved profile with the intended organization and project. Confirm it with `tp context show` before running commands. Add root `--json` before the command when machine parsing needs JSON output; use root `--profile <name>` only to override the active profile.

## CLI context and preparation

Use the installed `tp --help` and `tp apps publish --help` when checking version
support. Select the intended profile, organization and project through the usual
global CLI options; do not copy credentials into the source tree. Examples below
assume that context is already selected.

The current publication API requires a companion HTML Dynamic UI in the same
project. Create or select it with `tp dynamic-ui` or `client.canvas.dynamic_ui`.
It is an access/resource anchor; the container provides the actual app UI:

```python theme={"system"}
ui = client.canvas.dynamic_ui.create({
    "renderer": "html", "title": "My app", "owner": {"scope": "project"},
    "html": "<!doctype html><title>My app</title>",
})
```

Use the returned `uiId` as `--ui-id`. Inspect the returned mapping rather than
guessing an ID. Provision needed Tables with `tp db tables create --body @table.json`
or reuse a suitable existing Table. Do not change existing column contracts to
fit an unrelated demo.

## Publish source or an existing image

```sh theme={"system"}
tp apps publish my-app --name "My app" --source ./my-app \
  --ui-id "$UI_ID" --request-id "$REQUEST_UUID" --wait 300 \
  --table-read "$TABLE_ID" --table-write "$TABLE_ID"
tp apps show my-app
tp apps list
```

Generate a UUID once for a new publication; retain it before submitting. A wait
limit or temporary transport error does not establish that a build failed. Resume
with the same source, options and `--request-id`; inspect the recorded state before
creating another request. `--wait 0` advances one step without polling. Do not
automatically resubmit an operation explicitly reported as terminally failed.

For a prebuilt image replace `--source` with
`--image registry.example/projects/p4/apps/my-app@sha256:<digest>` using the actual
organization Registry address. `tp registry push` returns an immutable digest when
pushing from a Docker-capable environment. Source publication combines remote
build, push and deployment; an Agent Workspace does not need Docker-in-Docker.

The compressed context is limited to 50 MiB. The CLI archive does not interpret
`.dockerignore`: in Git it includes tracked files and nonignored untracked files,
subject to built-in exclusions for common credential/cache paths. Use `.gitignore`
for local-only files and inspect tracked files separately. Outside Git, stage a
dedicated source directory containing only intended build inputs. Keep
`.dockerignore` for Docker's own context handling. Build the production frontend
in a Docker stage rather than uploading `node_modules`. Include a wheel only when
needed by the app; do not embed package-feed passwords in the Dockerfile.

Optional SDK permissions:

* Repeat `--table-read` / `--table-write` for exact Table IDs.
* `--lakehouse-read` allows the delegated read-only statement API for the project.
* Repeat `--project-permission` for supported Canvas, Flow or Module read scopes;
  inspect CLI help for the current accepted values.

For updates, inspect `tp apps show my-app`, then publish using a new request UUID
and `--replaces "$OLD_RELEASE"`. Use `--replaces-stopped` only if that exact release
is stopped. `--replace-current` is a convenience for the initial submission;
retain the resolved previous release for retries. Stop an exact release with:

```sh theme={"system"}
tp apps stop my-app --release "$RELEASE"
```

## Canvas application page

Use the existing configured SDK client to create a dedicated app page:

```python theme={"system"}
page = client.canvas.pages.create({
    "title": "My app",
    "layout": {"surface": "application", "contentWidth": "full"},
    "application": {"source": {"kind": "hosted-app", "appId": "my-app"}},
    "blocks": [],
})
```

Use the returned page ID in `/canvas/<page-id>` on the selected TP host.
For an existing HTML-only app the source is
`{"kind": "dynamic-ui", "uiId": "<ui-id>"}`. A dashboard/document embedding instead
uses a widget of kind `hosted-app` with `config: {"appId": "my-app"}`. Dedicated
application pages have no widget frames or document blocks.

## Backend Python SDK

Install the released ThinkPlatform client available in the target environment.
The private `ThinkPlatform` package also includes the SDK. Keep imports independent
of Agent/Flow runtime modules.

```python theme={"system"}
from fastapi import FastAPI, Request
from starlette.concurrency import run_in_threadpool
from thinkplatform.sdk import client_from_hosted_request

app = FastAPI()

@app.get("/api/items")
async def items(request: Request):
    def read():
        with client_from_hosted_request(request.headers) as tp:
            return tp.db.rows.query(TABLE_ID, {"page_size": 100})
    return await run_in_threadpool(read)
```

`TABLE_ID` is app configuration and must match the declared permissions. The
headers include a short-lived invocation handle and bound org/project; retain
neither headers nor client in global state or background jobs. The SDK routes via
the platform-injected broker. The application receives no TP user access token.

Use `tp.db.rows.create(TABLE_ID, values)` and
`tp.db.rows.update(TABLE_ID, row_id, values)` for writes, following the installed
method signatures. For a project Lakehouse read:

```python theme={"system"}
with client_from_hosted_request(request.headers) as tp:
    statement = tp.lakehouse.statements.execute("SELECT 1 AS example", wait_timeout="0s")
    current = tp.lakehouse.statements.get(statement.statement_id)
```

Poll within the current authenticated request or make another authenticated app
request; use the returned statement ID and inspect its status. SQL result values
follow the native API's representation, which may contain numeric strings.
Read-only Lakehouse permission does not allow SQL writes, and SDK method existence
does not imply that the delegated transport admits that operation.

## Verify the delivered app

Verify publication readiness, launch through TP, the requested app action, and
persisted readback after reload. For data apps include a denied operation outside
the declared resource scope when a suitable test exists. Report separately any
untested multi-user or revocation scenario; one successful operator request does
not prove the user's app invocation path.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.