---
title: Telegram Mini App Flow
tags: [flow, telegram, mini-app, auth, bilingual, RTL, shop, cart, payment]
related_models: ["[[User]]"]
related_apis: ["POST /api/auth/telegram", "[[Auth API]]"]
task: "5.4"
---

> **Last updated:** 2026-06-08
> **Status:** IN PROGRESS — Task 5.4 (dependencies: 5.1 auth infra, 5.2 Telegram sign-in endpoint)
> **Frontend branch:** `integrate-main-into-development` · v2.8.94+
> **Entry point:** `src/sections/telegram/` · route `/telegram`

# Telegram Mini App Flow

End-to-end specification for the **Amaneh Telegram Mini App** — a fully self-contained marketplace shell surfaced inside Telegram's in-app browser via the WebApp SDK. Buyers and sellers can browse requests, create new escrow requests, shop seller templates, manage a cart, review offer state, follow payments, and message each other without leaving Telegram.

> **Two separate Mini Apps exist on this platform.** This document covers the **main marketplace Mini App** (`amn.gg/telegram`) built inside the primary Next.js frontend. For the AI-assisted request-creation Mini App, see [[amanat-assist]].

---

## 1. Architecture Overview

```
Telegram Client
  └─ Mini App iframe (https://amn.gg/telegram)
       └─ TelegramMiniAppView           ← shell orchestrator
            ├─ useTelegramLiveContext   ← SDK probe + polling
            ├─ useTelegramLanguage      ← EN / FA detection
            ├─ useTelegramAutoSignIn    ← silent JWT exchange
            ├─ useTelegramMainButton    ← native chrome sync (disabled)
            ├─ useTelegramBackButton    ← native chrome sync
            ├─ useTelegramHaptic        ← haptic wrapper
            ├─ useTelegramCart          ← shared localStorage cart
            ├─ useTelegramNotifications ← unread badge count
            │
            ├─ [state: loading]         → TelegramLoadingState
            ├─ [state: unsupported]     → TelegramUnsupportedState
            ├─ [state: unlinked]        → TelegramUnlinkedState
            └─ [state: linked]
                 ├─ TelegramHeader
                 ├─ TelegramTabBar (Home / Shop / Requests / Chat / Account)
                 │
                 ├─ [drilldown] TelegramPaymentView        ← highest priority
                 ├─ [drilldown] TelegramChatThreadView
                 ├─ [drilldown] TelegramRequestDetailView
                 ├─ [drilldown] TelegramTemplateDetailView
                 ├─ [drilldown] TelegramSellerShopView
                 │
                 ├─ [overlay] TelegramPointsView
                 ├─ [overlay] TelegramSettingsView
                 ├─ [overlay] TelegramAddressesView
                 ├─ [overlay] TelegramCartView
                 ├─ [overlay] TelegramCheckoutView
                 ├─ [overlay] TelegramNotificationsView
                 ├─ [overlay] TelegramNewRequestView
                 │
                 ├─ TelegramHomeView
                 ├─ TelegramShopView → TelegramSellerShopView
                 ├─ TelegramRequestsView → TelegramRequestDetailView
                 ├─ TelegramChatView → TelegramChatThreadView
                 └─ TelegramAccountView
```

The shell is a **single-page, no-router** design: all navigation (tabs, overlays, detail drilldowns) is pure React state in `TelegramMiniAppView`. `window.location.assign` is used only as a final escape hatch to external URLs. `openTelegramExternalLink` is used for deep links into the web dashboard, which opens inside Telegram's WebView or an external browser depending on the Telegram client.

---

## 2. Launch Points

| Entry | Mechanism | `startapp` context |
|---|---|---|
| Bot profile | User opens bot → taps "Open App" | none |
| Menu button | Pinned button in any chat with the bot | none |
| Inline button | Bot sends a card with an embedded button | `req_<requestId>` |
| Direct deep link | `https://t.me/AmanehBot/app?startapp=req_<id>` | `req_<requestId>` |
| Web fallback | Browser at `/telegram` | none (unsupported state) |

`startapp` / `tgWebAppStartParam` is read from either the WebApp SDK (`window.Telegram.WebApp`) or from URL query/hash params (for older Telegram clients that append them directly).

---

## 3. amanat-assist vs Main Mini App

Two distinct Telegram Mini Apps exist for this platform:

| Property | Main Mini App (this doc) | amanat-assist |
|---|---|---|
| URL | `amn.gg/telegram` | `assist.amn.gg` |
| Bot | AmanehBot | AmanehBot (same) |
| Codebase | `frontend/` (Next.js, `src/sections/telegram/`) | `/amanat-assist` (React + Vite, separate repo) |
| Purpose | Full marketplace shell: browse, buy, sell, chat, manage account | Conversational LLM wizard to create one purchase request |
| LLM | None | Mistral → DeepSeek fallback (via `amanat-llm-proxy` on port 3001) |
| Backend access | Direct calls to `api.amn.gg` | Proxied through `amanat-llm-proxy` which holds the LLM API keys |
| Auth | Telegram `initData` → `POST /api/auth/telegram` | Same endpoint; also supports web redirect via `?access_token=` |
| Deep links between apps | Main Mini App has "New Request" overlay with an "Open Assist" CTA that navigates `window.location.href` to `assist.amn.gg?access_token=...` | Assist submits the finished request then the user returns to the main app |
| Status | In production | Live at `assist.amn.gg` v1.1.0 |

**Hand-off from main app to assist:** `handleOpenAssist()` in `TelegramMiniAppView` constructs a URL to `https://assist.amn.gg` with `access_token`, `user_json`, `theme`, and `source=miniapp` query params. `window.location.href` is used (not `openLink`) to keep the navigation inside Telegram's WebView rather than opening Safari on iOS.

---

## 4. SDK Initialisation & Context Probe

**File:** `src/utils/telegram-webapp.ts` · `getTelegramContext()`

The function assembles a `TelegramContext` object from:

1. `window.Telegram.WebApp` — primary SDK surface (available when the app is opened inside Telegram).
2. URL query/hash fallback — `tgWebAppStartParam`, `tgWebAppData`, `tgWebAppVersion`, `tgWebAppPlatform` — used by older clients or during dev testing.

**Fields extracted:**

| Field | Source | Notes |
|---|---|---|
| `isMiniApp` | Any Telegram signal present | Drives unsupported vs unlinked state |
| `initData` | `webApp.initData` or `tgWebAppData` URL param | HMAC-signed payload sent to `/api/auth/telegram` |
| `initDataUnsafe` | `webApp.initDataUnsafe` | Client-side user identity (not trusted) |
| `safeArea` | `contentSafeAreaInset` or `safe_area_insets` | Parsed to `{top, right, bottom, left}` in px |
| `theme` | `webApp.themeParams` | Both camelCase and snake_case normalised |
| `platform` | `webApp.platform` or URL param | e.g. `ios`, `android`, `tdesktop` |
| `startParam` | `startapp` / `tgWebAppStartParam` / `start_param` | Deep-link context |
| `isUnsupported` | `!webApp && Boolean(startParam)` | Partial signal — no SDK but has URL param |

**Polling on mount** (`useTelegramLiveContext`): Telegram sometimes finishes injecting the WebApp object after the first React render. The hook re-probes at 0 ms, 100 ms, 500 ms, and 1000 ms after mount, and also re-probes on `hashchange` events (triggered by the native back-button on some platforms).

---

## 5. Shell State Machine

`getTelegramStatus(context, hasWebAccount)` returns one of three states:

```
unsupported ─── !context.isMiniApp
                (opened in browser, not Telegram)

unlinked ─────── isMiniApp && (!user || !telegramUser.id)
                (inside Telegram but no JWT session linked)

linked ──────── isMiniApp && user && telegramUser.id
                (authenticated, full shell rendered)
```

State transitions occur on:
- Auth session check completing (`loading → false`)
- Telegram auto sign-in completing (`tgAuthLoading → false`)
- Manual sign-in button tap (unlinked → linked)

---

## 6. Authentication Flow

### 6.1 Silent Auto Sign-In

**Hook:** `useTelegramAutoSignIn` · **File:** `hooks/use-telegram-auto-sign-in.ts`

On mount, if `context.isMiniApp && context.initData && !user`:

1. Exchange `initData` for a JWT by calling `signInWithTelegram({ initData })` → `POST /api/auth/telegram`.
2. On success, call `checkUserSession()` to refresh the auth context.
3. If the backend returns `isNewUser: true`, show `TelegramOnboardingSheet`.
4. A `useRef` deduplication guard (`attemptedInitDataRef`) prevents re-runs under React Strict Mode's double-effect behaviour.

### 6.2 Manual Sign-In (Unlinked State)

When `initData` is present but auto sign-in failed (or hasn't run yet), `TelegramUnlinkedState` renders:
- **Continue with Telegram** — calls the same `signIn()` function from `useTelegramAutoSignIn`.
- **Sign in with email** — `window.location.assign(paths.auth.jwt.signIn)`.
- **Create an account** — `window.location.assign(paths.auth.jwt.register)`.

When `initData` is absent (accessed via a path that skips Telegram context), only the email/register buttons appear.

### 6.3 Backend Endpoint

`POST /api/auth/telegram` — expects `{ initData: string }`. Backend verifies the HMAC using the Telegram bot token, extracts `user` from the payload, upserts a `User` record (`telegramId`, `telegramVerified: true`), and issues a JWT + refresh token. Returns `{ token, refreshToken, isNewUser }`.

Registered at `authRoutes.ts` line 24: `router.post("/telegram", ctrl.telegramAuth.bind(ctrl))` — public route, no auth middleware.

### 6.4 Session Linking (Telegram ↔ Amaneh Account)

The `POST /api/auth/telegram` endpoint both creates and links accounts:

- **New Telegram user, no existing Amanat account:** a new `User` is created with `telegramId` set; `isNewUser: true` is returned and the onboarding sheet is shown.
- **Existing Amanat account with the same `telegramId`:** the existing user is returned; session continues.
- **Existing Amanat account that has never used Telegram:** `telegramId` and `telegramVerified: true` are written onto the existing record (matched by Telegram user id).

After the JWT is issued the standard `checkUserSession()` re-hydrates the React auth context. The Mini App shell reads `user.telegramVerified` and `user.isEmailVerified` from this context to render verification chips in the Account tab.

---

## 7. Navigation Model

All navigation is in-shell React state — no Next.js router is involved.

```
activeTab             : 'home' | 'shop' | 'requests' | 'chat' | 'account'
overlayScreen         : 'new-request' | 'notifications' | 'cart' | 'checkout'
                        | 'points' | 'settings' | 'addresses' | null
openConversationId    : string | null
openRequestId         : string | null
openPaymentRequestId  : string | null   ← payment drilldown (highest priority)
paymentCheckoutFlow   : boolean         ← true when reached from shop checkout
openSellerId          : string | null
openTemplate          : { template, seller } | null
```

**Priority rendering** (first match wins):

1. `openPaymentRequestId` → `TelegramPaymentView` ← new, highest priority
2. `openConversationId` → `TelegramChatThreadView`
3. `openRequestId` → `TelegramRequestDetailView`
4. `openTemplate` → `TelegramTemplateDetailView` ← new
5. `openSellerId` → `TelegramSellerShopView`
6. `overlayScreen === 'points'` → `TelegramPointsView` ← new
7. `overlayScreen === 'settings'` → `TelegramSettingsView` ← new
8. `overlayScreen === 'addresses'` → `TelegramAddressesView` ← new
9. `overlayScreen === 'cart'` → `TelegramCartView`
10. `overlayScreen === 'checkout'` → `TelegramCheckoutView` ← new (replaces web handoff)
11. `overlayScreen === 'notifications'` → `TelegramNotificationsView`
12. `overlayScreen === 'new-request'` → `TelegramNewRequestView`
13. `activeTab` → appropriate tab view

**Back button** (Telegram native `BackButton`) dismisses in reverse priority order:
- Payment drilldown → if `paymentCheckoutFlow`, steps back to cart; otherwise clears payment state.
- Chat thread → clears `openConversationId`.
- Request detail → clears `openRequestId`.
- Template detail → clears `openTemplate`.
- Seller shop → clears `openSellerId`.
- Overlay (`checkout` steps back to `cart`) → clears `overlayScreen`.
- Non-home tab → returns to `home`.

`BackButton` visibility: shown whenever `state === 'linked'` and either an overlay/drilldown is active, or `activeTab !== 'home'`.

`MainButton` visibility: **intentionally disabled** (`isReady: false`) — the native Telegram MainButton cannot use the project font and duplicates in-shell CTAs, so it is kept hidden. All primary actions live inside the shell UI itself.

Both chrome buttons retain the amaneh saffron palette (`color: #C2410C`, `text_color: #FFFFFF`) via `setParams` (WebApp SDK >= 6.1) as a fallback should the MainButton ever be re-enabled.

---

## 8. Tab Structure

The shell has **five bottom tabs** rendered by `TelegramTabBar`:

| Tab | Icon | View | Purpose |
|---|---|---|---|
| Home | house | `TelegramHomeView` | Welcome banner, quick-action cards, escrow-state chips |
| Shop | storefront | `TelegramShopView` | Sellers list; drill into seller store; add templates to cart |
| Requests | list | `TelegramRequestsView` | User's escrow requests with status stepper |
| Chat | speech bubble | `TelegramChatView` | Conversation list + support entry |
| Account | person | `TelegramAccountView` | Profile, preferences, links to web dashboard sections |

`handleTabSelect` clears all overlays and drill-down IDs before switching tab.

---

## 9. Supported Flows

### 9.1 Home Tab

`TelegramHomeView` is the landing screen shown on first open. It contains:
- **Welcome banner** (`TelegramWelcomeBanner`): escrow account summary, primary CTA.
- **Quick-action cards** (`TelegramQuickActions`): shortcuts to Requests, Payments, Chat.
- **Escrow state chips** (`TelegramEscrowStateChips`): legend of status values visible in the platform.
- **"New Request" CTA** → opens `overlayScreen = 'new-request'`.
- **"Open Assist" CTA** → calls `handleOpenAssist()` to navigate to `assist.amn.gg` in the same WebView (see section 3).

### 9.2 Shop Tab — Sellers List

**`TelegramShopView`** (`telegram-shop-view.tsx`):
- Fetches all sellers via `useTelegramShops()` → SWR wrapping `getTemplateSellers()` → `GET /api/request-templates/sellers`.
- Renders `TelegramShopRow` per seller: avatar, name, rating, template count, sales count.
- Shows a floating cart badge button (`TelegramCartFab`) in the header when `totalItems > 0`; tap opens `overlayScreen = 'cart'`.
- Tap a seller row → sets `openSellerId` → navigates to `TelegramSellerShopView`.

### 9.3 Shop Tab — Seller Store

**`TelegramSellerShopView`** (`telegram-seller-shop-view.tsx`):
- Fetches seller + active templates via `useTelegramSellerShop(sellerId)` → `GET /api/request-templates/sellers/:id`.
- Dark header: seller avatar, name, rating, template count, description.
- Each template card shows: image, title, 2-line description, budget range, usage count.
- **Two actions per template:**
  - **Add to cart / Remove from cart** — toggles item in `useTelegramCart` (localStorage, no API).
  - **View template details** — sets `openTemplate` → navigates to `TelegramTemplateDetailView`.
- Floating "Cart · N templates" sticky button at bottom when `totalItems > 0`; tap calls `onOpenCart()`.

### 9.4 Shop Tab — Template Detail

**`TelegramTemplateDetailView`** (`telegram-template-detail-view.tsx`):
- Full-screen view of a single template.
- Shows full description, seller info, price, delivery info, usage/capacity counters.
- Add/remove cart action; direct "Order this template" link to `/dashboard/request/from-template?shareableLink=...` (exits to web dashboard).
- Back button returns to the seller store (`openTemplate` cleared, `openSellerId` retained).

### 9.5 Shopping Cart Overlay

**`TelegramCartView`** (`telegram-cart-view.tsx`):
- Rendered as `overlayScreen = 'cart'`; dismissed by Telegram BackButton.
- Lists each cart item: image, name, seller name, USDT price × quantity, +/− quantity controls, remove button.
- Subtotal/total in USDT, locale-formatted (`fa-IR` for Persian, `en-US` for English); amounts always `dir="ltr"`.
- **"Continue to payment"** → calls `onCheckout()` which sets `overlayScreen = 'checkout'` (in-shell checkout, replacing the previous web handoff).

**Cart storage (`useTelegramCart`):**
- Reads/writes `localStorage` key **`app-request-template-checkout`** — the same key the web `RequestTemplateCheckoutProvider` reads. This enables the web dashboard checkout to hydrate the same cart.
- Dispatches a custom `tg-cart-changed` DOM event on every write; listens on both that event and the native `storage` event so all open tabs stay in sync.
- Operations: `addTemplate(template, seller)`, `removeItem(itemId)`, `changeQuantity(itemId, qty)`, `isInCart(templateId)`.
- No API calls — cart is purely client-side until checkout.

### 9.6 In-Shell Checkout Overlay

**`TelegramCheckoutView`** (`telegram-checkout-view.tsx`):
- Rendered as `overlayScreen = 'checkout'`; BackButton steps back to `overlayScreen = 'cart'`.
- A 3-step stepper running entirely inside the Mini App shell:
  - **Step 0 (Cart review):** item list, quantities, totals, discount.
  - **Step 1 (Address):** physical address or online delivery email.
  - **Step 2 (Payment):** wallet-based payment execution.
- On successful order (`onPlaced(reqId)` callback):
  - If a `reqId` is returned, sets `paymentCheckoutFlow = true` and `openPaymentRequestId = reqId` → immediately opens the payment view.
  - If no `reqId`, switches `activeTab` to `'requests'`.
- Stock validation clamps or removes items exceeding `remainingCapacity` before payment.
- Integrates with `onManageAddresses()` to open the `addresses` overlay mid-flow.

### 9.7 Payment View (In-Shell)

**`TelegramPaymentView`** (`telegram-payment-view.tsx`):
- Highest-priority drilldown (rendered before all other overlays).
- Loaded for a specific `requestId`. Used from two entry points:
  - **Shop checkout flow** (`paymentCheckoutFlow = true`): after `TelegramCheckoutView` creates the requests. Shows a 3-step progress header (cart → address → payment).
  - **Requests tab** (`paymentCheckoutFlow = false`): buyer taps "Pay" on an existing request. No progress header.
- Fetches request details via `useTelegramRequest`.
- Fetches offers via `useTelegramOffers`.
- Calls `getPaymentOptions()` → `GET /api/payment/options` and `createDirectBalanceIntent()` → `POST /api/payment/direct-balance`.
- Polls `checkDirectBalancePayment()` for confirmation.
- On successful payment: calls `onPaid()` → clears `openPaymentRequestId`, switches to `activeTab = 'requests'`.
- Back button: if `paymentCheckoutFlow`, steps back to `overlayScreen = 'cart'`; otherwise clears the payment state.

### 9.8 Browse Requests (Requests Tab)

- `TelegramRequestsView` fetches the user's purchase requests via `useTelegramMyRequests` (GET `/api/requests`).
- Displays a skeleton loader, then a scrollable list of `TelegramRequestRow` items.
- Each row shows: title, status chip, budget, creation date.
- Tap → sets `openRequestId` → renders `TelegramRequestDetailView`.

### 9.9 Request Detail with Stepper and Offers

- `TelegramRequestDetailView` fetches a single request via `useTelegramRequest`.
- Renders `TelegramRequestStepper` — a visual timeline of the escrow status flow from `pending_payment` → `completed`.
- `determineCurrentStepFromStatus` maps the current `status` to a step index.
- Also renders: budget, description, creation date, category, urgency.
- **Offer review:** fetches offers via `useTelegramOffers`; renders offer cards with seller info, price, and accept/reject actions.
- **Pay action:** renders a "Pay" button when request is in a payable state → calls `onPay(id)` → sets `openPaymentRequestId`.
- **Web fallback:** "View full details" → `openTelegramExternalLink(context.webApp, path)`.
- **Chat seller:** taps the seller chat icon → calls `onChatSeller(sellerId)` → `createConversation` + sets `openConversationId`.
- Role-aware: `role` prop is `'seller'` or `'buyer'` based on `user.role`.
- Dates formatted via `toLocaleDateString` with `fa-IR` locale for Persian.

### 9.10 Create New Request

- `TelegramNewRequestView` is a full-screen overlay (not a routed page).
- Form fields: title, description, category (fetched from `/api/categories`), budget min/max, urgency.
- Includes an **"Open Assist"** button that delegates to `handleOpenAssist()` for users who prefer the conversational LLM flow.
- On submit: calls `createPurchaseRequest()` → POST `/api/purchase-requests`.
- On success: closes overlay, switches `activeTab` to `'requests'`.

### 9.11 Chat Tab

- `TelegramChatView` shows the user's active conversations via `useTelegramConversations`.
- Includes a Support row that calls `createSupportChat()` → `POST /api/chat/support`, then opens `TelegramChatThreadView` with the returned conversation ID.
- Tap a conversation row → sets `openConversationId` → renders `TelegramChatThreadView`.
- `TelegramChatThreadView` loads messages via `useTelegramChatThread`, renders `TelegramChatBubble` items, and includes `TelegramChatComposer` for sending.
- Optimistic send: message appears immediately, confirmed/rolled back on API response.
- Real-time updates via Socket.IO events; SWR is mutated on `new-notification` and `unread-count-update` events.

### 9.12 Account Tab

**`TelegramAccountView`** (`telegram-account-view.tsx`):

**Profile header:**
- Avatar (from `user.profile.avatar`, falls back to initials), full name, Telegram `@username`, role chip (buyer / seller / admin / resolver / guard).
- Verification chips: "Telegram Verified" (if `user.telegramVerified`) and "Email Verified" (if `user.isEmailVerified`).

**Preferences section:**
- Language toggle (FA / EN, in-shell via `TelegramLanguageToggle`).
- **Settings** → opens `overlayScreen = 'settings'` (in-shell `TelegramSettingsView`).
- **Points** → opens `overlayScreen = 'points'` (in-shell `TelegramPointsView`).
- Wallet → truncated address (`0x1234…abcd`) or "not connected" → `/dashboard/account/wallet` (web via `openTelegramExternalLink`).
- Notifications → opens `TelegramNotificationsView` overlay in-shell.
- **Addresses** → opens `overlayScreen = 'addresses'` (in-shell `TelegramAddressesView`).
- Passkey → `/dashboard/account/passkey` (web).

**Help section:**
- Support → `createSupportChat()` → opens `TelegramChatThreadView` in-shell.
- Terms & Conditions → placeholder, "coming soon".

**Session section:**
- Sign Out → `TelegramBottomSheet` confirmation dialog → `authSignOut()` + `window.location.assign(paths.auth.jwt.signIn)`.

### 9.13 Settings Overlay

**`TelegramSettingsView`** (`telegram-settings-view.tsx`):
- Rendered as `overlayScreen = 'settings'`.
- Allows editing profile fields (name, bio) in-shell.
- On save: calls `onSaved()` which triggers `checkUserSession()` to refresh the auth context.

### 9.14 Addresses Overlay

**`TelegramAddressesView`** (`telegram-addresses-view.tsx`):
- Rendered as `overlayScreen = 'addresses'`.
- Fetches addresses via `use-telegram-addresses.ts`.
- Used both from the Account tab and as a mid-flow step from `TelegramCheckoutView`.

### 9.15 Points Overlay

**`TelegramPointsView`** (`telegram-points-view.tsx`):
- Rendered as `overlayScreen = 'points'`.
- Fetches user points via `use-telegram-points.ts`.
- Shows points balance and transaction history.

### 9.16 Notifications Overlay

- `TelegramNotificationsView` is rendered as `overlayScreen = 'notifications'`.
- Fetches via `useTelegramNotifications` → `getNotifications(userId, 1, 50)` → `GET /api/notifications?userId=...&page=1&limit=50`.
- Real-time updates: Socket.IO events `new-notification`, `unread-count-update` trigger SWR mutate.
- "Mark all read" calls `markAllNotificationsAsRead(userId)` → `PATCH /api/notifications/mark-all-read`.
- Unread count is also surfaced in the `TelegramHeader` bell icon badge.

---

## 10. API Calls

| Action | Hook / call | Backend endpoint |
|---|---|---|
| Auto sign-in | `useTelegramAutoSignIn` → `signInWithTelegram({initData})` | `POST /api/auth/telegram` |
| Sellers list | `useTelegramShops` → `getTemplateSellers()` | `GET /api/request-templates/sellers` |
| Seller + templates | `useTelegramSellerShop` → `getSellerWithTemplates(id)` | `GET /api/request-templates/sellers/:id` |
| Marketplace sellers | `useTelegramSellers` → `getSellers()` | `GET /api/marketplace/sellers` |
| My requests | `useTelegramMyRequests` | `GET /api/requests` |
| Single request | `useTelegramRequest` | `GET /api/purchase-requests/:id` |
| Create request | shell → `createPurchaseRequest()` | `POST /api/purchase-requests` |
| Offers for request | `useTelegramOffers` → `getOffers(requestId)` | `GET /api/marketplace/offers?requestId=...` |
| Payment options | `getPaymentOptions()` | `GET /api/payment/options` |
| Create payment intent | `createDirectBalanceIntent()` | `POST /api/payment/direct-balance` |
| Poll payment status | `checkDirectBalancePayment()` | `GET /api/payment/:id` |
| Update request status | `updateRequestStatus()` | `PATCH /api/marketplace/requests/:id/status` |
| Conversations | `useTelegramConversations` | `GET /api/chat/conversations` |
| Chat thread | `useTelegramChatThread` | `GET /api/chat/:id` + Socket.IO real-time |
| Support chat | `createSupportChat()` | `POST /api/chat/support` |
| Direct conversation | `createConversation({ type: 'direct', participantIds })` | `POST /api/chat/conversations` |
| Notifications | `useTelegramNotifications` | `GET /api/notifications?userId=...&page=1&limit=50` |
| Mark all read | `markAllNotificationsAsRead(userId)` | `PATCH /api/notifications/mark-all-read` |
| Auth sign-out | `authSignOut()` | JWT sign-out endpoint |
| Addresses | `use-telegram-addresses.ts` | `GET /api/user/addresses` |
| Points | `use-telegram-points.ts` | `GET /api/user/points` |

Cart operations (add/remove/quantity) are **pure localStorage** — no API calls until checkout.

---

## 11. Bilingual Support (EN / FA)

**Language detection priority** (`useTelegramLanguage`):

1. `?lang=` URL query param — dev preview override.
2. `localStorage` key `amn_tg_lang` — user's persisted manual selection.
3. `initDataUnsafe.user.language_code` — Telegram-reported language (`"fa"` or `"fa-IR"` → Persian).
4. Fallback → English.

**Language toggle:** `TelegramLanguageToggle` in the header — two buttons `[ EN | فا ]`. On tap: haptic light + language switch + persist to `localStorage`.

**RTL layout:**

| Element | EN (LTR) | FA (RTL) |
|---|---|---|
| Root `dir` attribute | `ltr` | `rtl` |
| Font family | IBM Plex Sans | Vazirmatn |
| Arrow icons | `→` | `←` |
| Text alignment | left | right (inherits from `dir`) |
| Chip list wrap | left-to-right | right-to-left |
| Amounts | always `dir="ltr"` | always `dir="ltr"` |

Font size bumps for Persian: body 13 px → 14 px, labels 10 px → 11 px (Vazirmatn renders optically smaller).

**Translation structure:**

```ts
// src/sections/telegram/locales/en.ts + fa.ts
const TR = {
  en: { loading, unsupported, unlinked, header, home, shop, requests,
        chat, account, newRequest, tabs, main, onboarding, errors, displayName, dir },
  fa: { /* same keys, Farsi strings, dir: 'rtl' */ },
};
```

All JSX uses `t.<section>.<key>` — no inline strings in components.

---

## 12. Design System

**File:** `src/sections/telegram/constants.ts` · `src/sections/telegram/telegram-shell-css.ts`

The Mini App has a distinct visual identity (cream/saffron Persian palette) that does not inherit from the main dashboard theme. All tokens are feature-scoped.

**Palette:** `TG_PALETTE`

| Token | Hex | Usage |
|---|---|---|
| `cream50` | `#FBF6EB` | Page background |
| `ink900` | `#1C1410` | Primary text |
| `ink600` | `#6B5D4E` | Secondary text / labels |
| `saffron600` | `#C2410C` | Primary action |
| `saffron500` | `#D97757` | Hover states |
| `pistachio700` | `#3D6B4F` | Success / released states |
| `pomegranate700` | `#8E2424` | Error / disputed states |
| `bgPage` | `#E7DFCB` | Shell outer background |

**Fonts:** `TG_FONTS` — Source Serif 4 (headings), IBM Plex Sans (body LTR), Vazirmatn (body RTL), IBM Plex Mono (amounts/addresses).

**CSS:** `buildTelegramShellCss()` injects a `<style>` tag at shell root with all class utilities (`.tg-chip`, `.tg-shell`, `.tg-tab-bar`, `.tg-header`, etc.). Theme CSS variables (`--cream-50`, `--ink-900`, etc.) are set on `.tg-shell` root. Dark mode: `.tg-shell--dark` class toggled from `themeScheme`.

**Safe area:** `getTelegramSafeAreaStyle(safeArea)` maps the Telegram-reported safe area insets to CSS padding using `max(${px}px, env(safe-area-inset-*))` to handle both Telegram-native and iOS/Android safe areas.

---

## 13. Telegram SDK Usage Patterns

### 13.1 Safe-Area Inset

```ts
// TelegramContext.safeArea = { top, right, bottom, left } (px)
// Source: webApp.contentSafeAreaInset || webApp.safe_area_insets
// Normalised to number via parseNumber() — rejects non-finite strings
const topInset = (context.safeArea?.top ?? 0) as number;
```

All views receive `topInset` / `bottomInset` props and add them as explicit `paddingTop` / `paddingBottom` to avoid content being obscured by the Telegram chrome.

### 13.2 Haptic Feedback

```ts
// useTelegramHaptic(webApp) → haptic('light' | 'medium')
webApp?.HapticFeedback?.impactOccurred?.(type)
```

Used on: tab switches (light), new-request CTA (medium), language toggle (light), back button (light), payment actions (medium). All calls are wrapped in try/catch — the API may be absent on older clients.

### 13.3 Back Button

```ts
useTelegramBackButton({ webApp, isVisible, onClick })
// Calls webApp.BackButton.show() / hide() and registers onClick handler
// Cleanup: offClick() on unmount / visibility change
```

### 13.4 Main Button

```ts
useTelegramMainButton({ webApp, isReady: false, text: '', onClick: mainButtonAction })
// isReady is always false — MainButton is intentionally kept hidden.
// The hook is retained so it can be re-enabled without structural changes.
```

### 13.5 External Links

```ts
openTelegramExternalLink(context.webApp, path)
// Uses webApp.openLink() for fully external URLs (opens browser).
// Uses window.location.href for same-origin navigation that must stay in WebView.
```

### 13.6 Theme Integration

Telegram's `themeParams` is normalised (both camelCase and snake_case accepted) and injected as CSS custom properties on the shell root. The amaneh palette overrides these for the Mini App's own UI.

---

## 14. Edge Cases

| Scenario | Detection | Handling |
|---|---|---|
| Opened in browser (not Telegram) | `context.isMiniApp === false` | `TelegramUnsupportedState` — shows "Open in Telegram" badge, web dashboard link |
| Partial Telegram signal (URL params but no SDK) | `!webApp && Boolean(startParam)` → `isUnsupported: true` | Same unsupported state |
| Telegram SDK injected late | `useTelegramLiveContext` polls at 0/100/500/1000 ms | Re-probes until SDK is ready; seed context bypasses polling |
| `initData` absent (no auth data) | `!context.initData` in unlinked state | Sign-in button triggers error string `t.errors.no_init_data`; email/create buttons remain available |
| Auto sign-in replay (React Strict Mode) | `attemptedInitDataRef.current === context.initData` | Deduplication ref — second effect is a no-op |
| Backend sign-in failure | Catch block in `useTelegramAutoSignIn` | Error string displayed in `TelegramUnlinkedState`; retry via "Continue with Telegram" |
| New user first login | `result.isNewUser === true` | `TelegramOnboardingSheet` shown over the shell; dismissed to account settings or "Later" |
| Expired session inside Mini App | Auth context `user === null` after session check | Shell falls back to `unlinked` state |
| Old Telegram client (< 6.1) | `setParams` throws | Try/catch silences it; button shows without saffron colour |
| RTL + keyboard overlap | Viewport shrinks on soft keyboard open | `flex: 1` + `overflowY: auto` on content area; bottom safe-area inset on tab bar |
| Persian locale date formatting | `lang === 'fa'` | `toLocaleDateString('fa-IR', ...)` in `formatDate` helper |
| Cart cross-tab sync | Multiple tabs / Mini App + web | `tg-cart-changed` DOM event + `storage` event both trigger re-render |
| Template at capacity | `remainingCapacity === 0` at checkout | Stock validation clamps/removes over-capacity items before payment |
| Payment from shop checkout | `paymentCheckoutFlow === true` | BackButton steps back to cart; progress header shows 3-step flow |
| Display name resolution | User may have no name set in DB | Falls back to Telegram profile name (`first_name` / `last_name`), then generic label |
| Seller chat from request detail | `onChatSeller(sellerId)` | `createConversation({ type: 'direct', participantIds: [sellerId] })` → opens chat thread in-shell |
| Assist hand-off on iOS | `webApp.openLink()` opens Safari | `window.location.href` used instead to keep navigation in the Telegram WebView |

---

## 15. File Map

```
src/
  app/telegram/page.tsx                        # Next.js route (thin shell, no auth guard)
  utils/telegram-webapp.ts                     # SDK probe, context types, shell style helpers
  sections/telegram/
    constants.ts                               # TG_PALETTE, TG_FONTS, TG_EASE, status maps
    telegram-shell-css.ts                      # buildTelegramShellCss() — inlined CSS blob
    avatar-url.ts                              # avatar URL helper
    index.ts                                   # barrel
    locales/
      types.ts                                 # TelegramDict, TelegramLang, TelegramTabId
      en.ts                                    # English strings
      fa.ts                                    # Persian strings
      index.ts                                 # getTelegramDict(lang)
    hooks/
      use-telegram-live-context.ts             # SDK polling
      use-telegram-language.ts                 # EN/FA detection + ?lang= + localStorage persist
      use-telegram-auto-sign-in.ts             # initData → JWT exchange
      use-telegram-main-button.ts              # MainButton lifecycle (kept, isReady=false)
      use-telegram-back-button.ts              # BackButton lifecycle
      use-telegram-haptic.ts                   # HapticFeedback wrapper
      use-telegram-cart.ts                     # localStorage cart (shared with web checkout)
      use-telegram-theme.ts                    # dark/light theme detection
      use-telegram-realtime.ts                 # shared Socket.IO real-time helper
      use-telegram-shops.ts                    # GET /api/request-templates/sellers
      use-telegram-seller-shop.ts              # GET /api/request-templates/sellers/:id
      use-telegram-sellers.ts                  # GET /api/marketplace/sellers
      use-telegram-my-requests.ts              # GET /api/requests
      use-telegram-request.ts                  # GET /api/purchase-requests/:id
      use-telegram-offers.ts                   # GET /api/marketplace/offers?requestId=...
      use-telegram-conversations.ts            # Chat conversation list
      use-telegram-chat-thread.ts              # Chat thread + optimistic send
      use-telegram-notifications.ts            # GET /api/notifications
      use-telegram-addresses.ts               # GET /api/user/addresses
      use-telegram-points.ts                   # GET /api/user/points
      index.ts
    view/
      telegram-mini-app-view.tsx               # Shell orchestrator (all state lives here)
      telegram-home-view.tsx                   # Home tab
      telegram-shop-view.tsx                   # Shop tab — sellers list
      telegram-seller-shop-view.tsx            # Seller store drill-down + cart actions
      telegram-template-detail-view.tsx        # Template full detail + cart/order actions
      telegram-cart-view.tsx                   # Cart overlay
      telegram-checkout-view.tsx               # In-shell 3-step checkout overlay
      telegram-payment-view.tsx                # In-shell payment drilldown
      telegram-requests-view.tsx               # Requests list tab
      telegram-request-detail-view.tsx         # Request drilldown + stepper + offers
      telegram-new-request-view.tsx            # New request overlay form + Assist CTA
      telegram-chat-view.tsx                   # Chat conversation list tab
      telegram-chat-thread-view.tsx            # Chat thread drilldown
      telegram-archived-chats-view.tsx         # Archived conversations
      telegram-account-view.tsx                # Account + preferences + sign-out tab
      telegram-notifications-view.tsx          # Notifications overlay
      telegram-settings-view.tsx               # In-shell profile/settings overlay
      telegram-addresses-view.tsx              # In-shell address management overlay
      telegram-points-view.tsx                 # In-shell points/loyalty overlay
      index.ts
    components/
      telegram-header.tsx                      # AMN logo + subtitle + language toggle + bell
      telegram-tab-bar.tsx                     # Bottom tab bar (5 tabs)
      telegram-welcome-banner.tsx              # Home: escrow account banner + CTA
      telegram-quick-actions.tsx               # Home: action cards (Requests / Payments / Chat)
      telegram-escrow-state-chips.tsx          # Home: status chip legend
      telegram-shop-row.tsx                    # Shop: seller list row
      telegram-request-row.tsx                 # Requests: list row
      telegram-request-stepper.tsx             # Detail: visual escrow timeline
      telegram-list-row.tsx                    # Generic list row primitive
      telegram-list-skeleton.tsx               # Skeleton loader for lists
      telegram-list-controls.tsx               # List sort/filter controls
      telegram-chat-row.tsx                    # Chat: conversation list row
      telegram-chat-bubble.tsx                 # Chat: message bubble
      telegram-chat-composer.tsx               # Chat: message input
      telegram-review-prompt.tsx               # Post-transaction review prompt
      telegram-loading-state.tsx               # Loading spinner state
      telegram-unlinked-state.tsx              # Unlinked / sign-in prompt state
      telegram-unsupported-state.tsx           # Not-in-Telegram fallback state
      telegram-onboarding-sheet.tsx            # New-user onboarding bottom sheet
      telegram-empty-state.tsx                 # Generic empty list state
      telegram-language-toggle.tsx             # EN | FA header toggle
      telegram-theme-toggle.tsx                # Dark / light theme toggle
      telegram-bottom-sheet.tsx                # Generic bottom sheet primitive
      telegram-form-field.tsx                  # Form field + input style helper
      telegram-cart-fab.tsx                    # Floating cart badge button
      telegram-support-fab.tsx                 # Floating support chat button
      telegram-seal-mark.tsx                   # SealMark logo component
      telegram-icons.tsx                       # Telegram-scoped icon set
      index.ts
```

---

## 16. Current Implementation Status

| Area | Status | Notes |
|---|---|---|
| Shell + state machine | Done | `TelegramMiniAppView` — all states wired |
| SDK probe + live context | Done | Polling + hashchange listener |
| Auto sign-in | Done | Deduped initData exchange |
| Manual sign-in (unlinked) | Done | Email + create account fallbacks |
| Bilingual EN/FA | Done | Full string inventory, RTL layout, Vazirmatn font |
| Language toggle | Done | Header toggle + localStorage persist |
| `?lang=` dev preview param | Done | URL param override |
| Dark mode | Done | `.tg-shell--dark` class, `use-telegram-theme` |
| Home tab | Done | Banner + quick actions + state chips + Assist CTA |
| Shop tab — sellers list | Done | API-backed with skeleton + empty states, cart FAB |
| Shop tab — seller store | Done | Templates list, add/remove cart, template detail drilldown |
| Template detail drilldown | Done | Full detail, cart/order actions |
| Shopping cart (localStorage) | Done | Shared key with web checkout; cross-tab sync |
| Cart overlay | Done | Quantity controls, remove, total, in-shell checkout CTA |
| In-shell checkout | Done | 3-step cart→address→payment; replaces web handoff |
| In-shell payment view | Done | Direct balance intent + polling; checkout-flow back-nav |
| Requests list | Done | API-backed with skeleton + empty states |
| Request detail + stepper | Done | Status timeline, budget, dates with fa-IR locale |
| Offer review in request detail | Done | Offers fetched via `useTelegramOffers`; accept/reject |
| New request form | Done | In-shell overlay, category fetch, validation, Assist CTA |
| Chat list | Done | API-backed conversation list + support row |
| Chat thread | Done | Messages + optimistic send + Socket.IO real-time |
| Direct seller chat | Done | `createConversation` from request detail |
| Account tab | Done | Profile, preferences, help, sign-out |
| Settings overlay | Done | In-shell profile editing |
| Addresses overlay | Done | In-shell address management; reachable from checkout |
| Points overlay | Done | In-shell points/loyalty |
| Notifications overlay | Done | API-backed; Socket.IO real-time; mark-all-read |
| Notifications unread badge | Done | Bell icon in header |
| Telegram chrome (BackButton) | Done | Full back-stack with checkout flow awareness |
| Telegram MainButton | Disabled | Intentionally hidden (`isReady: false`); hook retained |
| Haptic feedback | Done | All tap interactions |
| Safe area insets | Done | Normalised from SDK + CSS env() fallback |
| amanat-assist integration | Done | "Open Assist" CTA in Home + New Request; window.location hand-off with access_token |
| Deep link `startapp` routing | Partial | `startParam` parsed; auto-navigation to specific request not yet wired |
| Backend room-scoped Socket.IO | Partial | Global socket broadcast fixed client-side (v2.8.4); server-side room scoping is a follow-up |
| Client matrix QA (iOS/Android/Desktop) | Pending | Needs cross-platform testing pass |

### Open Items

1. **`startapp` deep link routing:** if `context.startParam` matches `req_<id>`, auto-open `TelegramRequestDetailView` on first render.
2. **Backend room-scoped Socket.IO:** server-side scoping for real-time chat updates (follow-up from client-side fix in v2.8.4).
3. **Client matrix QA:** iOS Telegram, Android Telegram, Telegram Desktop, and web clients all need a full feature pass.
4. **Review prompt:** `TelegramReviewPrompt` component exists but integration point (post-payment / post-delivery) is TBD.
5. **Archived chats:** `TelegramArchivedChatsView` exists but is not yet surfaced in the navigation.

---

## 17. Related Documents

- [[amanat-assist]] — the separate AI-driven Mini App for LLM-assisted request creation
- [[PRD - Telegram Mini App Bilingual (EN + FA)]] — bilingual string inventory and RTL layout spec
- [[PRD - Telegram Phone Number Authentication]] — phone-number auth as a future sign-in path
- [[Authentication Flow]] — JWT lifecycle shared with the Mini App auth
- [[Purchase Request Flow]] — escrow state machine surfaced in the stepper
- [[Chat Flow]] — real-time messaging that the Mini App embeds
- [[Request Template Checkout]] — web checkout flow; the Mini App now has its own in-shell checkout, but the localStorage cart key is shared
