Zernio
Zernio
Overview

Social Posting

XInstagramFacebookLinkedInTikTokYouTubePinterestRedditBlueskyThreadsGoogle Business ProfileSnapchat

Messaging

WhatsAppTelegramDiscordSlack

Telephony

Phone NumbersVoice & CallsSMS

Advertising

Meta AdsGoogle AdsLinkedIn AdsTikTok AdsPinterest AdsX AdsOpenAI Ads

Commerce

Shopify
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources

Telegram

Publish text, photos, videos, documents and albums to Telegram channels and groups with the Zernio API, edit published messages, and run DMs and bot commands from the inbox.


Publish text, photos, videos, documents and albums to a Telegram channel or group with POST /v1/posts and platform: "telegram". The same bot serves DMs and bot commands in the inbox.

Quick reference

PropertyValue
Text limit4,096 characters (text messages)
Caption limit1,024 characters (media captions)
Images per album10
Videos per album10
Mixed mediaYes (images and videos in the same album)
Image formatsJPEG, PNG, GIF, WebP
Image max size10 MB (auto-compressed)
Video formatsMP4, MOV
Video max size50 MB (auto-compressed)
SchedulingYes
Editing published postsYes (text or caption, media cannot be changed)
Inbox (DMs)Yes
Inbox (comments)No
AnalyticsNo (Telegram limitation)

Before you start

Telegram requires Zernio's bot, @ZernioScheduleBot, to be an administrator of your channel or group with permission to post. There is no OAuth and there are no scopes: what the bot can do comes from the admin permissions you grant it. In a channel, posts appear as the channel's name and logo. In a group, posts appear as sent by "ZernioScheduleBot", and that cannot be changed.

Add @ZernioScheduleBot as an administrator with the Post Messages permission before you connect. A bot that is a plain member, or an admin without post permission, is the most common setup failure: the connect call returns 400 and every post fails with "Bot is not a member of the channel".

Connect

Call GET /v1/connect/telegram with profileId to generate an access code, valid for 15 minutes. The user adds the bot to the channel or group and sends it the code; you poll until the connection completes. The connecting accounts guide covers where this fits next to the OAuth platforms.

Access code flow

import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: access } = await zernio.connect.telegram.getTelegramConnectStatus({
  query: { profileId: '66a1f0c2a4b9d3e8f1a2b3c4' }
});

console.log(access.code, access.botUsername);

Response (200):

{
  "code": "ZRN-ABC123",
  "expiresAt": "2027-01-01T12:15:00.000Z",
  "expiresIn": 900,
  "botUsername": "ZernioScheduleBot",
  "instructions": [
    "1. Add @ZernioScheduleBot as an administrator in your channel/group",
    "2. Open a private chat with @ZernioScheduleBot",
    "3. Send: ZRN-ABC123 @yourchannel (replace @yourchannel with your channel username)",
    "4. Wait for confirmation - the connection will appear in your dashboard",
    "Tip: If your channel has no public username, forward a message from it along with the code"
  ]
}

The user then does 3 things in Telegram:

  1. Adds @ZernioScheduleBot to the channel as an administrator with the Post Messages permission, or to the group as an administrator.
  2. Opens a private chat with @ZernioScheduleBot.
  3. Sends the code with the channel username: ZRN-ABC123 @yourchannel. For a private channel without a username, they forward any message from the channel to the bot together with the code.

Poll PATCH /v1/connect/telegram with the code until status is connected:

const { data: status } = await zernio.connect.telegram.completeTelegramConnect({
  query: { code: access.code }
});

console.log(status.status);

Response (200) while the user has not sent the code yet:

{
  "status": "pending",
  "expiresAt": "2027-01-01T12:15:00.000Z",
  "expiresIn": 542
}

Response (200) once the bot has received it:

{
  "status": "connected",
  "chatId": "-1001234567890",
  "chatTitle": "My Channel",
  "chatType": "channel",
  "account": {
    "_id": "66b2e19d8c3f5a7e9d0b1c2d",
    "platform": "telegram",
    "username": "mychannel",
    "displayName": "My Channel"
  }
}

account._id is the accountId for every call below. A code that is not used within 15 minutes returns status: "expired"; generate a new one.

Direct connection

If the bot is already an administrator and you know the chat id, skip the code and connect directly with POST /v1/connect/telegram:

const { data: connected } = await zernio.connect.telegram.initiateTelegramConnect({
  body: {
    profileId: '66a1f0c2a4b9d3e8f1a2b3c4',
    chatId: '-1001234567890'
  }
});

console.log(connected.account._id);

Response (200):

{
  "message": "Telegram channel connected successfully",
  "account": {
    "_id": "66b2e19d8c3f5a7e9d0b1c2d",
    "platform": "telegram",
    "username": "mychannel",
    "displayName": "My Channel",
    "isActive": true,
    "chatType": "channel"
  }
}

chatId is the username with an @ prefix for a public channel (@mychannel) or the numeric id for a private channel or a group. To find a numeric id, forward a message from the channel to @userinfobot, which replies with the id (private channel ids start with -100), or add that bot to the group for a moment and remove it once it has shown the group's negative id.

Publish

A plain post becomes a text message. One media item becomes a photo, video or document message with content as its caption, and 2 to 10 items become an album with the caption on the first message. Text is parsed as HTML unless parseMode says otherwise.

Text message

const { data: published } = await zernio.posts.createPost({
  body: {
    content: '<b>Release 2.3 is out.</b>\n\nRead the <a href="https://example.com/changelog">changelog</a>.',
    platforms: [
      {
        platform: 'telegram',
        accountId: '66b2e19d8c3f5a7e9d0b1c2d',
        platformSpecificData: { parseMode: 'HTML' }
      }
    ],
    publishNow: true
  }
});

console.log(published.post.platforms[0].platformPostUrl);

Response (201):

{
  "post": {
    "_id": "65f1c0a9e2b5af0012ab34cd",
    "status": "published",
    "platforms": [
      {
        "platform": "telegram",
        "status": "published",
        "platformPostUrl": "https://t.me/mychannel/42"
      }
    ]
  }
}

Every sample below changes only the mediaItems or the platforms entry of this request.

Photo message

A single image with content as the caption (1,024 characters):

"mediaItems": [
  { "type": "image", "url": "https://cdn.example.com/launch.jpg" }
]

Video message

A single video with content as the caption:

"mediaItems": [
  { "type": "video", "url": "https://cdn.example.com/demo.mp4" }
]

Document message

Any file type, sent as a document:

"mediaItems": [
  { "type": "document", "url": "https://cdn.example.com/report.pdf" }
]

Album

Up to 10 items, images and videos mixed, sent as one album:

"mediaItems": [
  { "type": "image", "url": "https://cdn.example.com/photo1.jpg" },
  { "type": "image", "url": "https://cdn.example.com/photo2.jpg" },
  { "type": "video", "url": "https://cdn.example.com/clip.mp4" },
  { "type": "image", "url": "https://cdn.example.com/photo3.jpg" }
]

Formatting

parseMode selects how Telegram reads content. HTML is the default:

<b>bold</b>
<i>italic</i>
<u>underline</u>
<s>strikethrough</s>
<code>inline code</code>
<pre>code block</pre>
<a href="https://example.com">link</a>

"Markdown" supports *bold*, _italic_, [link](https://example.com) and backtick-wrapped inline code. "MarkdownV2" adds __underline__, ~strikethrough~ and ||spoiler||, and needs a backslash before every one of these characters:

_ * [ ] ( ) ~ ` > # + - = | { } . !

An unescaped character fails the post with "Can't parse entities".

Silent and protected messages

disableNotification delivers the message without a notification sound, protectContent stops recipients from forwarding or saving it, and disableWebPagePreview suppresses the link preview:

{
  "platform": "telegram",
  "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
  "platformSpecificData": {
    "parseMode": "HTML",
    "disableWebPagePreview": true,
    "disableNotification": true,
    "protectContent": true
  }
}

Channels and groups

DestinationShown as the author
ChannelThe channel's name and logo
GroupZernioScheduleBot

Edit a published message

Edit post replaces the text of a published Telegram message. Messages stay editable with no time window and no limit on the number of edits, and the message id does not change. What content replaces depends on the original post:

Original postWhat content replacesLimit
Text onlyThe message text4,096 characters
Single photo, video or documentThe caption1,024 characters
AlbumThe caption on the album (its first message)1,024 characters

The media itself cannot be swapped, added or removed, and an edit with unchanged content is rejected with 400. Check which limit applies before sending: a 2,000-character edit fits a text message and is too long for a photo caption.

const { data: edited } = await zernio.posts.editPost({
  path: { postId: published.post._id },
  body: {
    platform: 'telegram',
    content: '<b>Updated.</b> The webinar starts at 18:00 CET.'
  }
});

console.log(edited.id, edited.url);

Response (200):

{
  "success": true,
  "id": "42",
  "url": "https://t.me/mychannel/42"
}

If the post went to several Telegram channels or groups, pass accountId to pick which copy to edit. Without it, the first telegram entry on the post is edited.

Platform fields

All fields go in platformSpecificData on the Telegram entry.

FieldTypeDefaultDescription
parseMode"HTML", "Markdown", "MarkdownV2""HTML"How Telegram parses content. See Formatting.
disableWebPagePreviewbooleanfalseNo link preview for URLs in the message.
disableNotificationbooleanfalseDeliver silently: recipients get the message without a notification sound.
protectContentbooleanfalseRecipients cannot forward or save the message.

Media requirements

Files above these limits are compressed automatically: images above 10 MB and videos above 50 MB, which are Telegram's own Bot API caps. Compression has a ceiling of its own: a video over 200 MB is passed through untouched, and Telegram then refuses it.

Images

PropertyRequirement
Max per album10
FormatsJPEG, PNG, GIF, WebP
Max file size10 MB (auto-compressed)
Max resolutionWidth plus height must total 10,000 px or less; a larger image is downscaled

Videos

PropertyRequirement
Max per album10
FormatsMP4, MOV
Max file size50 MB (auto-compressed)
Max durationNo limit
CodecH.264 recommended

Media URLs must be direct HTTPS URLs that are publicly accessible with no redirects; Telegram rejects HTTP URLs and redirecting links with "Wrong file identifier/HTTP URL specified". Upload files through the media endpoint to get a URL that qualifies.

Analytics

Telegram's Bot API exposes no analytics, so there are none for Telegram accounts. View counts for channel posts are visible in the Telegram app, and channels with 500 or more subscribers get Telegram's own channel statistics there.

Inbox

Telegram supports DMs with attachments, inline and reply keyboards, and a bot command menu. There are no comments.

Direct messages

FeatureSupported
List conversations
Fetch messages
Send text messages
Send attachments (images, videos, documents)
Edit messages (text and inline keyboard)
Inline keyboards (buttons with callback data or URLs)
Reply keyboards (one-time custom keyboards)
Reply to a message (replyTo with the message id)
Archive and unarchive

Attachments:

TypeMax size
Images10 MB
Videos50 MB
Documents50 MB

When a user taps an inline keyboard button, the callback data arrives on the incoming message in metadata.callbackData. The Messages API has every endpoint.

Bot commands

Bot commands are the entries in the "/" menu users see when they chat with the bot. Manage them with GET, PUT and DELETE /v1/accounts/{accountId}/telegram-commands (Account settings): up to 100 commands, each a lowercase name of at most 32 characters (a-z, 0-9, _, no leading slash) with a description of at most 256 characters.

Webhooks

EventWhen it fires
message.receivedNew incoming message to the bot
message.sentOutgoing message is sent
message.editedThe user edits a sent message; also fires for edited_channel_post in channels where the bot is an administrator
reaction.receivedA participant adds or removes an emoji reaction. The bot must be an administrator of the chat; reactions in private chats are not delivered

Messages are stored locally as webhooks arrive; the webhooks page has the payloads. Telegram's Bot API exposes no deletion or read-receipt events for regular bot chats. Delivery and read tracking exist only in the separate Telegram Business integration, which Zernio does not use.

What you cannot do

Telegram's Bot API does not expose:

  • Polls or quizzes
  • Telegram's own scheduled messages (schedule with scheduledFor instead)
  • Replacing the media on a published post (only the text or caption can be edited)
  • Managing channel administrators
  • Message analytics
  • Pinning messages
  • Channel invite links

Common errors

ErrorCauseFix
"Bot is not a member of the channel"@ZernioScheduleBot is not in the channel or group, or is not an administratorAdd the bot as an administrator and grant the Post Messages permission.
"Message is too long"Text over 4,096 characters, or a caption over 1,024Shorten the content or split it into several messages.
"Wrong file identifier/HTTP URL specified"The media URL is not reachable, uses HTTP, or redirectsUse a direct, public HTTPS URL with no redirects.
"Can't parse entities"Broken HTML or Markdown, or an unescaped MarkdownV2 characterClose every tag in HTML mode; escape the reserved characters in MarkdownV2.
Media not displayedAn unsupported format, or a video over 200 MB, which is too large for Zernio to compress under Telegram's 50 MB capCheck the format; compress a video over 200 MB before you send it.
"Access code expired"The code was not used within 15 minutesGenerate a new code with GET /v1/connect/telegram.

A publishNow: true post that Telegram rejects returns 207 with post.status: "failed" and the message in platforms[].errorMessage:

{
  "message": "Post created but publishing failed",
  "error": "All platforms failed",
  "post": {
    "_id": "65f1c0a9e2b5af0012ab34cd",
    "status": "failed",
    "platforms": [
      {
        "platform": "telegram",
        "status": "failed",
        "errorMessage": "Bot is not a member of the channel"
      }
    ]
  }
}

207 is a 2xx status, so fetch(...).ok is true; branch on the status code and on post.status. Make the bot an administrator with post permission, then retry. Error handling covers the envelope.

Related

  • Connecting accounts: the access code flow next to the OAuth platforms.
  • Create post: every field of the request.
  • Edit post: edit the text or caption of a published message.
  • Media uploads: upload images and videos instead of hosting them.
  • Messages and Account settings: the inbox API and bot commands.
  • Pricing: what the inbox and outbound messages cost.
Was this page helpful?

Media & Limits

WhatsApp media formats and size limits, what the API does not expose, and the Meta error codes you will meet, each with its fix.

Discord

Publish messages, embeds, polls, forum posts and threads to Discord channels with the Zernio API, send DMs, manage roles and pins, and create scheduled events, with no bot hosting.

On this page

Quick referenceBefore you startConnectAccess code flowDirect connectionPublishText messagePhoto messageVideo messageDocument messageAlbumFormattingSilent and protected messagesChannels and groupsEdit a published messagePlatform fieldsMedia requirementsImagesVideosAnalyticsInboxDirect messagesBot commandsWebhooksWhat you cannot doCommon errorsRelated