🔓

UNLOCK: Interactive Greeting Cards and Advent Calendars

our own studio service: a card behind a password link, managed from Telegram

Our own service for interactive greeting cards and advent calendars: the recipient cracks a locked terminal, and the owner edits everything from a Telegram bot and a web cabinet. Python, aiohttp, PostgreSQL.

In production · in-house product
1 install
serves any number of cards and advent calendars, each with its own address and theme
72 labels
edited from the bot and the cabinet with no code or deploys
7 themes
for cards, plus 4 more styles for the calendar

The task

An ordinary greeting card is a picture or a template landing page that the recipient scrolls past in a second. Nobody will deploy and pay for a separate backend-driven site for every personal occasion. We needed a way to turn a link into a small story without launching a new project for each client.

Additional requirements:

  • the service owner creates and edits cards alone, from a phone, without a developer;
  • one card is sent to many guests, and each sees their own name;
  • clients can be connected and limited by a plan;
  • a card opens on a weak phone and does not go down with the server.

The solution

An interactive card is a page where the recipient does something themselves: solves, chooses, opens. We built it as a locked terminal and added an admin panel in Telegram.

What was implemented:

  • A card behind a link with a password screen, the Recovery Protocol, three screens and a finale; the recipient's choice goes to the owner's Telegram.
  • A card-creation wizard in the bot with five questions: occasion, to whom, from whom, when, key.
  • Seven themes: Romance, Marksman, Bunker, Birthday, Wedding, Neon and Noir.
  • A web cabinet with a live preview: fields on the left, the card on the right, and a «How it went» report showing how far the recipient got.
  • One card for many guests: the name in the link, bulk link generation, and export of guests and replies to CSV.
  • An advent calendar: windows by day, four styles, and the password and date checked by the server.
  • Plans, owner, operator and client roles, invitation codes, private cards and closed drafts.
  • Delayed publishing, a QR code for the link, a webhook for submissions, and a showcase landing page for the service with demo cards.

How it works

A card made of one HTML file

The card page is a single static HTML file: no build step, no dependencies, no backend. The font sits next to it, the fireworks are drawn by its own code, and sound effects are synthesised with Web Audio. It gets its theme and texts from the service but keeps its own copy of the values, so when the service is unavailable it opens on those and on the cache of the last visit. The copy is not written by hand: a sync_site_defaults command generates it from the service schema, and the smoke tests fail if the file drifts from the schema.

A web cabinet as a Telegram Mini App

A Mini App is a web page that opens inside Telegram. Sign-in needs no passwords: Telegram passes signed data about the user, and the service verifies the signature with the bot token on every request. Permissions are the same as in the chat: a client sees only their own cards. On a computer the cabinet opens through a one-time link from the bot: it lives for 10 minutes and is spent only by pressing a button, so messenger link previews do not burn it. The database stores only token hashes, and a session lasts 30 days.

A birthday, a wedding, a company event: many recipients, one card. A link like «/c/address?to=Name» puts the name into the terminal, the greeting, the ticket and the preview. Without the parameter, the name from the card itself is used, so links sent earlier keep working. The report shows who opened, how far they got and who replied, including those who stayed silent.

The advent calendar: the server checks the password and the date

In a card the key is part of the game; in a calendar it guards the gift. A window's contents and files reach the browser only after two server checks: the password is right and the day has come. A window opens at midnight in the recipient's time zone, and missed days are not lost. Letter case, the letter «ё» and extra spaces in the password are ignored.

A messenger's robot does not run scripts and reads only what is baked into the file. So the service serves the page at «/c/address» and inserts the title, description and image of that very card. Without this, every client would show the same link card in a chat.

Plans and client roles

Each plan has three numbers: how many cards, how many megabytes per card and how many days it lives. The plan arrives with the invitation code: the owner presses «Code for a client», the person sends the code to the bot and immediately works under their plan. Three days before expiry the card owner gets a warning in the chat.

Results

  • A new client is connected with an invitation code: no deploy, no separate site and no code changes.
  • A card turns from a picture into a short scenario, and the recipient's reply arrives in Telegram.
  • A card stays working when the service is unavailable.
  • The database publishes no port and is visible only to the service.
  • Plans offer five options: from a trial card for 14 days to a Studio plan with an unlimited number of cards.
  • Smoke tests run in CI on every push; the README notes 2,305 checks.

Technologies and why

  • Python 3.13 and aiohttp — the API that serves card settings and receives the recipient's choice.
  • aiogram 3 — the admin bot on long polling: the creation wizard, edit panels, invitation codes.
  • PostgreSQL 17 — cards, replies, plans and sessions; timestamps are stored as TIMESTAMPTZ.
  • One HTML file — the card itself, with no build step or dependencies.
  • Docker Compose and nginx — two containers, the service and the database; exactly one port is published.
  • GitHub Actions — smoke tests with Postgres on every push.

Status

Version 3.16.1 of 29 September 2026, in production, an in-house product of the studio. The latest releases are the service's showcase landing page and a cabinet that works well on phone and desktop. What is not there yet: payment acceptance, so plans are assigned manually with an invitation code or an owner command. A live run with a real Telegram token has not been done, and the tests run on a stub. The task list is kept in the repository's ROADMAP.

Questions about this project

What is an interactive greeting card behind a password link?
It is a link that opens not a picture but a locked terminal that asks for a password. If the recipient forgot it, a Recovery Protocol with personal questions kicks in. Enter it correctly and the terminal blooms into the theme, followed by three screens: a question, an invitation and a finale where the recipient assembles the evening. The choice arrives in the owner's Telegram.
How do you make one card for many guests?
The guest's name is passed in the link as a «?to=» parameter and is not stored in the card. Each guest sees their own name in the terminal, the greeting, on the ticket and in the link preview, and the reply arrives signed with the name. The cabinet has a «Guests» tab: paste a list of names, get a personal link for each, and export the list to CSV.
How do you build an online gift quest with an advent calendar?
A calendar is created from the bot chat: «New», «Advent calendar», four questions, and the link is ready. It is a grid of 1 to 31 windows, each opening on its own day in the recipient's time zone. Inside can be a note, photo, GIF, video, video note, voice message, music, a link button or a promo code. The server checks the password and the date.
How does the owner edit texts and design without touching code?
From the Telegram bot with buttons or from a web cabinet that opens right inside Telegram, or on a computer through a one-time link from the bot. Any of 72 labels can be changed, along with one of seven themes, the key, media and the link preview. Files for a card can simply be sent to the bot in chat.
What happens if the service is down?
The card keeps working on values baked into the file and on the cache of the last visit. The page is a single HTML file with its font stored next to it and fireworks drawn by its own code, so it makes no external requests.
How are client access and card lifetimes limited?
Plans set three numbers: how many cards, how much storage per card and how many days a card lives. A client is admitted to the admin panel with an invitation code and sees only their own cards. An expired card goes to the archive: the link stops opening, while the texts and statistics remain.

Need something similar?

Tell us about the task — we'll show how we solved it and estimate the scope.