Skip to content

Notifications

Moon ERP includes an in-app notification system built on Laravel's native notification infrastructure. Notifications are stored in the database and delivered to individual users. The API provides endpoints for listing notifications, marking them as read, and checking unread counts.

Purpose

  • Deliver real-time system notifications to users (e.g., journal entry approved, budget submitted)
  • Support read/unread status tracking per notification
  • Provide an unread count for badge display in the UI
  • Enable bulk "mark all as read" functionality

Entity Attributes

Notifications use Laravel's built-in notifications table:

FieldTypeDescription
iduuidPrimary key (UUID)
typestringNotification class name
notifiable_typestringAlways App\Models\User
notifiable_idintegerThe user ID
datajsonNotification payload (title, message, metadata)
read_atdatetime?When the notification was read (null = unread)
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp

Notification Data Structure

The data JSON field typically contains:

KeyTypeDescription
titlestringNotification title
title_arstringNotification title (Arabic)
messagestringNotification body text
message_arstringNotification body text (Arabic)
modulestring?Source module (e.g., accounting, core)
actionstring?Action type (e.g., journal_approved, budget_submitted)
entity_typestring?Related entity class
entity_idinteger?Related entity ID

Relationships

API Endpoints

MethodPathDescription
GET/api/core/notificationsList notifications for the authenticated user
POST/api/core/notifications/{id}/readMark a specific notification as read
POST/api/core/notifications/read-allMark all notifications as read
GET/api/core/notifications/unread-countGet the count of unread notifications

Request/Response Examples

GET /api/core/notifications

Retrieve a paginated list of notifications for the authenticated user.

bash
curl -X GET https://moon-erp.test/api/core/notifications \
  -H "Accept: application/json" \
  -H "Accept-Language: ar" \
  -H "Authorization: Bearer {token}"
dart
final response = await http.get(
  Uri.parse('https://moon-erp.test/api/core/notifications'),
  headers: {
    'Accept': 'application/json',
    'Accept-Language': 'ar',
    'Authorization': 'Bearer $token',
  },
);

Response 200 OK

json
{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "type": "Modules\\Accounting\\Notifications\\JournalApproved",
      "data": {
        "title": "Journal Entry Approved",
        "title_ar": "تم اعتماد قيد اليومية",
        "message": "Journal entry JE-2026-00015 has been approved",
        "message_ar": "تم اعتماد قيد اليومية JE-2026-00015",
        "module": "accounting",
        "action": "journal_approved",
        "entity_type": "Modules\\Accounting\\Models\\JournalEntry",
        "entity_id": 15
      },
      "read_at": null,
      "created_at": "2026-02-16T10:00:00.000000Z"
    },
    {
      "id": "660f9511-f30c-52e5-b827-557766551111",
      "type": "Modules\\Core\\Notifications\\UserCreated",
      "data": {
        "title": "New User Added",
        "title_ar": "تمت إضافة مستخدم جديد",
        "message": "User Fatima Hassan has been added to the system",
        "message_ar": "تمت إضافة المستخدمة فاطمة حسن إلى النظام",
        "module": "core",
        "action": "user_created"
      },
      "read_at": "2026-02-16T09:30:00.000000Z",
      "created_at": "2026-02-15T14:00:00.000000Z"
    }
  ],
  "links": { "first": "...?page=1", "last": "...?page=1", "prev": null, "next": null },
  "meta": { "current_page": 1, "from": 1, "last_page": 1, "per_page": 25, "to": 2, "total": 2 }
}

GET /api/core/notifications/unread-count

Get the number of unread notifications for badge display.

bash
curl -X GET https://moon-erp.test/api/core/notifications/unread-count \
  -H "Accept: application/json" \
  -H "Authorization: Bearer {token}"
dart
final response = await http.get(
  Uri.parse('https://moon-erp.test/api/core/notifications/unread-count'),
  headers: {
    'Accept': 'application/json',
    'Authorization': 'Bearer $token',
  },
);

final count = jsonDecode(response.body)['unread_count'];
// Use count to display badge: e.g., "3"

Response 200 OK

json
{
  "unread_count": 3
}

POST /api/core/notifications/{id}/read

Mark a single notification as read.

bash
curl -X POST https://moon-erp.test/api/core/notifications/550e8400-e29b-41d4-a716-446655440000/read \
  -H "Accept: application/json" \
  -H "Authorization: Bearer {token}"
dart
final response = await http.post(
  Uri.parse('https://moon-erp.test/api/core/notifications/550e8400-e29b-41d4-a716-446655440000/read'),
  headers: {
    'Accept': 'application/json',
    'Authorization': 'Bearer $token',
  },
);

Response 200 OK

json
{
  "message": "Marked as read"
}

POST /api/core/notifications/read-all

Mark all unread notifications as read for the authenticated user.

bash
curl -X POST https://moon-erp.test/api/core/notifications/read-all \
  -H "Accept: application/json" \
  -H "Authorization: Bearer {token}"
dart
final response = await http.post(
  Uri.parse('https://moon-erp.test/api/core/notifications/read-all'),
  headers: {
    'Accept': 'application/json',
    'Authorization': 'Bearer $token',
  },
);

final count = jsonDecode(response.body)['count'];
// e.g., "5 notifications marked as read"

Response 200 OK

json
{
  "message": "All notifications marked as read",
  "count": 5
}

Notification Flow

Business Rules

  • User scoping -- notifications are always scoped to the authenticated user. A user can only see and manage their own notifications.
  • UUID identifiers -- notification IDs are UUIDs, not integers. Always use string type when referencing notification IDs.
  • Read status -- a notification is considered unread when read_at is null. Marking it as read sets read_at to the current timestamp.
  • Idempotent read -- marking an already-read notification as read again is safe and does not produce an error.
  • Bulk read -- the read-all endpoint marks all unread notifications as read in a single operation and returns the count of notifications that were marked.
  • Bilingual content -- notification payloads should include both title / message and title_ar / message_ar. The client can choose which to display based on the user's locale preference.
  • No delete endpoint -- notifications cannot be deleted through the API. They persist in the database for audit trail purposes.
  • Polling recommended -- the client should periodically poll GET /api/core/notifications/unread-count to update the notification badge. Consider polling every 30-60 seconds.

Moon ERP API Documentation