Skip to main content

Overview

For clients unable to maintain persistent WebSocket connections, the REST polling endpoint provides an alternative notification retrieval mechanism. This approach supports battery-constrained devices and simplified client implementations.

Fetch Pending Notifications

Endpoint: GET /api/v1/client/notifications
Authentication: Requires Bearer token using user credentials

Query Parameters

ParameterTypeRequiredDescription
statusstringNoFilter by notification state, e.g., “pending” (default: all)
limitintegerNoMaximum notifications to return (default: 50, max: 100)
cursorstringNoPagination cursor from previous response
service_idstringNoFilter by originating service ID
projectstringNoFilter by project identifier
sortstringNoSort order, either “newest” or “oldest” (default: “newest”)

Response

{
  "notifications": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "version": "1.0",
      "timestamp": "2025-05-25T10:30:00Z",
      "deadline": "2025-05-25T11:00:00Z",
      "service": {
        "id": "lovelace-ide",
        "name": "Lovelace IDE",
        "icon": "https://lovelace.dev/icon.png"
      },
      "context": {
        "title": "Deploy to Production?",
        "description": "New version 2.1.0 is ready for deployment to production servers.",
        "project": "backend-api",
        "metadata": {
          "version": "2.1.0",
          "changes": 47,
          "test_coverage": "98.3%"
        }
      },
      "actions": [
        {
          "id": "approve",
          "label": "Approve Deployment",
          "response_type": "simple",
          "flags": ["irreversible"]
        },
        {
          "id": "reject",
          "label": "Reject",
          "response_type": "text",
          "constraints": {
            "placeholder": "Reason for rejection"
          }
        }
      ],
      "status": "delivered"
    },
    // Additional notifications...
  ],
  "pagination": {
    "next_cursor": "cursor_for_next_page",
    "has_more": true,
    "total_count": 156
  }
}
The pagination object contains:
FieldTypeDescription
next_cursorstringCursor to use for fetching the next page
has_morebooleanIndicates if more results are available
total_countintegerTotal count of matching notifications
The pagination mechanism ensures efficient retrieval of large notification sets while maintaining consistent ordering.

Error Responses

Status CodeError CodeDescription
400 Bad RequestINVALID_PARAMETERInvalid query parameter
401 UnauthorizedAUTH_INVALID_TOKENUser token is invalid
429 Too Many RequestsRATE_LIMIT_EXCEEDEDRate limit exceeded

Fetch Single Notification

Endpoint: GET /api/v1/client/notifications/{id}
Authentication: Requires Bearer token using user credentials

Path Parameters

ParameterTypeDescription
idstringThe UUID of the notification to retrieve

Response

Returns a single notification object with the same structure as in the list endpoint.

Error Responses

Status CodeError CodeDescription
401 UnauthorizedAUTH_INVALID_TOKENUser token is invalid
403 ForbiddenNOTIFICATION_ACCESS_DENIEDUser does not have access to this notification
404 Not FoundNOTIFICATION_NOT_FOUNDNotification with specified ID does not exist

Acknowledge Notification

Endpoint: POST /api/v1/client/notifications/{id}/acknowledge
Authentication: Requires Bearer token using user credentials
This endpoint allows clients to acknowledge receipt of a notification, updating its status to acknowledged.

Path Parameters

ParameterTypeDescription
idstringThe UUID of the notification to acknowledge

Response

Status Code: 200 OK
{
  "notification_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "acknowledged",
  "acknowledged_at": "2025-05-25T10:32:15Z"
}

Error Responses

Status CodeError CodeDescription
401 UnauthorizedAUTH_INVALID_TOKENUser token is invalid
403 ForbiddenNOTIFICATION_ACCESS_DENIEDUser does not have access to this notification
404 Not FoundNOTIFICATION_NOT_FOUNDNotification with specified ID does not exist
409 ConflictNOTIFICATION_ALREADY_RESPONDEDNotification has already been responded to

Examples

Fetch Pending Notifications - JavaScript

async function fetchPendingNotifications() {
  try {
    const response = await fetch(
      'https://atp.example.com/api/v1/client/notifications?status=pending&limit=20',
      {
        headers: {
          'Authorization': 'Bearer user_token_xyz789'
        }
      }
    );
    
    if (!response.ok) {
      throw new Error(`HTTP error ${response.status}`);
    }
    
    const data = await response.json();
    
    // Process notifications
    for (const notification of data.notifications) {
      displayNotification(notification);
    }
    
    // Store pagination cursor for next request
    if (data.pagination.has_more) {
      sessionStorage.setItem('notifications_cursor', data.pagination.next_cursor);
    }
    
    return data;
  } catch (error) {
    console.error('Error fetching notifications:', error);
    throw error;
  }
}

Fetch Pending Notifications - Python

import requests

def fetch_pending_notifications(token, cursor=None, limit=20):
    url = 'https://atp.example.com/api/v1/client/notifications'
    
    params = {
        'status': 'pending',
        'limit': limit
    }
    
    if cursor:
        params['cursor'] = cursor
    
    headers = {
        'Authorization': f'Bearer {token}'
    }
    
    response = requests.get(url, params=params, headers=headers)
    response.raise_for_status()
    
    data = response.json()
    
    # Process notifications
    for notification in data['notifications']:
        display_notification(notification)
    
    # Return pagination cursor for next request
    next_cursor = None
    if data['pagination']['has_more']:
        next_cursor = data['pagination']['next_cursor']
    
    return data['notifications'], next_cursor

Acknowledge Notification - JavaScript

async function acknowledgeNotification(notificationId) {
  try {
    const response = await fetch(
      `https://atp.example.com/api/v1/client/notifications/${notificationId}/acknowledge`,
      {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer user_token_xyz789',
          'Content-Type': 'application/json'
        }
      }
    );
    
    if (!response.ok) {
      throw new Error(`HTTP error ${response.status}`);
    }
    
    const data = await response.json();
    console.log(`Notification ${notificationId} acknowledged`);
    
    return data;
  } catch (error) {
    console.error('Error acknowledging notification:', error);
    throw error;
  }
}

Polling Strategy

For applications that use REST polling instead of WebSockets, implement an efficient polling strategy to minimize battery consumption and server load:
  1. Adaptive Polling: Adjust polling frequency based on notification activity
  2. Background Polling: Use platform-specific background fetch capabilities for mobile apps
  3. Exponential Backoff: Increase polling interval after periods of inactivity
  4. Priority-Based: Poll more frequently for high-priority projects or services

Example Adaptive Polling Strategy

class AdaptivePoller {
  constructor(options = {}) {
    this.minInterval = options.minInterval || 30000; // 30 seconds
    this.maxInterval = options.maxInterval || 300000; // 5 minutes
    this.scaleFactor = options.scaleFactor || 1.5;
    this.currentInterval = this.minInterval;
    this.timer = null;
    this.lastNotificationCount = 0;
  }
  
  start(callback) {
    this.stop();
    
    const poll = async () => {
      try {
        const result = await callback();
        
        // Adjust polling interval based on activity
        if (result.notifications.length > 0) {
          // Activity detected, decrease interval
          this.currentInterval = this.minInterval;
          this.lastNotificationCount = result.notifications.length;
        } else if (this.lastNotificationCount === 0) {
          // No recent activity, increase interval
          this.currentInterval = Math.min(
            this.currentInterval * this.scaleFactor,
            this.maxInterval
          );
        } else {
          // Had notifications before but none now, reset counter
          this.lastNotificationCount = 0;
        }
      } catch (error) {
        console.error('Polling error:', error);
        // Increase interval on error
        this.currentInterval = Math.min(
          this.currentInterval * this.scaleFactor,
          this.maxInterval
        );
      }
      
      // Schedule next poll
      this.timer = setTimeout(poll, this.currentInterval);
    };
    
    // Start polling
    poll();
  }
  
  stop() {
    if (this.timer) {
      clearTimeout(this.timer);
      this.timer = null;
    }
  }
}

// Usage
const poller = new AdaptivePoller();
poller.start(fetchPendingNotifications);

Best Practices

  1. Use WebSockets When Possible: Prefer WebSocket connections for real-time updates when available
  2. Efficient Polling: Implement adaptive polling to reduce server load and battery consumption
  3. Use Pagination: Always handle pagination correctly to retrieve all notifications
  4. Cache Results: Cache notification data to reduce redundant API calls
  5. Batch Operations: Use the bulk acknowledge endpoint for marking multiple notifications
  6. Error Handling: Implement proper error handling and retries for network failures
I