Notifications and calendars
Updated 27.10.2026
How to find out about schedule changes without asking the API every minute. Checked with booth d1 on October 23, 2026; the methods of this page are also available on production.
What to choose
| Problem | Than to solve |
|---|---|
| Schedule in Google Calendar, Apple Calendar, Outlook | Subscription to calendar feed GET /calendar/schedule.ics |
| Exams and tests in the calendar | GET /session.ics |
| Bot or service that reports changes | Polls GET /schedule/changes every few minutes |
| A complete copy of the schedule at home | GET /schedule times an hour or two, a comparison of updatedAt |
| Push to the browser | Only for the application itself VISH — see below |
Calendar Feed
GET /calendar/schedule.ics returns a schedule in iCalendar format with the same filters as GET /schedule: group or groupId, teacher or teacherId, scope, dateFrom, dateTo, and lessonIds and subjectIds (only these classes or subjects), locale (ru, en, zh) and attachment=1 (to give as a file).
- You don't need a key: it's the same schedule that any visitor sees.
- Subscribe to the address — the calendar itself rereads it and picks up edits, cancellations and deletions. Each activity has a constant
UID, so the change updates the event rather than adding a second one. - Time is with the time zone (
DTSTART;TZID=Europe/Moscow:…). - No more than 60 requests per minute from one address.
https://d1.vish-app.ru/api/v1/calendar/schedule.ics?group=ШАД-111
GET /session.ics is the same for the session.: exams, tests, and consultations with a group or teacher.
Change Feed
GET /schedule/changes — what has recently changed in the schedule of one group (groupId) or teacher (teacherId), new from above.
period=recent(by default) — recent changes;limit— how many.period=weeks— changes in classes for this and next week.- At the record:
lessonId,change(created,announced,updated,deleted),subject,room,startAt,endAt,changedFields,changes( field, was, became),origin(institute— revision at the institute,source— new university schedule) andcreatedAt.
How to poll: once every 5-15 minutes, memorizing id or createdAt of the last processed record, and process only what is newer. The answer from period=weeks is cached for a minute — it's pointless to ask more often.
Push in the browser
POST /push/subscriptions and POST /push/unsubscribe subscribe the browser of application VISH to notifications about changes to the group or teacher; the public VAPID key for subscription is in GET /app-config (push.publicKey). These methods are designed for the application VISH itself: the subscription is created by its service worker. The change feed is more convenient for your site or bot.
Example: feed and changes
The example downloads the calendar feed of a group, checks its format and uniqueness UID, and then reads the changes of this and next week. The group is VISH_GROUP, with ШАД-111 by default.
// The group's calendar feed and its recent changes; no key needed.
const API = process.env.VISH_API ?? 'https://d1.vish-app.ru/api/v1'
const GROUP = process.env.VISH_GROUP ?? 'ШАД-111'
const groups = await (await fetch(`${API}/groups?search=${encodeURIComponent(GROUP)}&all=1`)).json()
const group = groups.list.find((item) => item.name === GROUP)
if (!group) throw new Error(`group ${GROUP} not found`)
const feed = await fetch(`${API}/calendar/schedule.ics?group=${encodeURIComponent(GROUP)}`)
const calendar = await feed.text()
if (!feed.ok || !feed.headers.get('content-type')?.startsWith('text/calendar') || !calendar.startsWith('BEGIN:VCALENDAR')) {
throw new Error(`not a calendar: HTTP ${feed.status}`)
}
const uids = [...calendar.matchAll(/^UID:(.+)$/gmu)].map((match) => match[1].trim())
if (new Set(uids).size !== uids.length) throw new Error('duplicate UIDs')
const response = await fetch(`${API}/schedule/changes?groupId=${group.id}&period=weeks`)
if (!response.ok) throw new Error(`changes: HTTP ${response.status}`)
const changes = (await response.json()).list
if (!Array.isArray(changes) || changes.some((item) => !item.lessonId || !item.change || !item.createdAt)) {
throw new Error('unexpected changes answer')
}
console.log(`${GROUP}: ${uids.length} events in the feed, ${changes.length} changes this week and next`)
for (const item of changes.slice(0, 5)) {
console.log(`- ${item.createdAt.slice(0, 16)} ${item.change}: ${item.subject?.ru ?? item.lessonId} (${item.changedFields?.join(', ') ?? ''})`)
}To try
The current week, the call grid, and the public key for push:
Try the request
Only methods with a lock need a key. It lives on this page only: it is never stored, goes out only as a header of this request and is forgotten when you leave. Where to get a key
Verified: 2026-10, PWA aa337c45, platform bff65fce._