Repository navigation
API
Resolve services through Laravel's container. All owners/recipients are persisted Eloquent UUID models supplied by the application. The services enforce record ownership relative to that model; they do not authenticate a request or infer which model the caller is authorized to use.
Use a validated FormRequest in your authenticated route/controller. Validate installation_id as a stable installation-specific string, platform as android or ios, and token as a nonempty string. Limit token inputs to 4096 bytes and device names to 255 bytes. UUIDv4 is suitable for a client installation identifier. It identifies an installation, not the authenticated user.
use Moztopia\Notifications\Services\NotificationDeviceService;
$devices = app(NotificationDeviceService::class);
$device = $devices->register(
owner: $request->user(),
installationId: $request->validated('installation_id'),
platform: $request->validated('platform'),
token: $request->validated('token'),
name: $request->validated('name'),
);Send the same installation identifier and latest token to register() when the token rotates. Repeating an unchanged registration preserves its identity and generation. Token/scope changes increment generation. Android selects fcm; iOS selects apns. Clients cannot select an arbitrary provider or application environment.
Serialize only safe fields in the application's response resource, for example the registration UUID, platform, device name and push setting. Tokens/fingerprints are hidden by the model and must never be exposed in custom resources, request logs or error diagnostics.
$devices->setPushEnabled($request->user(), $deviceId, false);
$devices->remove($request->user(), $deviceId);On logout, unregister before invalidating the application's authenticated session. Removal revokes the token, releases its fingerprint and increments generation. Retaining a revoked registration protects queued work from accidental reuse.
An installation owned by another user cannot be silently registered. For an account switch, complete authenticated logout/unregistration while the old session is available. Explicit reassignment is available only to application-authorized flows:
use Illuminate\Support\Facades\Gate;
Gate::authorize('transfer-notification-device', [$currentOwner, $deviceId, $newOwner]);
$device = $devices->reassign($currentOwner, $deviceId, $newOwner);The host defines this ability and authenticates the acting principal. Never obtain either owner model from untrusted request IDs without authorization.
use Moztopia\Notifications\NotificationPayload;
use Moztopia\Notifications\Services\NotificationService;
$notifications = app(NotificationService::class);
$payload = new NotificationPayload(
title: 'An update is available',
body: 'Open the app to view it.',
category: 'updates',
deepLink: 'exampleapp://notifications',
data: ['resource_id' => $resourceUuid],
priority: 'normal',
collapseId: 'updates',
ttl: 3600,
);
$message = $notifications->send($recipient, $payload, idempotencyKey: 'update:'.$eventUuid);
$messages = $notifications->sendMany($recipients, $payload, idempotencyKey: 'update:'.$eventUuid);
$message = $notifications->send($recipient, $payload, deviceId: $deviceId);
$message = $notifications->send($recipient, $payload, immediate: true);send() returns the internal NotificationMessage, or null when global/category settings suppress creation. A repeat application/recipient/event key returns the original identity without adding history or intents. Reusing a key with a changed payload or target does not replace the original event. No key means a new notification. sendMany() accepts an iterable; each recipient commits independently, so a partial bulk failure can be safely resubmitted with the same event key.
The default first attempt is queued; select a durable connection in production. Immediate mode attempts after commit and retains transient failures for scheduled recovery. Calling it inside an outer transaction still defers submission until that transaction commits. It can block the caller on provider latency. It does not imply device receipt.
Optional imageUrl requires HTTP(S). Custom data must be JSON-serializable; the complete neutral payload has a conservative 2800-byte budget. Priority is normal or high, collapse identifiers have a 64-byte maximum, and TTL is 1–2419200 seconds. APNs rich images also require a client notification service extension; a URL alone cannot force the OS to display an image.
Always dispatch through NotificationService. NotificationPushMessage is an internal Laravel notification adapter and rejects normal $user->notify() channel selection so callers cannot silently bypass delivery tracking.
use Moztopia\Notifications\Services\NotificationPreferenceService;
$preferences = app(NotificationPreferenceService::class);
$current = $preferences->get($request->user());
$preferences->update($request->user(), enabled: false);
$preferences->update($request->user(), pushEnabled: false);
$preferences->update($request->user(), categories: ['updates' => false]);
$preferences->update($request->user(), pushCategories: ['messages' => false]);Omitted settings (null) remain unchanged. Each supplied categories or pushCategories map independently replaces its previous map; [] clears its overrides. Keys must be nonempty category strings of at most 120 bytes and values must be booleans. Unspecified categories default to allowed, subject to every other eligibility check. Category identifiers are application-defined strings, not a hardcoded enumeration. Defaults enable notifications and push.
Global/category disablement suppresses new history and push. Global push-only, per-category pushCategories and device-only disablement preserve history. Server-owned push_categories configuration can forbid category push even if a recipient enables it; see Configuration. No active devices also preserves history. Workers recheck current preferences; disablement after creation suppresses pending push but leaves existing history. Skipped deliveries are terminal and are not automatically resurrected when settings are reenabled.
use Moztopia\Notifications\Services\NotificationHistoryService;
$history = app(NotificationHistoryService::class);
$page = $history->paginate($request->user(), perPage: 20);
$unread = $history->paginate($request->user(), unreadOnly: true);
$count = $history->unreadCount($request->user());
$history->markAsRead($request->user(), $notificationId);
$history->markAsUnread($request->user(), $notificationId);
$history->markAllAsRead($request->user());
$history->delete($request->user(), $notificationId);History models and collections retain Laravel's database-notification read/unread behavior. Query operations are scoped to application, recipient type and UUID. Access to another recipient's UUID fails as not found. The service clamps page sizes to 1–100.
Use the service's delete() operation for cancellation. Calling Eloquent delete() directly or deleting raw history rows bypasses lifecycle coordination. A delete removes visible history, cancels pending/retryable intents, scrubs the internal payload and retains event metadata through the deduplication window. Accepted submissions cannot be recalled.
Moztopia Notifications
One notification per recipient. Independent delivery to every device.
Documentation · Source · Report an issue · MIT License
© 2026 Moztopia. Distributed under the MIT License.
