Programmatic Notifications
Learn how to programmatically dispatch secure, real-time push notifications to Campus One users from your connected applications.
Programmatic Notifications API
Connected applications can dispatch transactional notifications directly to their authorized users' Campus One app shell in real-time. This guide details the integration flow, required authentication scopes, REST API specifications, and standard code examples.
Architectural Flow
The notifications mechanism follows a secure OAuth/OIDC authorization model. Connected apps must be granted the notifications scope by the user during sign-in to dispatch messages.
Authentication & Authorization
All notification requests require standard OIDC User Access Tokens.
- Required Scope:
notifications - Method: Bearer Token Authentication
- Header format:
Authorization: Bearer <access_token> - Admin prerequisite: The
permNotificationsflag must be enabled for your app (it is on by default) under Permissions in the developer dashboard.
When a connected app sends a notification, the recipient user and origin app are securely resolved from the OIDC Access Token record (userId is mapped to the token's authenticated owner, and appId is mapped to the registered client application). Therefore, connected apps do not need to supply a userId in the body payload—preventing any unauthorized spoofing of notifications.
[!WARNING] Access tokens are verified in real-time against active sessions. If the token is expired, revoked, or does not contain the
notificationsscope, the gateway immediately returns401 Unauthorizedor403 Forbidden.
REST API Reference
Send Notification
Send a push notification to a specific user.
POST /api/apps/notifications
Headers
| Header | Type | Description |
|---|---|---|
Authorization | string | Required. Bearer <access_token> containing the notifications scope. |
Content-Type | string | Required. Must be application/json. |
Idempotency-Key | string | Optional. Supply a unique key to make retries safe — a repeated key returns the original notification instead of creating a duplicate. Scoped per app, expires after 24 hours. |
Request Payload
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | The notification title (Maximum 128 characters). |
body | string | Yes | Detailed notification body message (Maximum 512 characters). |
type | string | No | Color theme identifier. Can be info, success, warning, or action_required. (Default: info). |
targetUrl | string | No | Destination deep-link/URL. Opens in a new secure tab when the user clicks the notification's primary action button. |
[!NOTE]
action_requiredsurfaces the notification prominently in the student dashboard's Action Required carousel — use it for time-sensitive items that need the student to take an explicit action (e.g. clearance steps, outstanding fees, consent forms). All other types (info,success,warning) appear only in the Recent Activity feed.
Example Integration
curl -X POST https://auth.campusone.com.ng/api/apps/notifications \
-H "Authorization: Bearer c1_act_abc123xyz" \
-H "Content-Type: application/json" \
-d '{
"title": "Assignment Graded",
"body": "Your submission for CSC 401 has been reviewed. Grade: A-",
"type": "success",
"targetUrl": "https://portal.nileuniversity.edu.ng/courses/csc401"
}'const sendNotification = async () => {
const response = await fetch('https://auth.campusone.com.ng/api/apps/notifications', {
method: 'POST',
headers: {
'Authorization': 'Bearer c1_act_abc123xyz',
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'Assignment Graded',
body: 'Your submission for CSC 401 has been reviewed. Grade: A-',
type: 'success',
targetUrl: 'https://portal.nileuniversity.edu.ng/courses/csc401'
})
});
if (!response.ok) {
const err = await response.json();
throw new Error(`Failed to send notification: ${err.message}`);
}
const result = await response.json();
console.log('Notification dispatched:', result.id);
};import requests
headers = {
"Authorization": "Bearer c1_act_abc123xyz",
"Content-Type": "application/json"
}
payload = {
"title": "Assignment Graded",
"body": "Your submission for CSC 401 has been reviewed. Grade: A-",
"type": "success",
"targetUrl": "https://portal.nileuniversity.edu.ng/courses/csc401"
}
res = requests.post(
"https://auth.campusone.com.ng/api/apps/notifications",
json=payload,
headers=headers
)
if res.status_code == 200:
print("Notification sent successfully! ID:", res.json()["id"])
else:
print("Failed to dispatch:", res.status_code, res.text)Response Schemas
[!NOTE] The live, always-accurate schemas are published at the interactive API reference. The examples below are illustrative.
200 OK
Returned when the notification is created. The response body is the full created Notification record (the id, unread state and timestamps are server-generated).
{
"id": "clh83kf1a0001st09k4m2p9zx",
"userId": "clx8z2abc1230009ab12cd34",
"appId": "clw7a1def4560002gh56ij78",
"title": "Assignment Graded",
"body": "Your submission for CSC 401 has been reviewed. Grade: A-",
"type": "success",
"unread": true,
"targetUrl": "https://portal.nileuniversity.edu.ng/courses/csc401",
"createdAt": "2026-05-28T10:00:00.000Z",
"updatedAt": "2026-05-28T10:00:00.000Z"
}Error responses
A 401 (authentication) is rejected at the gateway and returns { "error": "..." }. A 400 (validation) or 403 (authorization) comes from the endpoint and returns the richer { code, status, message } object. See Status Codes & Errors for the canonical reference.
{ "defined": false, "code": "FORBIDDEN", "status": 403, "message": "Token lacks 'notifications' scope" }| Status | Body | Cause |
|---|---|---|
400 | { code, status, message, data } | Payload fails validation (missing field, string too long, invalid type or targetUrl). |
401 | { error } | Bearer token missing, malformed, expired, revoked, or not bound to a user. |
403 | { code, status, message } | Token lacks the notifications scope, or the app's permNotifications flag is disabled. |