The SMS panel dashboard with a Good morning greeting, Send SMS, Conversations and Contacts buttons, tiles with today's traffic and gateway status: working, 70 percent signal, SIM1 card, registered on the network

Programming

SMS panel: notifications sent from the browser

A web front end for my SMS gateway: sending to numbers, contacts and groups, scheduled sending, conversations, a shared inbox and notification sign-ups confirmed with an SMS code. One codebase runs as two instances, each with its own SIM card and look.

05/11/2026

My SMS gateway has an API for other systems, but people need something simpler. They want to type a number or pick a group, write the message and see whether it arrived.

That is how the SMS panel came about: a web application on top of the gateway. It never talks to the modem directly, only to the gateway API. Only the panel server knows the API key, and the gateway itself is reachable only from the local network.

The SMS panel dashboard with a Good morning greeting, Send SMS, Conversations and Contacts buttons, tiles with today's traffic and gateway status: working, 70 percent signal, SIM1 card, registered on the network(enlarge)
Panel dashboard: today's traffic and gateway status

One codebase, two instances

The panel operates in two places: as my personal instance and as an institution's instance, with its logo and colors. Each is a separate gateway client, with its own SIM card, API key, and webhook.

The name, owner, default theme and gateway connection are set in each instance's configuration file. A new release is a tag in the repository, and both instances pick it up on their own.

Sending

The send form combines several sources of recipients and shows the cost in text messages straight away:

  • manually entered numbers, contacts and entire groups in any combination, without duplicates,
  • a character and part counter that works exactly like the gateway, up to 10 parts,
  • send now or at a chosen time, with the option to cancel,
  • live progress: waiting, sent, delivered, failed,
  • resending only to recipients where it failed.

Polish letters cut an SMS from 160 to 70 characters. The panel warns about this below the message field, so it is easy to decide whether to write without diacritics.

The Send SMS form with fields for phone numbers and message text, a character and part counter, a send name, a choice of Now or At a chosen time, and a summary of recipients and messages(enlarge)
Send form with a part counter and a choice of time

Conversations and a shared inbox

An instance has a single number, so the inbox and conversations are shared among all users. Each user manages their own archives and trash. Conversations group messages by number and allow replying from the thread.

Message statuses arrive from the gateway through a signed webhook. Without a webhook, the panel checks the gateway history every 30 seconds. A status can only move forward, so a late event will not turn "delivered" back into "sent".

The inbox with Received, Sent, Scheduled, Archive and Trash tabs, a search box and Archive and Move to trash buttons, with one sent message listed and its number blurred(enlarge)
Inbox with folders and search functionality

Address book

Contacts and groups can be private or shared. Everyone sees shared ones, but only the author or an administrator can change them. CSV import and export handle semicolons, because that is what Excel writes, and skip numbers already in the address book.

The Contacts page with a search box, New contact and Export CSV buttons, a new contact form with a Shared option, and import from a CSV file(enlarge)
Contacts with a form and CSV import

Notification sign-ups

A group can be opened for public sign-ups. Then it has its own page, an embeddable window for another website, and a simple API. This way, an institution can collect the numbers of people who want to receive SMS notifications.

  • every number is confirmed with an SMS code, and the consent text is stored with the sign-up,
  • limits per number, IP address and group protect against cost inflation,
  • unsubscribing by replying with STOP or on the unsubscribe page, with SMS confirmation.

Security

  • argon2id passwords and Redis sessions,
  • mandatory two-factor authentication: app or SMS code, plus backup codes,
  • limit on login attempts and time-based lockout,
  • administrator and user roles,
  • event log stored for 12 months.

Look and accessibility

The panel has seven themes, each with its own colour palette, typefaces and shapes. The administrator picks one in Settings and sees a preview in light and dark mode at once. Each user sets their own mode and text size.

The interface is designed for WCAG 2.2 AA. A script checks the colour contrast of every theme before each release.

Appearance settings: the Graphite, Forest, Brick, Ink, Night and Raspberry themes plus the institution's theme, a preview of the selected theme in light and dark mode beside them, and the gateway connection below(enlarge)
Choosing a theme with a preview in both modes

Statistics

Statistics show messages and parts sent, delivery rate, average delivery time and an estimated cost. Each chart has a data table next to it, which screen readers can also read. The panel measures gateway availability itself every 30 seconds.

Good SMS sending doesn't end with the Send button. It's what happens afterwards that matters: did the message arrive, who replied, and is anyone opting out?

- Aleksander Kowal

Stack

  • Astro 7 with server-side rendering and a custom API,
  • TypeScript, Tailwind v4, DaisyUI v5 and Lucide icons,
  • PostgreSQL 16 and Redis 7,
  • Docker, deployment from a repository tag.

Click or scroll to zoom in. Drag to move around.Double tap or pinch to zoom in.

Was this useful?

New posts by e-mail

Every now and then, when a new post or project goes up. No ads.

Got an idea, a question or a comment? Write to me:Enable JavaScript to see the e-mail address