# Telegram API: Posting Reference

OmniSocials posts to a Telegram **channel** or **group** through the OmniSocials bot. The workspace connects the chat once in the OmniSocials dashboard (Channels, then Add a channel, then Telegram): the person opens the bot in Telegram, picks the channel or group, and Telegram adds the bot as an admin that can post. OmniSocials checks that the person who connects is an admin of that chat. Each workspace connects one Telegram chat.

In a channel, a post shows as the channel itself. In a group, a post shows as the OmniSocials bot.

**Channel ID:** `telegram`

## Supported content types

| Type | Supported |
|------|-----------|
| Text post | ✅ |
| Photo, video, GIF or file | ✅ |
| Album (2 to 10 photos and videos) | ✅ |
| Native poll | ✅ |
| Reel | ✅ (sent as a video message) |
| Story | - |

## Minimal example

```bash
curl -X POST https://api.omnisocials.com/v1/posts/create \
  -H "Authorization: Bearer $OMNISOCIALS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": { "default": "The new collection is live." },
    "channels": ["telegram"],
    "media_urls": ["https://example.com/collection.jpg"],
    "telegram": { "silent": true, "pin": true }
  }'
```

## Platform-specific options

| Field | Type | Description |
|-------|------|-------------|
| `telegram.silent` | boolean | Send without a notification sound. |
| `telegram.link_preview` | boolean | `false` hides the link preview of a text-only post. The preview shows by default. |
| `telegram.pin` | boolean | Pin the post in the chat after it is sent. See [Pinning](#pinning). |
| `telegram.protect_content` | boolean | Stop forwarding and saving of the post in Telegram apps. |
| `telegram.spoiler` | boolean | Blur the photos and videos until someone taps them. |
| `telegram.caption_above_media` | boolean | Show the text above the media instead of below. |
| `telegram.buttons` | array | Up to 3 link buttons under the post. See [Buttons](#buttons). |
| `telegram.poll` | object | A native Telegram poll; the post text is the question. See [Polls](#polls). |
| `telegram.first_comment` | string | A reply the bot sends under the post right after it is published (max 4,096 characters). |

On `PATCH /posts/:id`, every key merges into the stored options, and a key set to `null` removes it (for example `"telegram": { "poll": null }`).

## Buttons

```json
{
  "channels": ["telegram"],
  "content": { "telegram": "Early access opens today." },
  "media_urls": ["https://example.com/launch.jpg"],
  "telegram": {
    "buttons": [
      { "text": "Get early access", "url": "https://example.com/early" },
      { "text": "Read the story", "url": "https://example.com/blog/launch" }
    ]
  }
}
```

**Rules:**

- At most 3 buttons. Each button has its own row.
- `text` is 1 to 64 characters. `url` starts with `https://`, `http://` or `tg://`.
- Telegram shows no buttons under an album (2 or more media) or a poll. A request with buttons on an album or a poll is refused with `400 validation_error`.

## Polls

```json
{
  "channels": ["telegram"],
  "content": { "telegram": "Which day suits the live Q&A?" },
  "telegram": {
    "poll": {
      "options": ["Tuesday", "Thursday", "Saturday"],
      "open_period": 86400,
      "multiple": false
    }
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `options` | array of strings | 2 to 12 answers of up to 100 characters. The answers must differ from each other. |
| `open_period` | integer | Seconds the poll stays open, 5 to 2628000 (about 30 days). Leave it out and the poll stays open until someone closes it in Telegram. |
| `multiple` | boolean | Let people pick several answers (default `false`). |

The question is the post text, max 300 characters. A poll carries no media and no buttons: a request with both is refused with `400 validation_error`. The poll is anonymous, the Telegram default for channels. The votes come back in analytics under `metrics.poll`, updated while people vote.

## Pinning

`telegram.pin: true` pins the post after it is sent. The bot needs the **Edit messages** right in a channel, or the **Pin messages** right in a group. OmniSocials asks for these rights when the chat is connected. When the bot cannot pin, the post is still sent: `GET /posts/:id` then returns `telegram.result.pinned: false` with `pin_error: "no_right"`.

## Long text with media

A message holds up to 4,096 characters, but Telegram limits the text under media to 1,024 characters. When a post with media has a longer text, OmniSocials sends the media first and the full text as a reply under it. Buttons then go on the text message. A text-only post can use all 4,096 characters.

## Media requirements

| Media | Requirement |
|-------|-------------|
| Photo | JPEG and PNG go as they are; other formats such as WebP are sent as JPEG. Telegram allows 10 MB and a width plus height of 10,000 pixels; OmniSocials resizes a larger photo (up to 40 MB) to fit. A photo wider or taller than 20:1 is sent as a file. |
| Video | MP4 (H.264), max 50 MB. No duration cap. |
| GIF | Max 50 MB. A GIF goes on its own, never in an album. |
| File (PDF and similar) | Max 50 MB. Files do not share an album with photos or videos. |

An album holds 2 to 10 photos and videos, which can be mixed. OmniSocials uploads every file to Telegram from its own servers, so the 5 MB and 20 MB limits of Telegram's URL upload do not apply.

## Character limit

4,096 characters per message, counted in UTF-16 code units the way Telegram counts them: most emoji count as 2. With media, see [Long text with media](#long-text-with-media). A poll question allows 300 characters.

## Published post

`GET /posts/:id` returns under `telegram.result`:

| Field | Description |
|-------|-------------|
| `chat_id` | The Telegram chat the post went to. |
| `message_ids` | The ids of the sent messages (one per album item). |
| `text_message_id` | The reply that holds the long text, when the text went separately. |
| `pinned`, `pin_error` | Whether the pin worked. |
| `link_public`, `link_private` | The `t.me` link of a public chat, or the `t.me/c/...` link that only members can open. |
| `sent_at` | When the post went out. |

`published_urls.telegram` is the public link when the chat has a username, else the private link.

## Analytics

Telegram gives bots no view counts. A Telegram post carries:

- `likes`: all reactions together
- `reactions`: the count per emoji
- `poll`: the votes per answer, for a poll

The account row carries the subscribers of a channel, or the members of a group. Views, reach, forwards and comments are not reported; open the post in Telegram to see its views.

## Removing a post

Telegram lets a bot remove a message only in the first 48 hours after it was sent. In that window, "Delete from Telegram" on the post in the OmniSocials dashboard removes every message of the post. After 48 hours, remove the post in Telegram yourself. `DELETE /posts/:id` removes the post from OmniSocials only and never touches the live message.

## Limitations

- One Telegram chat per workspace. Connect more chats through more workspaces.
- Text formatting (bold, italic, links in text) is sent as plain text for now.
- Topics in forum groups are not supported: the post goes to the General topic.
- Telegram messages are not part of the Social Inbox.
- A post that was sent cannot be edited through OmniSocials.
- Files over 50 MB need a self-hosted Telegram Bot API server and are not supported.

## When the connection breaks

When someone removes the bot from the chat or takes away its right to post, the account shows `needs_reconnect: true` in `GET /accounts` and posts to it fail with a message that says what to fix. Add the bot back as an admin with the right to post, then reconnect the chat in OmniSocials.
