/iblai-vibe-notification
Mirrored from
iblai/vibeΒ·skills/users/iblai-vibe-notification/SKILL.md. This page is generated β edit it in the repository, not here.
First time here? If
iblai.envhas noARCHITECTURE=, run/iblai-vibe-startfirst (four questions; two minutes) β it decides single-org / multi-org / headless and who signs in, and every skill reads the answer.
Add notification features -- a compact bell icon with unread badge for your navbar and a full notification center page with Inbox and Alerts tabs.


Template: the bell this skill creates is bundled as
assets/notification-bell.tsx.j2. See/iblai-vibe-scaffoldfor the{{ }}contract.
Do NOT add custom styles to ibl.ai SDK components β they ship with their own styling. Do NOT implement dark mode unless asked.
Follow the component hierarchy: use ibl.ai SDK components
(@iblai/iblai-js) first, then shadcn/ui (npx shadcn@latest add <name>).
Navbar: If the user wants a navbar with the bell, send them to
/iblai-vibe-navbarfirst.
Step 0: Start from vibe-starter? (new projects)
Before running this skill, ask the user:
Are you starting a new project from scratch? vibe-starter (https://github.com/iblai/vibe/tree/main/skills/start/iblai-vibe-ops-init/assets/vibe-starter) already ships the notification bell and /notifications page wired up, alongside auth, navbar, and profile/account. Want to use that instead?
If yes, copy the bundled starter template from the installed
iblai-vibe-ops-init skill's assets/vibe-starter/ directory (it sits
alongside this skill in your skills directory; in the vibe repo it lives
under skills/start/), or fetch it from the vibe repo if those assets
are not installed -- tell the user which path you took -- then skip this
skill:
cp -a <skills-dir>/iblai-vibe-ops-init/assets/vibe-starter/. .
# or, without local assets:
git clone --depth 1 https://github.com/iblai/vibe.git vibe-tmp && cp -a vibe-tmp/skills/start/iblai-vibe-ops-init/assets/vibe-starter/. . && rm -rf vibe-tmp
pnpm install --ignore-scripts
Run with
--ignore-scriptsto skip package lifecycle (postinstall) scripts.
If they prefer to add notifications to an existing app, continue below.
Prerequisites
- Auth set up (
/iblai-vibe-auth) - MCP server + skills configured (
@iblai/mcpin.mcp.json) iblai.envpopulated withPLATFORM,DOMAIN,TOKEN. If missing, tell the user to download the template:curl -o iblai.env https://raw.githubusercontent.com/iblai/vibe/refs/heads/main/iblai.env
Create the Bell
Render assets/notification-bell.tsx.j2
into components/iblai/notification-bell.tsx (substitute {{ }}
placeholders) β a bell with unread badge. Reads userData and tenant from
localStorage; returns null when unauthenticated.
Components
Use MCP for full props/examples:
get_component_info("NotificationDropdown")
get_component_info("NotificationDisplay")
<NotificationDropdown> β bell icon for the navbar
| Prop | Type | Description |
|---|---|---|
org | string | Platform key |
userId | string | Username |
onViewNotifications | (id?) => void? | "View all" callback |
className | string? | Additional CSS class |
<NotificationDisplay> β full notification center page
| Prop | Type | Description |
|---|---|---|
org | string | Platform key |
userId | string | Username |
isAdmin | boolean? | Shows Alerts tab + Send button |
selectedNotificationId | string? | Pre-select a notification |
enableRbac | boolean? | Enable RBAC permission checks |
Derive isAdmin from the tenants array in localStorage.
Roles
| Feature | Everyone | Admin |
|---|---|---|
| Inbox, mark as read | β | β |
| Send notification, Alerts tab | β |
Verify
Run /iblai-vibe-ops-test before reporting done:
pnpm build && pnpm test, then touch test /notifications and the navbar bell
in the browser as /iblai-vibe-ops-test says.
Notes
- Import from
@iblai/iblai-js/web-containers(framework-agnostic). - Redux store must include
mentorReducerandmentorMiddleware. initializeDataLayer()takes 5 args (data-layer v1.2+).@reduxjs/toolkitdeduped via webpack aliases innext.config.ts.
Notification REST API
Base URL:
${dmUrl}/api/notification/v1/βdmUrlis the first arg passed toinitializeDataLayer()(sourced fromNEXT_PUBLIC_API_BASE_URL). Auth:Authorization: Token <token>Live OpenAPI:${dmUrl}/api/docs/schema/swagger-ui/
The SDK's RTK Query hooks (useGetUserNotificationsQuery,
useGetNotificationCountQuery, useUpdateNotificationStatusMutation,
useMarkAllNotificationsReadMutation, β¦) wrap most endpoints. Drop down
to raw HTTP only for admin endpoints not yet exposed as hooks (template
PATCH, builder send, SMTP test). Discover hooks via
get_api_query_info(...) in MCP. {org} and {platform_key} are the
same value (the platform slug).
User notifications
| Method | Path | Purpose |
|---|---|---|
GET | /orgs/{org}/users/{user_id}/notifications/ | List user notifications. Query: status, channel, exclude_channel, start_date, end_date, page |
PUT | /orgs/{org}/users/{user_id}/notifications/ | Update status. Body: {notification_id: "uuid,uuid", status: "READ|UNREAD|CANCELLED"} |
PATCH | /orgs/{org}/users/{user_id}/notifications/bulk-update/ | Set status on all user notifications. Body: {status} |
DELETE | /orgs/{org}/users/{user_id}/notifications/{id}/ | Delete one notification |
GET | /orgs/{org}/users/{user_id}/notifications-count/ | Count. Query: status, channel. Returns {count} |
POST | /orgs/{platform_key}/mark-all-as-read | Mark all (or notification_ids[]) read |
GET | /orgs/{org}/notifications/ / PUT / PATCH .../bulk-update/ | Org-wide variants (admin) |
Status values: READ, UNREAD, CANCELLED. Notification record:
{id, username, title, body, status, channel, context, short_message, created_at, updated_at}.
Templates (admin)
Templates inherit defaults; the first PATCH clones the default into a
platform-specific copy. reset deletes the override (toggle state is
preserved).
| Method | Path | Purpose |
|---|---|---|
GET | /platforms/{platform_key}/templates/ | List templates (system + custom) |
GET | /platforms/{platform_key}/templates/{type}/ | Template detail |
PATCH | /platforms/{platform_key}/templates/{type}/ | Customize template |
PATCH | /platforms/{platform_key}/templates/{type}/toggle/ | Body: {allow_notification: bool} |
POST | /platforms/{platform_key}/templates/{type}/reset/ | Revert to default |
POST | /platforms/{platform_key}/templates/{type}/test/ | Send test email. Body: {context?} |
Writable on PATCH: name, description, message_title,
message_body, short_message_body, email_subject,
email_from_address, email_html_template, spa_ids[], channel_ids[].
For PROACTIVE_LEARNER_NOTIFICATION, POLICY_ASSIGNMENT, and
HUMAN_SUPPORT_NOTIFICATION only their config fields are writable
(content is read-only). See "Special types" below.
Templates use Django syntax ({{ var }}, {% if %}, {% for %}).
email_html_template is sanitized by bleach β <script>, <iframe>,
<form>, event-handler attributes, and javascript: URLs are stripped.
Notification types
System types include: USER_NOTIF_USER_REGISTRATION,
APP_REGISTRATION, USER_NOTIF_COURSE_ENROLLMENT,
USER_NOTIF_COURSE_COMPLETION, USER_NOTIF_CREDENTIALS,
USER_NOTIF_LEARNER_PROGRESS, USER_NOTIF_USER_INACTIVITY,
PLATFORM_INVITATION, COURSE_INVITATION, PROGRAM_INVITATION,
COURSE_LICENSE_ASSIGNMENT, COURSE_LICENSE_GROUP_ASSIGNMENT,
PROGRAM_LICENSE_ASSIGNMENT, PROGRAM_LICENSE_GROUP_ASSIGNMENT,
USER_LICENSE_ASSIGNMENT, USER_LICENSE_GROUP_ASSIGNMENT,
ROLE_CHANGE, ADMIN_NOTIF_COURSE_ENROLLMENT, POLICY_ASSIGNMENT,
HUMAN_SUPPORT_NOTIFICATION, PROACTIVE_LEARNER_NOTIFICATION,
REPORT_COMPLETED, CUSTOM_NOTIFICATION,
ACTIVITY_COURSE_MILESTONE, ACTIVITY_NEW_CONTENT,
COURSES_PROGRESS_SUMMARY, PATHWAY_ENROLLMENT_CONFIRMATION,
PROGRAM_ENROLLMENT_CONFIRMATION, SKILL_MASTERY_CHANGE. Per-type
template variables are returned in available_context from the GET
detail endpoint.
Direct send (notification builder)
Admin-only. Flow: context β validate β preview β send.
| Method | Path | Purpose |
|---|---|---|
GET | /orgs/{platform_key}/notification-builder/context/ | Returns {templates[], channels[], sources[]} |
POST | /orgs/{platform_key}/notification-builder/validate_source/ | Body: {type, data}. Returns valid/invalid counts + sample |
POST | /orgs/{platform_key}/notification-builder/preview/ | Returns {build_id, count, warning, recipients[]} |
GET | /orgs/{platform_key}/notification-builder/{build_id}/recipients/ | Paginated. Query: search, page, page_size |
POST | /orgs/{platform_key}/notification-builder/send/ | Body: {build_id} |
Preview body:
{
"template_id": "uuid", // OR template_data β not both
"template_data": { "message_title": "...", "message_body": "..." },
"channels": [1, 2],
"sources": [{ "type": "email", "data": "a@x.com,b@x.com" }],
"context": { "course_name": "Python 101" },
"process_on": "2026-05-01T09:00:00Z" // optional, for scheduled send
}
Source types: email, username, platform, csv (multipart with
file_0), department, pathway, program, usergroup. Recipients
are merged + deduped across sources. Duplicate suppression: SHA-256 hash
of recipients+template+channels β if matched within 24h, send returns
"Similar notifications found".
Push (FCM)
| Method | Path | Body |
|---|---|---|
POST | /orgs/{org}/users/{user_id}/register-fcm-token/ | {name, registration_id, active?, cloud_message_type?, application_id?} |
DELETE | /orgs/{org}/users/{user_id}/register-fcm-token/ | {registration_id} |
SMTP
| Method | Path | Purpose |
|---|---|---|
POST | /platforms/{platform_key}/config/test-smtp/ | Verify SMTP. Body: {smtp_host, smtp_port, smtp_username, smtp_password, use_tls?, use_ssl?, test_email, from_email?} |
use_tls (port 587) and use_ssl (port 465) are mutually exclusive.
Campaigns
| Method | Path | Purpose |
|---|---|---|
GET | /campaigns/unsubscribe/{unsubscribe_hash}/ | Public β unsubscribe link target |
POST | /orgs/{platform_key}/campaigns/enable/ | Re-enable. Body: {campaign_id, campaign_title, email} |
POST | /orgs/{platform_key}/campaigns/exclude/ | Opt-out. Body: same |
Special types (config-only PATCH)
HUMAN_SUPPORT_NOTIFICATION
{
"human_support_recipient_mode": "platform_admins_and_mentor_owner",
"human_support_custom_recipients": [
{ "type": "user", "id": 42 },
{ "type": "user_group", "id": 7 },
{ "type": "rbac_policy", "policy_name": "Support Staff" }
]
}
recipient_mode: platform_admins_and_mentor_owner (default),
platform_admins_only, mentor_owner_only, custom.
POLICY_ASSIGNMENT
{
"policy_notify_on_assignment": true,
"policy_notify_on_removal": true,
"policy_enabled_policies": [
{ "role_name": "Analytics Viewer", "enabled": true, "subject": "..." }
]
}
Empty policy_enabled_policies = global flags apply to all roles.
Non-empty = only listed roles trigger notifications.
PROACTIVE_LEARNER_NOTIFICATION
{
"periodic_frequency": "WEEKLY",
"periodic_report_period_days": 7,
"periodic_execution_time": "09:00",
"periodic_timezone": "UTC",
"periodic_learner_scope": "ACTIVE_LEARNERS",
"periodic_custom_interval_days": 7,
"periodic_mentors": [
{ "unique_id": "uuid", "prompt": "Summarize {{student_name}}'s progress.", "name": "Coach" }
]
}
frequency: DAILY, WEEKLY, MONTHLY, CUSTOM. learner_scope:
ACTIVE_LEARNERS or ALL_LEARNERS. Empty mentors = use all platform
agents.
RBAC permissions
Ibl.Notifications/Notification/{list,write,delete},
Ibl.Notifications/NotificationTemplate/{list,read,write},
Ibl.Notifications/SMTP/action, Ibl.Notifications/Campaigns/action.
Example
curl "${dmUrl}/api/notification/v1/orgs/acme-learning/users/jane.doe/notifications/?status=UNREAD" \
-H "Authorization: Token ${TOKEN}"
curl -X PATCH "${dmUrl}/api/notification/v1/platforms/acme-learning/templates/USER_NOTIF_COURSE_ENROLLMENT/" \
-H "Authorization: Token ${TOKEN}" -H "Content-Type: application/json" \
-d '{"email_subject": "Welcome to {{ course_name }}"}'