Best use case
iblai-notification is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Add notification bell and center page to your Next.js app
Teams using iblai-notification should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
$curl -o ~/.claude/skills/iblai-notification/SKILL.md --create-dirs "https://raw.githubusercontent.com/iblai/vibe/main/skills/iblai-notification/SKILL.md"
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/iblai-notification/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How iblai-notification Compares
| Feature / Agent | iblai-notification | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Add notification bell and center page to your Next.js app
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# /iblai-notification
Add notification features -- a compact bell icon with unread badge for your
navbar and a full notification center page with Inbox and Alerts tabs.

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-navbar` first.
## 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-starter/tree/spa) already ships the
> notification bell and /notifications page wired up, alongside auth, navbar,
> and profile/account. Want to use that instead?
If yes, clone into a temp directory and copy into the current directory before
installing (running pnpm install inside the cloned subdirectory causes hardlink
issues), then skip this skill:
git clone -b spa https://github.com/iblai/vibe-starter.git vibe-starter-init
cp -a vibe-starter-init/. . && rm -rf vibe-starter-init
pnpm install
If they prefer to add notifications to an existing app, continue below.
## Prerequisites
- Auth set up (`/iblai-auth`)
- MCP and skills set up (`iblai add mcp`)
- `iblai.env` populated with `PLATFORM`, `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`
## Generate
```bash
iblai --version # upgrade if outdated: pip install --upgrade iblai-app-cli OR npm i -g @iblai/cli@latest
iblai add notification
```
Generated: `components/iblai/notification-bell.tsx` — 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-ops-test` before reporting done:
```bash
pnpm build && pnpm test
pnpm dev &
npx playwright screenshot http://localhost:3000/notifications /tmp/notifications.png
```
## Notes
- Import from `@iblai/iblai-js/web-containers` (framework-agnostic).
- Redux store must include `mentorReducer` and `mentorMiddleware`.
- `initializeDataLayer()` takes 5 args (data-layer v1.2+).
- `@reduxjs/toolkit` deduped via webpack aliases in `next.config.ts`.
---
# Notification REST API
> **Base URL:** `${dmUrl}/api/notification/v1/` — `dmUrl` is the first
> arg passed to `initializeDataLayer()` (sourced from
> `NEXT_PUBLIC_API_BASE_URL`).
> **Auth:** `Authorization: Token <token>`
> **Live OpenAPI:** <https://api.iblai.app/dm/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:
```json
{
"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`
```json
{
"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`
```json
{
"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`
```json
{
"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
mentors.
## RBAC permissions
`Ibl.Notifications/Notification/{list,write,delete}`,
`Ibl.Notifications/NotificationTemplate/{list,read,write}`,
`Ibl.Notifications/SMTP/action`, `Ibl.Notifications/Campaigns/action`.
## Example
```bash
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 }}"}'
```Related Skills
We are still matching the closest adjacent skills for this page. In the meantime, continue through the full directory.