Skip to main content
POST
/
api
/
notifications
/
subscribe
Subscribe to Push Notifications
curl --request POST \
  --url https://api.example.com/api/notifications/subscribe \
  --header 'Content-Type: application/json' \
  --data '
{
  "subscription": {
    "endpoint": "<string>",
    "keys": {
      "p256dh": "<string>",
      "auth": "<string>"
    }
  },
  "userId": "<string>"
}
'
{
  "success": true,
  "message": "<string>",
  "notificationsEnabled": true,
  "400 Bad Request": {}
}

Overview

This endpoint saves or updates a push notification subscription for a user. It stores the Web Push subscription object (including VAPID keys) and automatically enables notifications for the user.

Authentication

This endpoint supports two authentication methods:
  • Recommended: JWT authentication via req.user (authenticated session)
  • Fallback: userId in request body

Request Body

subscription
object
required
Web Push subscription object obtained from the browser’s Push API
userId
string
MongoDB ObjectId of the user (only required if not authenticated via JWT)

Response

success
boolean
Indicates whether the subscription was saved successfully
message
string
Human-readable confirmation message
notificationsEnabled
boolean
Confirms that notifications are now enabled for the user

Behavior

  • Uses findOneAndUpdate with upsert: true to handle both new subscriptions and updates
  • Multiple devices per user are supported (unique index on userId + endpoint)
  • Automatically sets notificationsEnabled: true in the User model
  • If the subscription already exists, it will be updated with new keys
// First, get the VAPID public key
const response = await fetch('/api/notifications/vapid-public-key');
const { publicKey } = await response.json();

// Request permission and subscribe
const permission = await Notification.requestPermission();

if (permission === 'granted') {
  const registration = await navigator.serviceWorker.ready;
  
  const subscription = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(publicKey)
  });
  
  // Send subscription to backend
  await fetch('/api/notifications/subscribe', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}` // JWT token
    },
    body: JSON.stringify({
      subscription: subscription.toJSON()
    })
  });
}

// Helper function
function urlBase64ToUint8Array(base64String) {
  const padding = '='.repeat((4 - base64String.length % 4) % 4);
  const base64 = (base64String + padding)
    .replace(/\-/g, '+')
    .replace(/_/g, '/');
  const rawData = window.atob(base64);
  return Uint8Array.from([...rawData].map(char => char.charCodeAt(0)));
}

Response Example

{
  "success": true,
  "message": "Suscripción guardada exitosamente",
  "notificationsEnabled": true
}

Error Responses

400 Bad Request
error
Missing required subscription object or userId
{
  "error": "Falta el objeto subscription en el body"
}
400 Bad Request
error
Missing authentication
{
  "error": "Se requiere autenticación o userId en el body"
}

Database Schema

The subscription is stored in the PushSubscription collection with the following structure:
{
  userId: ObjectId,        // Reference to User model
  endpoint: String,        // Push service endpoint URL
  keys: {
    p256dh: String,       // Public key for encryption
    auth: String          // Auth secret
  },
  createdAt: Date         // Timestamp of subscription
}

Build docs developers (and LLMs) love