# Telegram, Zalo and Lark notifications

Besides [webhooks](/en/docs/tinopay-webhook), TinoPay posts a notification for every transaction to Telegram, Zalo or Lark. Each notification app is tied to one bank account and filters by virtual account like a webhook. The routes are in the Notifications group of the [API Reference](/en/docs/reference#tinopay); calling, authentication and error codes are in [Getting started with TinoPay](/en/docs/tinopay-start).

Updated: 2026-10-25. Sources: Tino's TinoPay module and the TinoPay API v1 documentation.

## App types

| `type` | Bot | `endpoint` (where it posts) |
| --- | --- | --- |
| `telegram` | the shared bot **@allpay_telebot** by default; send `bot_token` to use your own bot | shared bot: a group ID starting with `-` (add @allpay_telebot to the group); own bot: a group ID (`-…`) or a private chat (positive number) |
| `zalo` | your own bot is required (`bot_token` from Zalo Bot Creator) | the Zalo `chat_id` of a private chat or a group (groups start with `zgr-`) |
| `lark` | a Lark bot webhook URL, no `bot_token` | `https://open.larksuite.com/open-apis/bot/v2/hook/…` (or `open.feishu.cn`) |

`bot_token` is never returned: lists carry only `bot_token_masked`. In a UI, make the token field a password field and do not refill it when editing.

## Adding an app (own bot)

1. [Check the bot token](/en/docs/api/tinopay-post-notification-apps-bot-info): `{type, bot_token}` → `{valid, bot_name, error}`. Show the bot name; when `valid` is `false`, show `error`.
2. [Find a chat](/en/docs/api/tinopay-post-notification-apps-find-chats): `{type, bot_token}` → `{chats: [{id, title, type}], error}`; the user picks one, and its `id` becomes the `endpoint`.
3. [Send a test message](/en/docs/api/tinopay-post-notification-apps-test): `{type, endpoint, bot_token}` sends a real message → `{connected, chat_title}`. Allow saving only when `connected` is `true`; Telegram returns `chat_title` to prefill the name.
4. [Create the app](/en/docs/api/tinopay-post-notification-apps): `type`, `name` (up to 250 characters), `endpoint`, `bot_token`, `bank_account_id`, `virtual_accounts` (empty means every virtual account), `status` (`active` by default, `inactive`) → 201. `type` cannot change after creation.

Step 2 by type:

- Telegram returns right away the groups and people that messaged the bot in the last 24 hours. A non-null `error` (the bot is attached to another system's webhook): let the user enter the ID by hand.
- Zalo keeps no old messages: the request waits up to about 20 seconds and only catches messages sent to the bot meanwhile (groups: mention the bot). Tell the user to message the bot now, and set the HTTP timeout to at least 30 seconds. When nothing arrives, `chats` is empty: let them retry or enter the ID by hand.

Shared Telegram bot: skip steps 1 and 2; enter the group ID, and step 3 sends no `bot_token`. Lark: only steps 3 and 4, with the webhook URL as `endpoint`.

Steps 2 and 3 are not retried on network errors (a retry would multiply the wait or send the test message twice): let the user try again.

## Editing an app

- When editing, steps 1–3 send `app_id` instead of `bot_token` to use the stored token; `app_id` must be an app of this merchant, otherwise 404 `RESOURCE_NOT_FOUND`.
- [Update the app](/en/docs/api/tinopay-patch-notification-apps-app): send only the fields to change, never `type`.

| `bot_token` when editing | Result |
| --- | --- |
| not sent | keep the stored token |
| a new string | replace the token |
| `null` | Telegram goes back to the shared bot (`endpoint` must be a group ID `-…`); Zalo cannot drop its token |

A `bank_account_id` that does not belong to the merchant: 404 `BANK_ACCOUNT_NOT_FOUND`.

## Watching delivery errors

The [app list](/en/docs/api/tinopay-get-notification-apps) carries `last_error` and `last_error_at`: the error of the latest delivery (token revoked, bot removed from the group…). Show a "connection error" warning with `last_error`; the next successful delivery resets it to `null`. A suspended merchant sends no notifications, as with webhooks.
