Appearance
Telegram and Slack notifications in Laravel, without code
A site's owner rarely sits in the admin. They hear about a new order from an email they may open tomorrow, about a failed backup from nobody, and about the contact form when a customer calls to ask why no one answered. What they do read, all day, is a chat.
Laravel has had notification channels for years, and a Telegram message is one HTTP call. Wiring them to a site is still the same work every time: which events are worth a message, who gets what, how to keep a bot token out of the code, what to do when the chat removes the bot, how not to send the same "low stock" fifty times. kitloom/notifications is that work done once, for every module of the site.
What the owner gets
A channel is one place to send to — a Telegram chat, a Slack channel or an email address — with the news it wants ticked. The site sends each piece of news to every channel that ticked it:
| From | News |
|---|---|
| Shop | a new order (exactly when the shop's "New order" email goes), a cancelled, failed or refunded one, low or no stock, a backorder, a new customer, a review waiting |
| Bookings | a new booking, saying whether it waits for confirmation; a client's own cancellation |
| Contact forms | every message that got through, its fields as the form shows them |
| Listings, Courses | an enquiry about a listing; a new student, a course review waiting |
| Comments, Privacy | a comment waiting for approval; a personal data request to answer, with the date to answer by |
| Site health | a failing health check, a failed backup, links that broke since yesterday |
| Security | sign-ins locked out, a role raised, a module switched, an admin signed in as someone else |
A message is short: a heading ("New order #1042 — $84.00"), a few lines — the customer, the items, the payment method — a button that opens the record in the admin, and one to the page on the site. It is written in the site's language, not in the language of whoever happened to cause it.
Set up in the admin
Notifications → Channels → New channel, a name, Send to Telegram, Slack or Email:
- Telegram: a bot from @BotFather —
/newbot, a name, and it hands you a token. Open the bot and press Start, or add it to your team's group. Paste the token, press Find the chat, and the site lists the chats that wrote to the bot; pick yours. No chat ids to look up. - Slack: an app with Incoming Webhooks on, a webhook for the channel, its URL pasted in.
- Email: one address or several.
The news worth acting on is ticked on a new channel already; the rest is a tick away. Send test sends one at once and says whether it got through. The token and the webhook URL are encrypted with the application key and never shown again.
A user ticks only the news they could see themselves: a shop manager given the Notifications section can send orders to the warehouse chat, but not failed backups or locked sign-ins. A channel another admin set up keeps the topics the manager may not see.
The parts you would otherwise build
- Repeats are held back. A product that stays low in stock, a health check that fails at every run — each reaches a channel once in a while (an hour by default), not at every order. A new order or a booking is never held back.
- A broken channel says so. A bot removed from its group fails every message; after five in a row the channel is turned off, the site's administrator gets an email, and Site Health warns until someone fixes it.
- Nothing slows the checkout. Messages go from the queue. A Telegram outage does not make an order fail.
- Nothing is kept. The site stores no copy of the messages: what was sent is in the chat. A switch keeps customers' names and addresses out of a chat shared with people outside the team.
- No SDK. Telegram and Slack are called over Laravel's HTTP client; email goes through Laravel's notifications, so the application hears
NotificationSentas for any other.
Run it from an AI agent
Channels are open to an AI agent connected to the site through kitloom/ai. It lists them with their news and their last error (notifications_list), changes their name, their news and whether they are on (notifications_update), and sends a test (notifications_test). You ask:
- "Stop telling the shop chat about reviews, and add failed payments to it."
- "Which channels failed this week, and why?"
- "Send a test to the team's Telegram."
The agent acts as the user whose key it holds: it adds only news that user could see, and only if that user may manage notifications. It never sees a bot token or a webhook URL, and it cannot make a channel or change where one sends — that needs a secret, and secrets stay out of chats. It deletes nothing; it can turn a channel off. describe_site tells it every topic there is before it starts. How to connect one: Run your site from Claude or Cursor.
Your module's own news
A module of your own tells the owner its news with one class: a NotificationTopic — its name, the events it hears, which of them count, and the message it makes — tagged in the module's provider. The channels' form lists it, the owner ticks it, and the module never requires the notifications module to do so. The example and the rules are in Extending › Telling the owner.
Watch it
Two short lessons from the admin, narrated:
Try it
Open a sandbox in one click, with no sign-up: Filament or Nova. Every detail of the module is in the Notifications guide.