Telegram bot to generate video: a Sume job, then sendVideo

A Telegram bot can turn /video into a Sume job, reply at once, then call sendVideo with the artifact URL when Sume's signed webhook arrives.

6 min readSume
All posts

To make a Telegram bot generate video, have its webhook turn a /video command into POST /v1/videos with model: "sume/auto" and a callback_url, reply right away, and when Sume's signed job.completed webhook arrives, call sendVideo with the video artifact's media.sume.com URL. Telegram fetches a video sent by URL only up to 20 MB, so upload bigger files with multipart/form-data, up to 50 MB, or send the link.

Telegram facts come from the Telegram Bot API reference; Sume facts come from Video Generation, Webhooks, and Verifying webhooks, plus the Sume API reference for artifact fields. All were read on 2026-09-27. Sume has no Telegram bot or integration: this is your bot's server calling Sume over HTTPS. The Discord version of this flow is in Discord bot AI video generation.

How does the bot take the /video command?

Register your HTTPS endpoint with setWebhook and a secret_token. Telegram then sends that token in the X-Telegram-Bot-Api-Secret-Token header of every update, so the route can refuse anything else. Telegram repeats an update your server answers with a non-2XX status, and update_id lets you spot repeats, so build Sume's Idempotency-Key from it: a replay returns the original job instead of starting another.

The reply can ride on the webhook response. Telegram runs a Bot API method named in the response body, though you cannot learn whether it succeeded.

import crypto from "node:crypto";
import express from "express";

const app = express();
app.post("/telegram", express.json(), async (req, res) => {
  const got = Buffer.from(req.get("X-Telegram-Bot-Api-Secret-Token") ?? "");
  const want = Buffer.from(process.env.TELEGRAM_SECRET_TOKEN);
  if (got.length !== want.length || !crypto.timingSafeEqual(got, want)) return res.sendStatus(401);
  const msg = req.body.message;
  if (!msg?.text?.startsWith("/video ")) return res.sendStatus(200);
  const r = await fetch("https://api.sume.com/v1/videos", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SUME_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": `telegram-${req.body.update_id}`,
    },
    body: JSON.stringify({ model: "sume/auto", prompt: msg.text.slice(7), callback_url: "https://bot.example.com/hooks/sume" }),
  });
  const job = await r.json();
  if (r.ok) await saveJob(job.id, msg.chat.id); // your store: job id -> chat id
  // A Bot API call in the webhook response: Telegram runs it, you get no result.
  res.json({ method: "sendMessage", chat_id: msg.chat.id, text: r.ok ? "Rendering your video..." : "Sume refused the request." });
});

How does the bot send the finished video?

Sume POSTs one signed job webhook when the job reaches a terminal state, with up to 10 attempts of 10 seconds each. Verify the raw body with verifyWebhook from @sume-com/sdk, which also accepts either sume-v1= entry during a secret rotation. Claim the job_id so a retried delivery is handled once, answer 204, then call Telegram. On job.completed, payload.artifacts[] holds a video entry whose url is a public media.sume.com artifact, and the API reference gives each artifact a content_type and a nullable size_bytes. Two choices in the code are this post's, not Telegram's: it reads the 20 MB URL limit as 20,000,000 bytes, and when size_bytes is missing or sendVideo fails, it posts the link as text.

import { verifyWebhook } from "@sume-com/sdk";

const tg = (method, params) =>
  fetch(`https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/${method}`, {
    method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(params),
  }).then((r) => r.json()); // { ok: true, result } or { ok: false, description }

app.post("/hooks/sume", express.raw({ type: "application/json" }), async (req, res) => {
  const ok = await verifyWebhook({ body: req.body, headers: req.headers, secret: process.env.SUME_COM_WEBHOOK_SIGNING_SECRET });
  if (!ok) return res.sendStatus(401);
  const event = JSON.parse(req.body);
  const chatId = await claimJob(event.job_id); // your store: null if unknown or already handled
  res.sendStatus(204); // Sume allows 10 s per attempt
  if (!chatId) return;
  const video = event.event === "job.completed" && event.payload.artifacts.find((a) => a.type === "video");
  if (!video) return tg("sendMessage", { chat_id: chatId, text: "The video job did not complete." });
  const byUrl = video.size_bytes != null && video.size_bytes <= 20_000_000; // our reading of "20 MB"
  const sent = byUrl ? await tg("sendVideo", { chat_id: chatId, video: video.url }) : { ok: false };
  if (!sent.ok) await tg("sendMessage", { chat_id: chatId, text: video.url }); // our fallback; or upload (50 MB)
});

Which way should the bot send the file?

sendVideo takes a file_id, an HTTP URL, or a multipart upload, and Telegram clients support MPEG4 video. The size limit depends on the method:

From the Telegram Bot API sections on sending files and sendVideo, and the artifact schema in the API reference, read 2026-09-27.
MethodLimitUse it when
HTTP URL in video20 MBsize_bytes is under the limit. Telegram downloads the file, which must have the correct MIME type.
Upload with multipart/form-data50 MBThe file is larger than 20 MB: download it from media.sume.com, then upload it.
file_idNo limitThe video is already on Telegram's servers. A file_id works only for the bot that received it.
Local Bot API ServerUploads up to 2000 MBYou run Telegram's Bot API server yourself. Otherwise, send the link as text.

What are the limits?

Plan for these before you open the bot to a chat:

  • Sume sends terminal job events only, with no progress deliveries, and video generation typically takes 30 seconds to several minutes. sendChatAction with upload_video shows a status for 5 seconds or less, so send a text reply for the wait.
  • Artifact URLs are public by link, so anyone who can read the chat can open the video.
  • Every command starts a job paid from your workspace balance. Sume's docs say to validate user input and enforce your own authorization before forwarding requests, so decide which chats may use the bot.
  • Telegram webhooks accept ports 443, 80, 88, and 8443, but current Sume code refuses a callback_url with a non-default port, such as :8443. Serve Sume's route on the default HTTPS port.
  • A delivery that never lands does not change the job. Keep a GET /v1/jobs/{id}/status read as the backup.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume