Skip to main content

Overview

The dashboard endpoint returns a hierarchical view of all your data - clients with their projects, and each project with its licenses. This provides a complete overview of your licensing infrastructure.

Endpoint

GET /dashboard
This endpoint requires authentication. Include a valid JWT token in the Authorization header.

Authentication

Include the JWT token in the Authorization header:
Authorization: Bearer <your-jwt-token>

Request

Optional Request Body

clientId
string
Optional MongoDB ObjectId to filter dashboard data for a specific client
If no clientId is provided, the endpoint returns data for all clients owned by the authenticated user.

Example Request

curl -X GET https://your-keybox-server.com/dashboard \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response

Success Response

message
string
Success message
clientsCount
number
Total number of clients returned
projectsCount
number
Total number of projects across all clients
data
array
Array of client objects with nested projects and licenses

Example Response

{
  "message": "Dashboard fetched successfully",
  "clientsCount": 2,
  "projectsCount": 3,
  "data": [
    {
      "_id": "60f7b3b3e6b3a72e8c8e4a1a",
      "name": "Acme Corporation",
      "email": "[email protected]",
      "owner": "60f7b3b3e6b3a72e8c8e4a19",
      "projects": [
        {
          "_id": "60f7b3b3e6b3a72e8c8e4a1b",
          "name": "E-commerce Website",
          "client": "60f7b3b3e6b3a72e8c8e4a1a",
          "licenses": [
            {
              "_id": "60f7b3b3e6b3a72e8c8e4a1c",
              "key": "KB-60f7b3b3e6b3a72e8c8e4a1b-A1B2-C3D4-E5F6",
              "duration": 12,
              "issuedAt": "2024-01-01T00:00:00.000Z",
              "expiresAt": "2025-01-01T00:00:00.000Z",
              "status": "ACTIVE",
              "services": ["Hosting", "Domain"],
              "machineId": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
            }
          ]
        },
        {
          "_id": "60f7b3b3e6b3a72e8c8e4a1d",
          "name": "Mobile App Backend",
          "client": "60f7b3b3e6b3a72e8c8e4a1a",
          "licenses": [
            {
              "_id": "60f7b3b3e6b3a72e8c8e4a1e",
              "key": "KB-60f7b3b3e6b3a72e8c8e4a1d-B2C3-D4E5-F6G7",
              "duration": 6,
              "issuedAt": "2024-02-01T00:00:00.000Z",
              "expiresAt": "2024-08-01T00:00:00.000Z",
              "status": "PENDING",
              "services": ["Hosting"]
            }
          ]
        }
      ]
    },
    {
      "_id": "60f7b3b3e6b3a72e8c8e4a1f",
      "name": "TechStart Inc",
      "email": "[email protected]",
      "owner": "60f7b3b3e6b3a72e8c8e4a19",
      "projects": [
        {
          "_id": "60f7b3b3e6b3a72e8c8e4a20",
          "name": "SaaS Platform",
          "client": "60f7b3b3e6b3a72e8c8e4a1f",
          "licenses": [
            {
              "_id": "60f7b3b3e6b3a72e8c8e4a21",
              "key": "KB-60f7b3b3e6b3a72e8c8e4a20-C3D4-E5F6-G7H8",
              "duration": 12,
              "issuedAt": "2024-01-15T00:00:00.000Z",
              "expiresAt": "2025-01-15T00:00:00.000Z",
              "status": "ACTIVE",
              "services": ["Hosting", "Domain"],
              "machineId": "b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7"
            }
          ]
        }
      ]
    }
  ]
}

Data Hierarchy

The dashboard returns data in this hierarchy:
Client
├─ Client Info (name, email, owner)
└─ Projects []
   ├─ Project Info (name, client)
   └─ Licenses []
      └─ License Info (key, status, duration, etc.)

Query Behavior

The endpoint automatically filters by the authenticated user’s ID (owner field). You only see clients you own.
If you provide a clientId, only that specific client’s data is returned (if you own it).
The endpoint uses MongoDB’s aggregation to efficiently fetch nested relationships without multiple queries.

Error Responses

Unauthorized

Status Code: 401
{
  "message": "Unauthorized"
}

Server Error

Status Code: 500
{
  "message": "Failed to fetch dashboard",
  "error": "Detailed error message"
}

Complete Example

class Dashboard {
  constructor(serverUrl, authToken) {
    this.serverUrl = serverUrl;
    this.authToken = authToken;
  }

  async fetchAll() {
    try {
      const response = await fetch(`${this.serverUrl}/dashboard`, {
        headers: {
          'Authorization': `Bearer ${this.authToken}`
        }
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }

      const data = await response.json();
      this.displaySummary(data);
      
      return data;
    } catch (error) {
      console.error('Failed to fetch dashboard:', error);
      throw error;
    }
  }

  async fetchClient(clientId) {
    try {
      const response = await fetch(`${this.serverUrl}/dashboard`, {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.authToken}`
        },
        body: JSON.stringify({ clientId })
      });

      const data = await response.json();
      return data;
    } catch (error) {
      console.error('Failed to fetch client:', error);
      throw error;
    }
  }

  displaySummary(data) {
    console.log('\n=== Dashboard Summary ===');
    console.log(`Total Clients: ${data.clientsCount}`);
    console.log(`Total Projects: ${data.projectsCount}`);
    
    let totalLicenses = 0;
    let activeLicenses = 0;
    let pendingLicenses = 0;
    
    data.data.forEach(client => {
      client.projects.forEach(project => {
        totalLicenses += project.licenses.length;
        project.licenses.forEach(license => {
          if (license.status === 'ACTIVE') activeLicenses++;
          if (license.status === 'PENDING') pendingLicenses++;
        });
      });
    });
    
    console.log(`Total Licenses: ${totalLicenses}`);
    console.log(`Active: ${activeLicenses}`);
    console.log(`Pending: ${pendingLicenses}`);
    console.log('========================\n');
  }

  getExpiringLicenses(days = 30) {
    const threshold = new Date();
    threshold.setDate(threshold.getDate() + days);
    
    const expiring = [];
    
    this.lastData?.data.forEach(client => {
      client.projects.forEach(project => {
        project.licenses.forEach(license => {
          const expiresAt = new Date(license.expiresAt);
          if (license.status === 'ACTIVE' && expiresAt < threshold) {
            expiring.push({
              client: client.name,
              project: project.name,
              key: license.key,
              expiresAt: expiresAt
            });
          }
        });
      });
    });
    
    return expiring;
  }
}

// Usage
const dashboard = new Dashboard(
  'https://your-keybox-server.com',
  'your-jwt-token'
);

// Fetch all data
const data = await dashboard.fetchAll();

// Check for expiring licenses
const expiring = dashboard.getExpiringLicenses(30);
if (expiring.length > 0) {
  console.log('\nLicenses expiring in 30 days:');
  expiring.forEach(item => {
    console.log(`${item.client} - ${item.project}: ${item.expiresAt.toDateString()}`);
  });
}

Use Cases

Admin Overview

Get a complete view of all clients, projects, and licenses

Client Portal

Filter by clientId to show client-specific data

Expiration Alerts

Identify licenses expiring soon for renewal

Analytics

Analyze license usage patterns and status distribution

Best Practices

Cache Results

Cache dashboard data locally to reduce API calls

Pagination

For large datasets, consider implementing pagination

Refresh Strategy

Refresh data periodically or on user action

Error Handling

Handle network errors and display fallback UI

See Also

Build docs developers (and LLMs) love