The WordPress Notification System That Shouldn’t Work (But Does)
Most people think WordPress’s post system is only for blog posts or pages. But I used it to build a real-time in-app notification system – and it works better than you’d expect. Here’s how. The Core Idea: Use Posts as Notifications Instead of creating a new database table, I registered a hidden custom post type

Most people think WordPress’s post system is only for blog posts or pages.
But I used it to build a real-time in-app notification system – and it works better than you’d expect.
Here’s how.
The Core Idea: Use Posts as Notifications
Instead of creating a new database table, I registered a hidden custom post type called sf_notification.
register_post_type('sf_notification', array(
'public' => false,
'show_ui' => false,
'show_in_menu' => false,
));
Every notification is just a “post.”
This means:
- You instantly get database indexing, querying, and revisions
- It works automatically in WordPress Multisite
- You avoid custom SQL or schema migrations
It’s a little unorthodox, but it gives you a free data layer that scales across hundreds of sites.
Making It Work Like a Real System
1. Idempotency: No Duplicate Notifications
A common problem with async actions is duplicate notifications – especially under load.
The solution: an idempotency hash that expires after 5 minutes.
$idempotency_key = md5($type . '_' . $user_id . '_' . serialize($meta) . '_' . $triggered_by);
$last = get_user_meta($user_id, '_sf_last_notification_' . $idempotency_key, true);
if ($last && (time() - intval($last)) < 300) {
return false; // Skip duplicate
}
This makes the system safe to call multiple times without spamming the user.
2. Role-Based Delivery Rules
Not every event should go to every user.
The system checks roles and context dynamically – and ignores self-triggered events.
Example:
- If a sales rep creates an order, they don’t get notified.
- But their manager and fulfillment users do.
It’s fully extensible and easy to map to WordPress roles or custom logic.
3. Performance: Batch Loading Meta
Storing notifications as posts could be slow if you’re not careful.
So it batch-loads all meta in one go to prevent N+1 queries.
if (!empty($notifications)) {
$ids = wp_list_pluck($notifications, 'ID');
$meta_keys = ['_notification_type', '_notification_priority', '_notification_status', '_notification_created'];
foreach ($meta_keys as $key) {
foreach ($ids as $id) {
$meta[$key][$id] = get_post_meta($id, $key, true);
}
}
}
Everything is pre-fetched, so the frontend can render instantly.
4. Frontend: Alpine.js + AJAX
The UI is powered by Alpine.js, making it reactive without needing a full SPA framework.
Notifications are fetched via AJAX and update live without a refresh.
Simple, lightweight, and no need for extra dependencies.
5. Built-In Multisite Support
The best part?
Because notifications are stored as posts, each site in a Multisite automatically has its own notifications table (wp_X_posts).
No extra schema, no cross-site pollution, no migration headaches.
It just works.
Why It Shouldn’t Work – But Does
This is one of those WordPress hacks that feels like it shouldn’t scale, yet it does.
It works because:
- WordPress’s post system is a flexible data model
- Its caching layer and object meta system are battle-tested
- Multisite automatically isolates post data per tenant
- You can treat posts as “documents” – just like MongoDB – with relational benefits
It’s not just a hack.
It’s a repurposed content system turned into a distributed notification engine.
Why I Love This Pattern
- No external tables
- Zero dependencies
- Multisite ready
- Queryable via WP_Query
- Caches and pagination for free
- Easy to back up and migrate
This is WordPress doing what it was never meant to do – and doing it surprisingly well.
Lessons from This Build
- Don’t fight WordPress – repurpose it.
- Idempotency is critical for real-time systems.
- Caching and batching make unconventional models viable.
- Multisite is a hidden scaling advantage if used right.
- You don’t need new infrastructure for every feature – sometimes the old tools are enough.
The Takeaway
Everyone talks about “headless WordPress,” but sometimes the smartest move is using WordPress itself as your backend framework.
A hidden post type, a few smart checks, and some front-end reactivity – and you’ve got a modern notification system running on the world’s most battle-tested CMS.
Bojan