- Create a product
- Upload files to Telegram and register them
- Create the steps
- Set when each step is sent
- Activate the product
Before you start
You need:- An API token — see Authentication.
- The bot ID — from List bots.
- The bot token from @BotFather — only for uploading files from a script. The API never returns it (only
token_hint). - You have started the bot — the Telegram account linked to your Zenedu user (the owner of the API token) must have pressed Start in the bot at least once. The file endpoints check the file by sending it to that chat and return
409otherwise. - For files over 50 MB — either an
api_idandapi_hashfrom my.telegram.org to upload them from a script, or a person who sends each such file to the bot once. See Upload files.
PRODUCT_ID, OFFER_ID, …):
1. Create a product
data.id as PRODUCT_ID. platform must be bot — steps belong to bot products. A new product is inactive; you activate it in step 5 when the content is ready.
2. Upload files
Step attachments live on Telegram’s servers. The API does not accept file uploads — it accepts a Telegramfile_id of a file uploaded by this bot. You attach files to steps in two stages: get the file into Telegram, then register it with Register a file to get an ID for telegram_file_ids.
There are three ways to get a file into Telegram. Choose by file size and by what you have:
Videos must be MPEG4 (
.mp4, for example H.264 video with AAC audio) to play as videos. Telegram may store other formats as a document, and registering such a file as video fails with type_mismatch.
- Bot API (up to 50 MB)
- MTProto (up to 2 GB)
- Send to the bot (up to 2 GB)
Send the file with the Bot API to your chat with the bot, take its Use
file_id, and delete the message:sendPhoto for pictures and sendDocument for PDFs and other files. For videos, pass width, height, duration, and supports_streaming — otherwise Telegram may store the file as a document, and registering it as video fails with type_mismatch.For documents, the uploaded file name is what subscribers see. Use a readable name: -F "document=@sheet.pdf;filename=Coloring page.pdf".file_id:
data.id of each file, for example as VIDEO_FILE_ID and IMAGE_FILE_ID.
The API checks the file by sending it to your chat and deleting the message right away, so you may see a short flicker in the chat.
Registered files stay in List Telegram files until you attach them. Files are per user: you only see and attach your own. Delete a file removes a file you don’t need.
3. Create the steps
Create steps with Create a product step. Each new step is added to the end of the product. All step types:
A lesson with a video and a picture:
data.id (here as LESSON_STEP_ID) — you’ll need step IDs for buttons and sending_time.
Text
text is HTML, the same as the dashboard editor produces. <p> and <br> become line breaks. Formatting tags are passed to Telegram: <b>/<strong>, <i>/<em>, <u>, <s>, <a href="…">, <code>, <pre>, <blockquote>, <tg-spoiler>. Any other tag is stripped and its text is kept — for example, lists (<ul>, <li>) turn into plain lines.
Length limits:
- 4096 characters — a regular message.
- 1024 characters — when the step has attachments and
settings.attach_fileistrue(the text is sent as the media caption). Withattach_file: false(the default), the text goes as a separate message after the media, and the 4096 limit applies. - 300 characters —
pollandquiz(the question).
Attachments
telegram_file_ids takes IDs from step 2. Several attachments are sent together as an album. The set you pass replaces the step’s attachments; to remove one file use Delete step attachment.
Questions
subtype is one of text, number, email, phone, name. The answer is checked against it; on a wrong format the subscriber gets question_error_message and can try again. If you don’t set it, a default message in the bot’s language is used. save_profile: true saves a name, email, or phone answer to the subscriber’s profile.
Polls and quizzes
- 2–12 options, up to 100 characters each.
- A
quizneeds exactly oneis_correct: true, otherwise422options. quiz_explanation(up to 200 characters) is shown after the answer.- A
pollhas no correct answer.
Assignments
task_autocheck: true— the assignment is accepted on submission, and the course continues right away.task_autocheck: false— the assignment waits for review in Tasks; the course continues after you approve it.
4. Set when each step is sent
Every step has asending_time. A step is scheduled when the previous step is sent; the first step — when the subscriber gets access to the product. sending_time.option decides when exactly:
Set it when you create the step, or later with Update sending time:
Only
stop waits for the subscriber. With any other option, the next step is scheduled as soon as the previous one is sent — even if the subscriber hasn’t answered the question or submitted the assignment yet. With immediately on every step, the whole course arrives at once.Wait for the subscriber: stop
What counts as an action on the previous step:
A
message or lesson has no action of its own — give it a button:
- create the steps one by one: after each
messageorlesson, add its button, then create the next step withstop; or - create all steps with
immediately, add the buttons, then switch steps 2…N tostopwith Update sending time.
After a delay: duration
delay_type is seconds, minutes, hours, or days.
After a text question there’s one exception: if the subscriber answers before the delay is over, the delay starts again from the moment of the answer. Answers to polls and quizzes and submitted assignments don’t affect the delay.
To keep messages out of the night or weekends, add a sending window (delay_limit, see below).
At a time of day: time
- Only the time of day from
delay_timeis used; the date part is ignored. Pass it with an offset — see Time zone. delay_numberis the number of days after the previous step was sent:0— the same day at that time, or the next day if that time has already passed;1— the next day; and so on.delay_limit.weekdayslimits the days: on a day that isn’t allowed, the step moves to the next allowed day at the same time.time_fromandtime_toare ignored for this option.
time with delay_number: 1 on each lesson.
On a date: date
delay_at with an offset — see Time zone.
- When you save an active step,
delay_atmust be in the future, otherwise422sending_time— “Change the sending time to a date in the future”. - A subscriber who reaches the step after the date has passed gets it right away. With
skip_past_date: true, the step is skipped instead, and the subscriber moves on to the next step.
Sending window: delay_limit
Limits duration to certain hours and days (for time, only the days apply):
time_fromandtime_toare minutes from midnight in the workspace time zone:540is 09:00,1260is 21:00. Only whole hours count —570(09:30) works as 09:00.time_fromgreater thantime_tois an overnight window, for example1320–360(22:00–06:00).time_fromequal totime_tomeans any time of day; then onlyweekdaysapply.weekdays:mon,tue,wed,thu,fri,sat,sun— at least one.- If the step falls outside the window, it’s moved to the next moment the window is open on an allowed day.
- A window of any time on all seven days is the same as no window and isn’t saved.
Not sent automatically: never
The step is skipped in the course: subscribers don’t get it after the previous step, and the next step follows the previous one directly. This is the default when sending_time is omitted.
Time zone
Schedules are calculated in the time zone of your workspace (set in the dashboard, workspace settings; UTC if not set): the time of day fortime, and the hours and days of delay_limit.
delay_at and delay_time are date-time values. A value without an offset is read as UTC, so pass the offset of your workspace time zone — for example, 10:00:00+02:00 for 10:00 in Kyiv in winter. For time, the time of day stays fixed in the workspace time zone all year.
5. Activate the product
Things to keep in mind
- Language of default texts. The task button, its messages, and the default question errors are created in the bot’s language. API responses and error messages are always in English.
- Files are per user. Files registered with one API token can’t be attached by another user’s token.