Add push notifications to your web games in minutes
Notification Kit is a TypeScript library that implements the Push Notification Protocol from the Open Game System (OGS) specification. It enables web games to send push notifications to users across multiple platforms, including iOS, Android, and web browsers.
- π Cross-Platform Notifications - Reach users on iOS, Android, and web
- π Status Tracking - Monitor delivery and read status
- π― Segmentation - Target specific user groups
- π§© Rich Media - Support for images and action buttons
- π Easy Integration - Simple API for sending notifications
- πͺ React Support - Ready-to-use React components and hooks
Notification Kit streamlines the process of sending push notifications from your game to players, even when they're not actively playing. The library handles:
- Sending Notifications: Easily trigger notifications from your server
- Status Tracking: Monitor when notifications are delivered and read
- Device Management: Register/unregister user devices
- User Preferences: Handle notification opt-in/opt-out
Notification Kit follows the Push Notification Protocol from the OGS specification, providing a client-server architecture:
flowchart TD
subgraph "Game Server"
GameBackend[Game Backend]
ServerSDK[Server SDK]
end
subgraph "Game Client"
ClientSDK[Client SDK]
DeviceRegistration[Device Registration]
end
subgraph "OGS Platform"
OGSApi[OGS API]
PushService[Push Service]
end
subgraph "User Devices"
AppleDevice[iOS Devices]
AndroidDevice[Android Devices]
WebBrowser[Web Browsers]
end
GameBackend -->|Uses| ServerSDK
ServerSDK -->|Sends notification| OGSApi
ClientSDK -->|Registers device| OGSApi
DeviceRegistration -->|Stores token| PushService
PushService -->|Delivers to| AppleDevice
PushService -->|Delivers to| AndroidDevice
PushService -->|Delivers to| WebBrowser
- Account Linking - Users should have OGS accounts linked to your game
- OGS API Key - Obtain through the certification process
- OGS Mobile App - Required for iOS/Android push notifications
npm install @open-game-system/notification-kitimport { createNotificationClient } from '@open-game-system/notification-kit/server';
// Create a client with your API key
const notificationClient = createNotificationClient({
apiKey: 'YOUR_OGS_API_KEY'
});
// Send a notification
async function sendGameInvitation(recipientId, inviterName, gameId) {
try {
const result = await notificationClient.sendNotification({
recipient: {
gameUserId: recipientId
},
notification: {
type: 'game_invitation',
title: 'New Game Invitation',
body: `${inviterName} invited you to play!`,
data: {
gameId: gameId,
inviterId: 'user-123',
inviterName: inviterName
},
deepLink: `opengame://your-game/join/${gameId}`
}
});
console.log('Notification sent:', result.id);
return result;
} catch (error) {
console.error('Failed to send notification:', error);
throw error;
}
}import { NotificationProvider, useNotifications } from '@open-game-system/notification-kit/react';
// Wrap your app with the provider
function App() {
return (
<NotificationProvider>
<YourGame />
</NotificationProvider>
);
}
// Use the hook in your components
function NotificationSettings() {
const {
isPermissionGranted,
requestPermission,
registerDevice,
unregisterDevice,
deviceId
} = useNotifications();
const handleToggle = async (enabled) => {
if (enabled) {
// Request permission if not granted
if (!isPermissionGranted) {
const granted = await requestPermission();
if (!granted) return;
}
// Register device
await registerDevice({
topics: ['game_invitation', 'turn_notification']
});
} else if (deviceId) {
// Unregister device
await unregisterDevice();
}
};
return (
<div>
<h2>Notification Settings</h2>
<label>
<input
type="checkbox"
checked={!!deviceId}
onChange={(e) => handleToggle(e.target.checked)}
/>
Enable Push Notifications
</label>
</div>
);
}// Create a client for the server
const client = createNotificationClient({
apiKey: string,
baseUrl?: string
});
// Client interface
interface NotificationClient {
// Send a notification to users
sendNotification(params: SendNotificationParams): Promise<SendNotificationResult>;
// Check notification status
getNotificationStatus(notificationId: string): Promise<NotificationStatus>;
// Get notification analytics for your game
getNotificationAnalytics(params?: AnalyticsParams): Promise<NotificationAnalytics>;
}
// Parameters for sending a notification
interface SendNotificationParams {
// Target recipient
recipient: {
// Your game's user ID
gameUserId: string;
// or Target a specific device
deviceId?: string;
// or Target a topic
topic?: string;
};
// Notification content
notification: {
// Notification type
type: string;
// Notification title
title: string;
// Notification body
body: string;
// Optional image URL
imageUrl?: string;
// Custom data to include
data?: Record<string, any>;
// Deep link URL
deepLink?: string;
// Actions that user can take
actions?: Array<{
id: string;
title: string;
deepLink?: string;
}>;
// When notification should expire
expiresAt?: string;
};
}
// Result of sending a notification
interface SendNotificationResult {
// Notification ID
id: string;
// Delivery status
status: 'queued' | 'delivered' | 'failed';
// Timestamp
timestamp: string;
}
// Notification status details
interface NotificationStatus {
// Notification ID
id: string;
// Current status
status: 'queued' | 'delivered' | 'read' | 'failed';
// When notification was created
createdAt: string;
// When notification was delivered
deliveredAt?: string;
// When notification was read
readAt?: string;
// Error details if failed
error?: {
code: string;
message: string;
};
}// React hook for notifications
const {
// Whether permission is granted
isPermissionGranted: boolean,
// Request permission from user
requestPermission: () => Promise<boolean>,
// Register this device for notifications
registerDevice: (params: RegisterDeviceParams) => Promise<RegisterDeviceResult>,
// Unregister this device
unregisterDevice: () => Promise<boolean>,
// Current device ID (if registered)
deviceId: string | null,
// Current notification preferences
preferences: {
topics: string[]
},
// Update notification preferences
updatePreferences: (params: UpdatePreferencesParams) => Promise<boolean>,
// Loading state
isLoading: boolean,
// Error state
error: Error | null
} = useNotifications();
// Parameters for registering a device
interface RegisterDeviceParams {
// Notification topics to subscribe to
topics?: string[];
}
// Result of registering a device
interface RegisterDeviceResult {
// Device ID
deviceId: string;
// Success status
success: boolean;
}
// Parameters for updating preferences
interface UpdatePreferencesParams {
// Topics to subscribe to
topics: string[];
}Create reusable notification templates:
// Define a template
const templates = {
gameInvitation: (inviterName, gameId, gameName) => ({
type: 'game_invitation',
title: 'New Game Invitation',
body: `${inviterName} invited you to play ${gameName}!`,
data: {
gameId,
inviterName,
gameName
},
deepLink: `opengame://your-game/join/${gameId}`
}),
turnNotification: (gameName) => ({
type: 'turn_notification',
title: 'Your Turn',
body: `It's your turn in ${gameName}!`,
data: {
gameName
}
})
};
// Use the template
await notificationClient.sendNotification({
recipient: { gameUserId: 'user-123' },
notification: templates.gameInvitation('Alice', 'game-456', 'Chess')
});Send notifications to multiple users:
async function sendBatchNotifications(userIds, notification) {
const results = await Promise.allSettled(
userIds.map(userId =>
notificationClient.sendNotification({
recipient: { gameUserId: userId },
notification
})
)
);
return {
sent: results.filter(r => r.status === 'fulfilled').length,
failed: results.filter(r => r.status === 'rejected').length,
results
};
}Build custom components:
import { useNotifications } from '@open-game-system/notification-kit/react';
function CustomNotificationToggle() {
const { deviceId, registerDevice, unregisterDevice, isLoading } = useNotifications();
return (
<button
className="notification-button"
disabled={isLoading}
onClick={async () => {
if (deviceId) {
await unregisterDevice();
} else {
await registerDevice({
topics: ['game_invitation', 'game_updates']
});
}
}}
>
{isLoading ? 'Loading...' : deviceId
? 'Disable Notifications'
: 'Enable Notifications'
}
</button>
);
}Monitor notification status and troubleshoot delivery issues:
// Get the status of a notification
const status = await notificationClient.getNotificationStatus('notification-123');
console.log('Notification status:', status);
// Enable debug mode
const notificationClient = createNotificationClient({
apiKey: 'YOUR_API_KEY',
debug: true
});
// Debug logs will be output to consoleEnable debug mode in the provider:
<NotificationProvider debug={true}>
<YourApp />
</NotificationProvider>- Keep titles under 50 characters
- Keep bodies under 150 characters
- Include clear call-to-action
- Personalize when possible
- Use rich media sparingly (affects delivery speed)
- Respect user time zones
- Avoid sending multiple notifications in short periods
- Use
expiresAtfor time-sensitive notifications
- Always make notifications opt-in
- Provide clear settings controls
- Explain the value of notifications
- Respect user preferences
- Group related notifications when possible
If users deny notification permission:
const { isPermissionGranted, requestPermission } = useNotifications();
// Check if permission was previously denied
if (!isPermissionGranted) {
// Show explanation about notification benefits
showNotificationExplanation();
// Then request permission
const granted = await requestPermission();
if (granted) {
// Permission granted, register device
} else {
// Permission denied, show instructions on how to enable manually
}
}Check notification status:
const status = await notificationClient.getNotificationStatus(notificationId);
if (status.status === 'failed') {
console.error('Delivery failed:', status.error);
// Handle the specific error
}- GitHub Issues: https://github.com/open-game-system/notification-kit/issues
- Discord Community: Join our Discord
- Email Support: hello@opengame.org
Notification Kit is licensed under the MIT License - see the LICENSE file for details.
Built with β€οΈ by the Open Game Collective