# HMAC inbound (Passthrough + Managed REMOTE)

The Telegram service POSTs to your backend. Verify every call.

## Headers

- `X-Telegram-App-Id` — numeric App id
- `X-Telegram-Timestamp` — Unix milliseconds
- `X-Telegram-Signature` — lowercase hex HMAC-SHA256

Canonical string:

```
timestamp + "." + rawBody
```

Signature = HMAC-SHA256(App backend secret, canonical string).

Reject if the signature does not match.

## Passthrough — you implement `POST {backendBaseUrl}{inboundPath}`

Default path: `/telegram/inbound`.

Example inbound body:

```json
{
  "triggerType": "COMMAND",
  "triggerKey": "start",
  "text": "/start",
  "params": {},
  "chatId": "555",
  "messageId": 42,
  "callbackQueryId": null,
  "userId": 999,
  "username": "alice",
  "chatType": "private",
  "correlationId": "msg:42:555"
}
```

`triggerType`: `COMMAND` | `CALLBACK` | `TEXT`.

Your response (one JSON):

```json
{
  "mode": "MESSAGE",
  "content": "<b>Confirm</b> Plus 20",
  "image": "data:image/png;base64,iVBOR...",
  "buttons": [
    { "name": "Pay", "key": "buy", "param": { "packageId": 1 } }
  ]
}
```

`mode`: `MESSAGE` (default), `EDIT_MESSAGE`, `TOAST`, `ALERT`. Empty body or HTTP 204 = no Telegram message.

Use **`MESSAGE`** (not edit) when you send a new photo.

On button tap, the next POST has `triggerType=CALLBACK`, `triggerKey` = button `key`, `params` = button `param`.

## Images (Passthrough)

Put the picture on **`image`** (aliases: `photo`, `photoUrl`). Caption is `content`. Either form is valid:

- **Link:** `"image": "https://cdn.example.com/card.png"` — Telegram downloads the URL (it must be reachable from the internet).
- **Base64:** `"image": "data:image/png;base64,iVBOR..."` — or a long raw base64 string with no prefix. jpeg / gif / webp data URLs work too.

This sample always sends **base64** on Confirm / Paid. Decoded size must stay under 8 MB. Use `mode: MESSAGE` when sending a new photo.

## Managed REMOTE — you implement each route `backendPath`

Same HMAC headers. Response example:

```json
{
  "mode": "MESSAGE",
  "vars": {
    "userName": "alice",
    "packageName": "Plus 20",
    "price": "20",
    "units": 220,
    "orderId": 15,
    "historyText": "#15 Plus 20 20 USDT (PAID)"
  }
}
```

Optional `templateId` in the JSON selects a template; otherwise the route default template is used. Placeholders in the template are `{{userName}}` etc.
