Celebrations
Birthdays and work anniversaries, posted in a Slack channel on your company's working days.
Overview
Celebrations is a module. It reads birthdays and start dates from the member profile, and once a day it posts whoever is celebrating in a channel you choose, with a π from the bot so people pile on. It plans around your working week and your company's holiday list, so nothing is posted on a day nobody is around to see it.
It is off by default. A workspace admin turns it on.
Turning It On
-
Open the Celebrations page
In the dashboard, Celebrations is in the sidebar after Kudos. You can set everything up while the module is still off.
-
Pick a channel and a timezone, then save
Both are required. The other settings have defaults (see Settings).
-
Invite the bot to that channel
slack/invite @Morgenruf -
Turn on celebrations
A workspace admin clicks Turn on celebrations. The button is enabled once a channel and a timezone are saved.
Once it is on, Morgenruf also sends one DM to each person with no dates on file, asking for them. See Asking for dates.
Letting HR run it
Celebrations can be delegated. A workspace admin gives someone the Celebrations grant from the Members page, and that person can look after it without being a workspace admin. The grant is offered once Celebrations is on, because Members lists grants for active features only.
| Workspace admin | Celebrations admin | Member | |
|---|---|---|---|
| Turn Celebrations on or off | yes | no | no |
| Change settings, working days and holidays | yes | yes | no |
| See the next 30 days of celebrations | yes | yes | no |
| Ask for dates | yes | yes | no |
| Read settings and holidays | yes | yes | yes |
| Edit other people's profiles, import dates | yes | no | no |
A Celebrations admin who sets it up sees a note to ask a workspace admin to turn it on.
Settings
| Setting | Default | Notes |
|---|---|---|
| Channel | none | Required. Where the posts go. Invite @Morgenruf to it. |
| Timezone | none | Required. The clock your company celebrates on. Separate from any standup's timezone, because the people who run standups and the people who run celebrations are often different. |
| Post at | 09:00 |
24-hour time in the Celebrations timezone. |
| Working days | Monday to Friday | Any set of days, at least one. For example Sunday to Thursday. |
| Celebrate birthdays | on | |
| Celebrate work anniversaries | on | |
| Holidays | empty | Your company's days off. See below. |
The page also shows Coming up: every celebration in the next 30 days and the day each one will be posted. Only workspace admins and Celebrations admins see it, since it lists birthdays.
Holidays
Morgenruf does not ship a holiday calendar. Your company's own list is the only source. Add holidays one at a time (adding a date that is already listed renames it), remove them, or import a list:
date,name
2026-12-24,Christmas Eve
2026-12-25,Christmas Day
2026-12-26,Boxing Day
csv
- One
date,nameper line. Thedate,nameheader is optional. - Dates are
YYYY-MM-DD. Names are required, up to 80 characters. - The same date twice in one file makes the second line invalid.
- Dates more than a year in the past are refused. At most 1,000 lines per import.
- The import shows a preview first and saves nothing until you confirm.
Working days and holidays belong to the workspace, not to Celebrations alone, so later features can plan around the same calendar. For now they are edited from the Celebrations page. Holidays more than a year old are removed by a nightly job.
How Posting Works
Once a day, at the post time in the Celebrations timezone, and only on working days:
- Morgenruf collects everyone with a birthday or a work anniversary on that day, or on any day off between it and the next working day.
- It skips anyone who ticked Don't celebrate me publicly, anyone deactivated, and anyone who has left.
- It skips start dates less than a year ago, and start dates in the future.
- It posts one message per kind per day: all of that day's birthdays together, and all of its anniversaries together. Then it adds a π to each post.
29 February birthdays and start dates are celebrated on 28 February in years without one. Names are mentions, listed alphabetically by first name.
Weekends and holidays
A celebration that falls on a day off (outside the working days, or on a listed holiday) is posted on the last working day before it, as its own message. With Monday to Friday working days:
| Celebration on | Posted on | Opens with |
|---|---|---|
| Saturday | Friday | "Tomorrow is..." |
| Sunday | Friday | "On Sunday it's..." |
| Monday, a listed holiday | Friday | "On Monday it's..." |
| 25 December, with 24 to 26 December all listed as holidays on weekdays | 23 December | "On 25 December it's..." |
The exact rule for the opening words:
- The next day is "Tomorrow is".
- A day outside the working week, or a holiday straight after one (a long weekend's Monday), is named by weekday: "On Sunday it's", "On Monday it's".
- Any other holiday is named by date: "On 25 December it's".
- Anything seven or more days away is always named by date.
The second line of an early post is "Off for the weekend, so let's celebrate early.", or "It's a day off, so let's celebrate early." when the day is a listed holiday.
Catch-up and no double posts
The daily job fires at the post time and then once an hour for up to 8 more hours, always on the same day, so a server that was down at post time still posts that day. Each post is recorded in the database before it is sent, which makes every later firing a no-op: a restart, a later firing or a second pod never posts the same celebration twice.
If Slack refuses a post, the record is released and the next hourly firing tries again. A post Slack accepted is never sent a second time.
The π Reaction
After each post the bot adds a π so people pile on. That needs the reactions:write Slack
scope. The scope is optional: without it the post still goes out, just without the reaction, and no
workspace is forced to reinstall.
A workspace that installed Morgenruf before this scope existed does not have it. The Celebrations page says so ("Posts go out without the π reaction until Slack is re-authorised") and shows a Re-authorise Slack link to workspace admins. After re-authorising, posts get the reaction. A reaction that fails is logged and never costs the post.
Asking for Dates
Most people will not open their profile on their own, so Celebrations asks them with a DM:
- Add my dates opens the profile form.
- Skip me turns off public celebration for that person (any dates they have stay) and replaces the DM with "Got it. You will not be celebrated publicly. Change your mind any time with
/morgenruf profile."
Only active people in the Slack directory with no birthday and no start date, who have not opted out, are asked. Bots are never asked. A DM that cannot be delivered is not retried.
The one-time DM
With Celebrations on, the daily job sends the DM once to everyone still missing dates, including people who join the workspace later. It goes out with the first daily run, so on a working day at the post time, 100 people per firing; the rest follow on the next hourly firings. Nobody gets this DM twice.
The Ask for dates button
Ask for dates is on the Celebrations page, and on the Members page while Celebrations is on. It shows how many people would be asked and the message they would get before sending anything. It DMs only people still missing dates, and reaches each person at most once every 30 days (the one-time DM counts). It needs a channel and a timezone saved first.
Joining the channel
When someone joins the celebrations channel and has no dates on file, they get the same DM, under the same 30 day limit, however often they leave and rejoin.
Message Examples
The text is fixed in this version and is not customisable. Posts never use pronouns, since the profile does not store them.
An early post for three or more birthdays ends "...so let's celebrate early. Happy birthday to all of you. π". Three or more anniversaries read "the work anniversary of @Ana (1 year), @Priya (2 years) and @Tom (5 years)" and end "Thanks to all of you for everything. π"