Skip to content

Repository files navigation

Telegram Mini App with OpenUI Cloud

A reference implementation showing how a Telegram Mini App can turn natural-language prompts into interactive OpenUI responses, charts, image-rich interfaces, reports, and presentations.

The repository combines Telegram's native Mini App launch and signed identity model with OpenUI Cloud's managed generation, conversation storage, artifacts, and React rendering stack. It is intended for developers who want to inspect the integration, run it locally, and adapt the patterns for their own bot.

Reference links

The Mini App is intended to be opened from Telegram. A normal browser can be used while developing locally when development authentication is enabled.

Capabilities

  • Native Telegram launch from the bot profile, menu button, or inline web_app button
  • Server-side validation of Telegram Mini App initData
  • OpenUI AgentInterface with responsive chat, thread history, and interactive generated UI
  • OpenUI Cloud conversation and artifact persistence
  • Streaming Responses API integration over Server-Sent Events
  • Built-in OpenUI Cloud web search and image search
  • Managed presentation and report generation
  • Telegram-aware theming, compact-sheet behavior, haptics, and user greeting
  • Demo deep links that automatically start a selected generation
  • Protected Telegram webhook with /start, /examples, /help, and callback interactions

Using this as a side project

The useful part of this repository is the integration pattern, not the example bot configuration. A small project can keep the Telegram identity, OpenUI Cloud storage, and Agent Interface wiring while replacing the demos and system instructions with one focused job.

For example, you could turn it into:

  • A personal research assistant that searches the web and returns cited visual summaries
  • A lightweight analytics bot that converts pasted data into charts and tables
  • A visual inspiration assistant that combines image search with generated mood boards
  • A presentation assistant that turns a short brief into a shareable deck

To adapt it, change the demo definitions in src/lib/demo-prompts.ts, adjust the Telegram-specific generation instructions in src/app/api/chat/route.ts, replace the artwork and product copy, and configure your own Telegram bot and OpenUI Cloud key. Keep the OpenUI Cloud app ID stable after users begin creating conversations because it is part of the conversation namespace.

Technology stack

Layer Implementation
Application Next.js 16 App Router, React 19, TypeScript
Telegram client bridge telegram-web-app.js and window.Telegram.WebApp
Chat shell AgentInterface from @openuidev/react-ui
Chat state and protocol @openuidev/react-headless
Generative UI library chatLibrary from @openuidev/thesys
Cloud storage useOpenuiCloudStorage() from @openuidev/thesys
Cloud server helpers @openuidev/thesys-server
Model transport OpenAI Node SDK pointed at the OpenUI Cloud Responses-compatible endpoint
Artifact rendering OpenUI presentation and report renderers
Bot integration Telegram Bot API over a Next.js webhook route

The exact package versions used by this repository are pinned in package.json and package-lock.json.

Architecture

OpenUI Cloud separates generation from browser-accessible storage. This application preserves that separation and adds Telegram authentication in front of both paths.

The two OpenUI Cloud planes

Storage plane

The browser uses useOpenuiCloudStorage() to list, create, rename, load, and delete conversations and to load or update artifacts.

  1. The adapter sends POST /api/frontend-token.
  2. The app validates Telegram's signed launch data.
  3. The server mints a short-lived OpenUI Cloud frontend token for the validated identity.
  4. The adapter sends that token directly to https://api.thesys.dev/v1/* in x-thesys-frontend-token.

The token is scoped by both values below:

app_id  = telegram-mini-app
user_id = telegram:<validated Telegram user ID>

This means the browser can access only the conversations and artifacts belonging to that Telegram user inside this app namespace. The master THESYS_API_KEY never reaches the browser.

Generation plane

The client implements ChatLLM directly:

  • send() posts the current conversation ID and only the newest user message to /api/chat.
  • openAIConversationMessageFormat converts the message to the OpenAI Conversations item format.
  • openAIResponsesAdapter() parses the returned Responses SSE stream, including OpenUI and artifact events.

The server then:

  1. Revalidates Telegram initData.
  2. Applies the per-user chat rate limit.
  3. Validates the request shape and size.
  4. Mints a user-scoped frontend token and verifies that the submitted conversation belongs to the authenticated user.
  5. Calls https://api.thesys.dev/v1/embed/responses through the OpenAI SDK.
  6. Associates the request with the existing Cloud conversation using conversation: threadId and store: true.
  7. Enables managed slides, reports, web search, and image search.
  8. Streams every upstream SSE event back to AgentInterface.

OpenUI Cloud is the source of truth for conversation history. The full history is not resent by the browser on every turn because the request references the stored Cloud conversation.

Identity and conversation model

Telegram identity

Telegram supplies two related client values:

  • initData is the raw signed query string. It is forwarded to server routes through X-Telegram-Init-Data and is the only value used for authorization.
  • initDataUnsafe is parsed client context. It is used only for immediate display behavior such as the welcome greeting.

The server validates initData using Telegram's documented HMAC process, checks for duplicate fields, compares the hash in constant time, enforces the configured age limit, parses the user object, and derives the user ID from the validated payload.

The validated first name, last name, and username are added to the generation instructions. The prompt explicitly distinguishes a Telegram handle from the person's name and treats every profile value as untrusted data.

Conversation isolation

OpenUI Cloud conversations are isolated by app_id and user_id:

  • Two Telegram users cannot see each other's conversations.
  • The menu button and /start inline button resolve to the same identity and Cloud namespace.
  • Renaming the bot or changing its @username does not move stored Cloud conversations as long as the Telegram user ID and OPENUI_CLOUD_APP_ID remain unchanged.
  • Changing OPENUI_CLOUD_APP_ID creates a different namespace and makes existing conversations invisible to this app.

OPENUI_CLOUD_APP_ID is intentionally hard-coded as telegram-mini-app in src/lib/openui-cloud.server.ts. Treat it as persistent data-model configuration.

Latest conversation restoration

Cloud remains the durable store, while browser localStorage holds only a per-Telegram-user pointer to the most recently selected thread:

openui:latest-thread:<Telegram user ID>

On a normal launch, the app:

  1. Tries the cached conversation ID.
  2. Falls back to the newest Cloud conversation when no cached ID exists.
  3. Loads a blank welcome state when the user has no conversations.

The first Cloud thread-list request is memoized for the mounted app session to avoid duplicate startup requests. Deleting or switching conversations continues to use OpenUI Cloud through AgentInterface.

Starter launches

The app reads a starter from the first available location:

  1. ?starter=<id>
  2. Telegram initDataUnsafe.start_param
  3. tgWebAppStartParam

When a starter parameter is present, automatic latest-thread restoration is skipped. IDs defined in src/lib/demo-prompts.ts automatically submit the corresponding prompt into a new conversation. The temporary starter query parameter is removed from the visible URL after the demo begins.

Current demo IDs:

  • live-ai-research
  • visual-design-guide
  • product-launch-presentation

Telegram bot experience

The bot webhook accepts only private-chat text messages and callback queries.

Interaction Behavior
/start Sends the branded welcome image, concise onboarding copy, links to demos and OpenUI GitHub, and one Open Agent Mini App button.
/start examples Opens the same capability picker as /examples. This is used by the onboarding demo link.
/examples Sends an interactive list of demos. Selecting one edits the message into a preview with Run this demo and All demos actions.
/help Explains the three-step product flow and provides an Open OpenUI button.
Other text Replies with a handoff explaining that the prompt should be sent inside the Mini App.

Telegram chat messages are not used as the OpenUI conversation history. The bot chat is the launch and onboarding surface; the Mini App conversation is stored in OpenUI Cloud.

Webhook configuration script

scripts/configure-telegram-webhook.mjs configures the bot through the Telegram Bot API. It:

  1. Loads .env.local through the npm script.
  2. Validates the expected bot username.
  3. Requires an HTTPS Mini App URL.
  4. Calls getMe and refuses to continue if the token belongs to another bot.
  5. Registers /api/telegram/webhook with a deterministic secret token.
  6. Restricts webhook updates to message and callback_query.
  7. Registers /start, /examples, and /help.
  8. Sets the long and short bot descriptions.
  9. Sets the native chat menu button to Open OpenUI.

The webhook secret is derived from the bot token with HMAC-SHA256. Telegram sends it in X-Telegram-Bot-Api-Secret-Token, and the webhook route compares it in constant time before reading an update.

BotFather's Main Mini App URL and loading-screen branding are separate Telegram settings. The script configures the webhook, commands, descriptions, and chat menu button, but it does not replace the Main Mini App configuration in BotFather.

Prerequisites

  • Node.js 20.9 or newer, as required by the installed Next.js major
  • npm 10 or a compatible npm version
  • A Telegram bot created through @BotFather
  • The bot token for the intended bot
  • An OpenUI Cloud API key from the Thesys console
  • A public HTTPS URL for Telegram testing

Local development

1. Install dependencies

npm install

2. Create local configuration

cp .env.example .env.local

For a browser-only local preview, the minimum useful configuration is:

THESYS_API_KEY=your_openui_cloud_key
OPENUI_MODEL=google/gemini-3.6-flash-free
ALLOW_DEV_AUTH=true

Development authentication resolves to this fixed identity:

user_id = telegram:dev-user
name    = Local Developer

Because the local preview still talks to OpenUI Cloud, it can create real Cloud conversations and consume Cloud usage. All local browser sessions sharing the same key and development identity see the same development conversation namespace.

3. Start Next.js

npm run dev

Open http://localhost:3000.

ALLOW_DEV_AUTH works only when NODE_ENV is not production. Production requests without valid Telegram initData are rejected even if the variable was accidentally set.

Local Telegram testing with ngrok

Telegram must load Mini Apps and deliver webhooks through a public HTTPS address. localhost is not reachable from Telegram's infrastructure or from another Telegram client.

With the development server running:

ngrok http 3000

Copy the generated HTTPS URL into .env.local:

TELEGRAM_MINI_APP_URL=https://your-current-subdomain.ngrok-free.app/

Then configure Telegram:

npm run telegram:webhook

Keep both npm run dev and ngrok running. Free ngrok URLs normally change when the tunnel restarts, so update TELEGRAM_MINI_APP_URL, rerun the webhook script, and update BotFather's Main Mini App URL whenever the address changes.

Telegram setup

1. Configure the bot variables

TELEGRAM_BOT_TOKEN=123456789:replace_with_the_real_token
TELEGRAM_BOT_USERNAME=your_bot_username
TELEGRAM_MINI_APP_URL=https://your-app.example.com/

TELEGRAM_BOT_USERNAME must not include @. The setup script strips one if present, but the canonical value should be the bare username.

2. Configure the Main Mini App in BotFather

In BotFather:

  1. Send /mybots.
  2. Select the bot.
  3. Open Bot Settings.
  4. Open Configure Mini App.
  5. Enable the Main Mini App and enter the same public URL.
  6. Configure the loading-screen icon and colors if desired.

This creates the prominent app launch entry on the bot profile. It is independent from the chat menu button configured by this repository.

3. Validate without changing Telegram

npm run telegram:webhook -- --dry-run https://your-app.example.com/

Dry-run mode still requires the local bot token, but it validates the URL and username format without making Telegram API calls or verifying which bot owns the token.

4. Apply the configuration

Use the URL in .env.local:

npm run telegram:webhook

Or override the URL for one invocation:

npm run telegram:webhook -- https://your-app.example.com/

The URL argument changes the menu button for that run without rewriting .env.local. Runtime buttons sent by /start, /examples, and /help still use TELEGRAM_MINI_APP_URL from the running server environment.

5. Test all entry points

  • Open the bot profile and tap the Main Mini App launch button.
  • Tap the native Open OpenUI chat menu button.
  • Send /start and tap Open Agent.
  • Send /examples, preview each demo, and run one.
  • Send /help.
  • Send ordinary text and confirm that the bot returns the Mini App handoff.

Environment variables

Variable Scope Required Description
THESYS_API_KEY Server only Yes OpenUI Cloud master API key. Used to mint frontend tokens and call the managed Responses endpoint.
OPENUI_MODEL Server only No OpenUI Cloud model in provider/model form. Defaults to google/gemini-3.6-flash-free.
TELEGRAM_BOT_TOKEN Server only Telegram mode Validates Mini App signatures, derives the webhook secret, calls the Bot API, and derives safety identifiers.
TELEGRAM_BOT_USERNAME Server and setup script Telegram mode Expected bot username without @. Defaults to generativeuibot. Prevents accidental configuration of the wrong bot.
TELEGRAM_MINI_APP_URL Server and setup script Telegram mode Canonical public HTTPS Mini App URL used in Telegram buttons, welcome artwork URLs, the menu button, and webhook derivation.
TELEGRAM_AUTH_MAX_AGE_SECONDS Server only No Maximum accepted age of signed Telegram launch data. Defaults to 86400 seconds.
ALLOW_DEV_AUTH Server only Local only Set to true to use the fixed development identity outside production. Never enable it on a public server.

Never commit .env.local. The repository ignores environment files except for the safe template in .env.example.

API routes

POST /api/frontend-token

Purpose: mint a short-lived OpenUI Cloud token for browser-side conversation and artifact storage.

Authentication:

X-Telegram-Init-Data: <raw Telegram WebApp.initData>

Successful response:

{
  "token": "fct_...",
  "expires_at": 1785312000
}

Behavior:

  • Validates Telegram identity.
  • Applies a process-local limit of 30 requests per user per 60 seconds.
  • Mints the token for telegram:<user ID> and telegram-mini-app.
  • Returns Cache-Control: no-store.

The Cloud storage adapter caches this token and refreshes it before expiry. A Cloud 401 invalidates the cached token and causes one remint and retry.

POST /api/chat

Purpose: authorize and proxy one generation turn to OpenUI Cloud.

Authentication:

Content-Type: application/json
X-Telegram-Init-Data: <raw Telegram WebApp.initData>

Request shape:

{
  "threadId": "conversation_id",
  "input": [
    {
      "type": "message",
      "role": "user",
      "content": "Build a weekly sales dashboard"
    }
  ]
}

Validation constraints:

  • Maximum encoded request body: 24,000 bytes
  • Exactly one new input item
  • User text between 1 and 20,000 characters
  • Conversation ID limited to 1 to 200 ASCII letters, digits, underscores, or hyphens
  • No unrecognized top-level or message fields
  • Conversation must be accessible through the authenticated user's scoped Cloud token
  • Process-local limit of 5 generation requests per user per 60 seconds

Successful response:

Content-Type: text/event-stream
Cache-Control: no-cache, no-transform
X-Accel-Buffering: no

The route propagates browser cancellation to OpenUI Cloud. If the upstream stream fails after headers are sent, the server emits an SSE error event and closes the stream.

POST /api/telegram/webhook

Purpose: process Telegram Bot API updates.

Authentication:

X-Telegram-Bot-Api-Secret-Token: <derived webhook secret>

The route rejects a request before parsing it when the secret header is missing or invalid. Telegram is configured to send only message and callback-query updates.

OpenUI generation configuration

The generation route enables these managed tools:

tools: [
  artifactTool({ artifacts: ["slides", "report"] }),
  { type: "web_search" },
  { type: "image_search" },
]

These tools execute inside OpenUI Cloud. This application does not run a local tool loop because it currently declares no app-owned function tools.

generateSystemPrompt() supplies OpenUI Cloud's built-in chat-library instructions and appends Telegram-specific rules:

  • Optimize for a narrow 360px viewport.
  • Prefer one-column, touch-friendly layouts.
  • Avoid fixed-width and side-by-side designs.
  • Prefer useful generative UI over long prose.
  • Include concise follow-up actions when helpful.
  • Use the validated Telegram first name naturally.

The model can produce normal generative UI in the chat stream or call the managed artifact tool. Presentation and report tool events are connected to dedicated OpenUI artifact renderers and appear in the Agent Interface artifact surfaces.

Telegram WebApp behavior

src/lib/telegram-web-app.ts initializes Telegram-specific behavior after telegram-web-app.js loads:

  • Calls WebApp.ready() to remove Telegram's native loading state.
  • Hides Telegram's Back button because navigation is owned by AgentInterface.
  • Maps header, page, and bottom-bar colors to Telegram theme slots when supported.
  • Enables Telegram's normal vertical swipe behavior.
  • Updates the OpenUI theme when Telegram emits themeChanged.
  • Uses light haptic feedback on prompt submission and error haptics for failed HTTP responses.

The app intentionally does not call WebApp.expand(). Telegram can therefore retain a compact sheet for launch modes that support it, while the user remains free to expand the Mini App.

The loading logo rendered by the web application and Telegram's native pre-page loading screen are different layers:

  • The in-app OpenUI logo is controlled by this repository.
  • Telegram's native loading icon and colors are configured in BotFather and may briefly appear before the web page runs.

Security model

Secrets

  • THESYS_API_KEY is used only by server routes.
  • TELEGRAM_BOT_TOKEN is used only by server routes and the local configuration script.
  • The browser receives only a short-lived, user-and-app-scoped OpenUI Cloud frontend token.
  • No route accepts user_id or app_id from a client request.

Telegram request validation

  • Mini App initData is validated server-side using Telegram's HMAC algorithm.
  • The server rejects missing, duplicate, malformed, expired, or authorization fields dated more than 60 seconds in the future.
  • Hashes and webhook secrets are compared with timingSafeEqual.
  • The default accepted authorization age is 24 hours, configurable through TELEGRAM_AUTH_MAX_AGE_SECONDS.
  • initDataUnsafe is never trusted for authorization.

Conversation authorization

Possessing or guessing a Cloud conversation ID is insufficient. Before generation, /api/chat requests the conversation through a frontend token scoped to the validated Telegram user. A Cloud 403 or 404 becomes an application 403.

Abuse controls

The repository includes request-size limits, strict message-shape validation, a per-user generation limit, a frontend-token limit, and a stable HMAC-derived safety_identifier.

The current rate limiter is an in-memory map stored on the Node.js process. It is useful as a local guard but is not a global quota. Multiple application instances do not share counters, and counters disappear when an instance is recycled. Replace it with a shared store such as Redis or a managed rate-limit service before relying on it as the primary abuse-control boundary.

Trust boundary for generated UI

The model composes components from OpenUI's registered library. It does not send arbitrary React code for browser execution. External images returned by image search are still loaded from their source URLs, so the availability and hotlink policy of those hosts remain outside this application's control.

Running it as a shared side project

Telegram needs a stable public HTTPS URL when other people use the Mini App. Run this Next.js application on any platform that supports Node.js server routes and environment variables, then configure at least these values:

THESYS_API_KEY=your_openui_cloud_key
OPENUI_MODEL=google/gemini-3.6-flash-free
TELEGRAM_BOT_TOKEN=your_bot_token
TELEGRAM_BOT_USERNAME=your_bot_username
TELEGRAM_MINI_APP_URL=https://your-app.example.com/
TELEGRAM_AUTH_MAX_AGE_SECONDS=86400

Do not enable ALLOW_DEV_AUTH in a publicly accessible environment. Build and start the application with:

npm run build
npm run start

The build script uses Webpack explicitly:

"build": "next build --webpack"

next.config.ts replaces Webpack's first minimizer with terser-webpack-plugin. Minification remains enabled. This workaround avoids a duplicate lexical-identifier error previously produced when optimized pre-bundled OpenUI artifact code was emitted. Re-test the presentation and report chunks before removing or changing this configuration.

Connect the public URL to Telegram

After the public URL is known:

  1. Set TELEGRAM_MINI_APP_URL wherever the application runs and restart it.
  2. Update the Main Mini App URL in BotFather.
  3. Set the same URL in local .env.local so the setup script uses it.
  4. Run npm run telegram:webhook to update Telegram's webhook and menu button.
  5. Send /start again to create a new message containing the current inline button URL.

Existing Telegram messages retain the URL embedded when they were sent.

Webhook runtime model

The webhook is a Next.js server route in the same application. There is no separate webhook program to keep running. Telegram sends updates to /api/telegram/webhook, and the application's Node.js runtime handles them.

Running npm run telegram:webhook does not serve the webhook. It is a one-time configuration command that tells Telegram where the public route lives and configures the bot's other Bot API settings.

Available scripts

Command Purpose
npm run dev Start the Next.js development server.
npm run build Create a production Webpack build with the configured Terser minimizer.
npm run start Serve an already-built production application.
npm run lint Run ESLint across the repository.
npm run typecheck Run TypeScript without emitting files.
npm run telegram:webhook Configure Telegram using .env.local.
npm run telegram:webhook -- --dry-run <url> Validate setup inputs without calling Telegram.
npm run telegram:webhook -- <url> Configure Telegram with a one-time Mini App URL override.

Project structure

.
├── public/
│   └── telegram-openui-welcome.png       Telegram onboarding artwork
├── scripts/
│   └── configure-telegram-webhook.mjs    Bot API configuration command
├── src/
│   ├── app/
│   │   ├── api/
│   │   │   ├── chat/                     Authorized Cloud generation proxy
│   │   │   ├── frontend-token/           User-scoped Cloud token minting
│   │   │   └── telegram/webhook/         Telegram bot update handler
│   │   ├── globals.css                   Telegram and Agent Interface styling
│   │   ├── layout.tsx                    Metadata, viewport, Telegram script
│   │   └── page.tsx                      Mini App entry route
│   ├── components/
│   │   └── telegram-cloud-agent.tsx      Agent Interface and Cloud wiring
│   ├── lib/
│   │   ├── demo-prompts.ts               Telegram and Mini App starters
│   │   ├── openui-cloud.server.ts        Stable Cloud identity and API helpers
│   │   ├── rate-limit.server.ts          Process-local request limits
│   │   ├── telegram-auth.ts              Signed initData validation
│   │   ├── telegram-bot.ts               Bot API and webhook helpers
│   │   └── telegram-web-app.ts           Telegram browser bridge
├── .env.example                          Safe environment template
├── next.config.ts                        Production Webpack minimizer override
└── package.json                          Dependencies and commands

Common changes

Add or edit a demo

Update src/lib/demo-prompts.ts. A single entry drives:

  • The Telegram /examples label
  • The Telegram preview title and description
  • The Mini App welcome starter
  • The auto-submitted demo prompt

Keep IDs stable after publishing links because Telegram messages can retain old starter URLs.

Change the model

Set OPENUI_MODEL to an OpenUI Cloud-supported provider/model identifier and restart the application. The app does not expose a client-side model selector, so the server setting applies consistently to all users.

Add an app-owned function tool

The current tools are managed by OpenUI Cloud. To add an app-owned function tool:

  1. Add its Responses API schema to the tools array in /api/chat.
  2. Implement the tool on the server.
  3. Add a controlled server-side tool loop that executes only explicitly registered function names.
  4. Preserve conversation consistency when handling cancellation or tool errors.
  5. Never execute arbitrary tool names or arguments from the model without validation.

The OpenUI Cloud CLI template includes a reference server-owned tool loop that can be used as an implementation guide.

Change the bot username

  1. Change the username through BotFather.
  2. Update TELEGRAM_BOT_USERNAME wherever the application and setup script run.
  3. Ensure TELEGRAM_BOT_TOKEN belongs to that bot.
  4. Restart the application.
  5. Rerun npm run telegram:webhook.
  6. Update README and product links.

If migration uses an entirely different bot token, users launch with signatures tied to the new bot. OpenUI conversation continuity still depends on stable Telegram user IDs and the unchanged OPENUI_CLOUD_APP_ID.

Change the Mini App URL

Update all four places:

  1. The running application's TELEGRAM_MINI_APP_URL
  2. The setup script's local .env.local
  3. BotFather Main Mini App URL
  4. Telegram menu and webhook settings through npm run telegram:webhook

Then restart the application and send a new /start message.

Troubleshooting

Open this application from its Telegram bot.

The server did not receive valid Telegram launch data.

  • Open the public URL through the Telegram bot rather than a normal browser.
  • Confirm the page includes telegram-web-app.js before the application scripts.
  • Confirm the configured TELEGRAM_BOT_TOKEN belongs to the bot that opened the Mini App.
  • For local browser development only, set ALLOW_DEV_AUTH=true and use a non-production build.

/start or /examples receives no bot response

  • Confirm the public application and webhook route are reachable.

  • Confirm TELEGRAM_BOT_TOKEN, TELEGRAM_BOT_USERNAME, and TELEGRAM_MINI_APP_URL are set in the server environment.

  • Run npm run telegram:webhook again after changing the URL or token.

  • Inspect Telegram's webhook state:

    curl "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getWebhookInfo"
  • Verify that the reported URL ends in /api/telegram/webhook and that Telegram reports no recent delivery error.

The menu button and /start open different URLs

Telegram stores button URLs in more than one place:

  • The native menu button is set by setChatMenuButton when the configuration script runs.
  • New /start buttons are generated from the running application's TELEGRAM_MINI_APP_URL at webhook request time.
  • The bot profile launch uses BotFather's Main Mini App URL.
  • Existing messages keep their old embedded URL.

Set every surface to one canonical URL, restart the application, rerun the webhook script, and send /start again.

A demo opens but does not run

  • Confirm the starter ID exists in DEMO_PROMPTS.
  • Confirm the button URL contains the intended starter query parameter.
  • Start a new conversation or delete the failed empty one if a thread was created before the prompt ran.
  • Inspect browser console and /api/chat response status for authentication, ownership, or Cloud quota errors.

The latest conversation does not restore

  • Confirm the frontend-token request succeeds.
  • Check that browser storage is available in the Telegram WebView.
  • Open the Agent Interface sidebar and verify the conversation still exists in OpenUI Cloud.
  • A starter launch intentionally suppresses latest-conversation restoration.
  • If the cached thread was deleted, the app clears the stale pointer after the failed load and can fall back on a later launch.

OpenUI Cloud returns 401 or 403

  • Verify THESYS_API_KEY against the Thesys console.
  • Confirm the key is configured in the server environment, not only locally.
  • Restart the application after changing the key.
  • A chat-route 403 can also mean that the submitted conversation does not belong to the authenticated Telegram user.

OpenUI Cloud returns 429

The workspace may be out of credits or temporarily rate-limited. Development responses expose the credit-oriented message; production responses intentionally use a less specific temporary-usage message.

Some image-search images fail while the response succeeds

Image search runs inside OpenUI Cloud, but the OpenUI image components load returned image URLs directly in the Telegram WebView. Individual source sites can expire URLs, reject hotlinking, rate-limit a client, redirect unexpectedly, or block Telegram's webview user agent.

Use these signals to locate the failure:

  • If /api/chat emits an SSE error or the image-search tool fails, investigate OpenUI Cloud or the selected model/tool request.
  • If the response completes and only selected browser image requests fail, the failure is usually the source host or direct-client loading path.
  • For production-critical images, add a controlled image proxy or copy permitted assets into durable application storage. Enforce content-type, size, timeout, redirect, and hostname protections before proxying arbitrary URLs.

A built application reports Identifier has already been declared

Confirm the application uses npm run build, which forces Webpack, and confirm the Terser minimizer override in next.config.ts is still present. Clear the platform's build cache and rebuild if it used an older configuration.

TypeScript or lint passes locally but the shared build differs

  • Confirm the local and remote Node.js versions satisfy Next.js requirements.
  • Use npm install so package-lock.json determines dependency versions.
  • Run the same build command locally with npm run build.
  • Confirm the platform is building the intended branch and commit.
  • Rebuild without a cached output when debugging stale optimized chunks.

Verification checklist

Before sharing an update:

npm install
npm run typecheck
npm run lint
npm run build

Then verify in Telegram:

  • Light and dark themes
  • Compact and expanded sheet heights
  • Menu, profile, /start, and demo entry points
  • New conversation creation
  • Existing conversation restoration
  • Conversation switching and deletion
  • Follow-up buttons and form submissions
  • Web-search citations and image-search rendering
  • Presentation and report artifact opening
  • Stream cancellation and retry behavior
  • Expired or invalid Telegram launch data
  • Cloud 401, 403, and 429 handling

Known operational limitations

  • Rate limits are process-local and are not a distributed production quota.
  • Conversation content and artifacts are stored in OpenUI Cloud, not Telegram or the application database.
  • localStorage is only a recent-thread pointer and may be unavailable or cleared by a client.
  • Image-search results can depend on third-party hotlink availability.
  • The webhook handles private-chat text and callback queries only.
  • Freeform bot messages are not copied into the Mini App composer.
  • The app uses one server-selected model for all users.
  • Changing the fixed OpenUI Cloud app ID creates a new visible conversation namespace.
  • BotFather settings and server environment variables remain external configuration and are not reproduced by a Git checkout alone.

Further reading

About

OpenUI renderer integration for a Telegram Mini App

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages