Skip to main content
GET
https://vij.example.com
/
api
/
groups
GET /api/groups
curl --request GET \
  --url https://vij.example.com/api/groups
{
  "groups": [
    {
      "group": "abc123def456",
      "message": "TypeError: Cannot read property 'map' of undefined",
      "name": "TypeError",
      "stack": "TypeError: Cannot read property 'map' of undefined\n    at processData (app.js:123:45)\n    at renderList (components/List.tsx:67:12)",
      "count": 2547,
      "severity": "error",
      "firstSeen": "2024-01-01T10:00:00Z",
      "lastSeen": "2024-01-03T14:30:00Z",
      "affectedApps": ["frontend", "mobile-app"],
      "affectedEnvironments": ["production"],
      "affectedUsers": 456,
      "recentOccurrences": [
        "65a1b2c3d4e5f6g7h8i9j0k1",
        "65a1b2c3d4e5f6g7h8i9j0k2",
        "65a1b2c3d4e5f6g7h8i9j0k3"
      ]
    },
    {
      "group": "def456ghi789",
      "message": "Payment processing failed",
      "name": "Error",
      "stack": "Error: Payment processing failed\n    at processPayment (payment.js:45:12)\n    at handleCheckout (checkout.js:123:5)",
      "count": 1234,
      "severity": "error",
      "firstSeen": "2024-01-02T08:00:00Z",
      "lastSeen": "2024-01-03T15:00:00Z",
      "affectedApps": ["frontend"],
      "affectedEnvironments": ["production", "staging"],
      "affectedUsers": 234,
      "recentOccurrences": [
        "65a1b2c3d4e5f6g7h8i9j0k4",
        "65a1b2c3d4e5f6g7h8i9j0k5"
      ]
    }
  ],
  "total": 347,
  "page": 1,
  "limit": 50,
  "pages": 7
}
The GET /api/groups endpoint retrieves deduplicated error groups, showing unique errors with their occurrence counts and metadata.

Query Parameters

environment
string
Filter groups by environment.Example: /api/groups?environment=production
appId
string
Filter groups by application ID.Example: /api/groups?appId=my-frontend-app
severity
string
Filter by highest severity in the group.Values: error, warning, infoExample: /api/groups?severity=error
startDate
string
Filter groups with occurrences after this date (ISO 8601).Example: /api/groups?startDate=2024-01-01T00:00:00Z
endDate
string
Filter groups with occurrences before this date (ISO 8601).Example: /api/groups?endDate=2024-01-31T23:59:59Z
minCount
number
Filter groups with at least this many occurrences.Example: /api/groups?minCount=10
page
number
default:"1"
Page number for pagination (1-indexed).Example: /api/groups?page=2
limit
number
default:"50"
Number of results per page (max: 100).Example: /api/groups?limit=100
sortBy
string
default:"count"
Field to sort by.Values: count, lastSeen, firstSeen, severityExample: /api/groups?sortBy=lastSeen
sortOrder
string
default:"desc"
Sort order.Values: asc (ascending), desc (descending)Example: /api/groups?sortOrder=asc

Response Fields

groups
array
Array of error group objects.
total
number
Total number of groups matching the query.
page
number
Current page number.
limit
number
Results per page.
pages
number
Total number of pages.

Example Requests

curl "https://vij.example.com/api/groups"

Example Response

{
  "groups": [
    {
      "group": "abc123def456",
      "message": "TypeError: Cannot read property 'map' of undefined",
      "name": "TypeError",
      "stack": "TypeError: Cannot read property 'map' of undefined\n    at processData (app.js:123:45)\n    at renderList (components/List.tsx:67:12)",
      "count": 2547,
      "severity": "error",
      "firstSeen": "2024-01-01T10:00:00Z",
      "lastSeen": "2024-01-03T14:30:00Z",
      "affectedApps": ["frontend", "mobile-app"],
      "affectedEnvironments": ["production"],
      "affectedUsers": 456,
      "recentOccurrences": [
        "65a1b2c3d4e5f6g7h8i9j0k1",
        "65a1b2c3d4e5f6g7h8i9j0k2",
        "65a1b2c3d4e5f6g7h8i9j0k3"
      ]
    },
    {
      "group": "def456ghi789",
      "message": "Payment processing failed",
      "name": "Error",
      "stack": "Error: Payment processing failed\n    at processPayment (payment.js:45:12)\n    at handleCheckout (checkout.js:123:5)",
      "count": 1234,
      "severity": "error",
      "firstSeen": "2024-01-02T08:00:00Z",
      "lastSeen": "2024-01-03T15:00:00Z",
      "affectedApps": ["frontend"],
      "affectedEnvironments": ["production", "staging"],
      "affectedUsers": 234,
      "recentOccurrences": [
        "65a1b2c3d4e5f6g7h8i9j0k4",
        "65a1b2c3d4e5f6g7h8i9j0k5"
      ]
    }
  ],
  "total": 347,
  "page": 1,
  "limit": 50,
  "pages": 7
}

Use Cases

Dashboard Overview

Display top error groups:
const { groups } = await fetch(
  'https://vij.example.com/api/groups?sortBy=count&sortOrder=desc&limit=10'
).then(r => r.json());

groups.forEach(group => {
  console.log(`${group.message}: ${group.count} occurrences`);
});

Identify New Issues

Find groups that appeared recently:
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();

const { groups } = await fetch(
  `https://vij.example.com/api/groups?startDate=${yesterday}&sortBy=firstSeen&sortOrder=desc`
).then(r => r.json());

// New error groups in last 24 hours
console.log(`${groups.length} new error groups detected`);

High-Impact Errors

Find errors affecting many users:
const { groups } = await fetch(
  'https://vij.example.com/api/groups?minCount=100&sortBy=count&sortOrder=desc'
).then(r => r.json());

// High-frequency errors
groups.forEach(group => {
  console.log(`${group.message}: ${group.affectedUsers} users affected`);
});

Application Health

Compare error groups across apps:
const frontendGroups = await fetch(
  'https://vij.example.com/api/groups?appId=frontend'
).then(r => r.json());

const backendGroups = await fetch(
  'https://vij.example.com/api/groups?appId=backend-api'
).then(r => r.json());

console.log(`Frontend: ${frontendGroups.total} unique errors`);
console.log(`Backend: ${backendGroups.total} unique errors`);

Filtering Examples

By Frequency

# High-frequency errors (100+ occurrences)
GET /api/groups?minCount=100

# Low-frequency errors (1-10 occurrences)
GET /api/groups?minCount=1&maxCount=10

By Time

# Groups seen in last 24 hours
startDate=$(date -u -d '1 day ago' +%Y-%m-%dT%H:%M:%SZ)
GET /api/groups?startDate=$startDate

# Groups first seen this week
startDate=$(date -u -d 'last monday' +%Y-%m-%dT00:00:00Z)
GET /api/groups?startDate=$startDate&sortBy=firstSeen

By Severity

# Critical errors only
GET /api/groups?severity=error

# All severities in production
GET /api/groups?environment=production

Combined Filters

# High-frequency production errors
GET /api/groups?environment=production&severity=error&minCount=50&sortBy=count&sortOrder=desc

# Recent low-frequency warnings
GET /api/groups?severity=warning&minCount=1&maxCount=10&sortBy=lastSeen&sortOrder=desc

Sorting Options

By Count (Most Frequent)

GET /api/groups?sortBy=count&sortOrder=desc
Use Case: Find the most impactful errors to fix first.

By Last Seen (Most Recent)

GET /api/groups?sortBy=lastSeen&sortOrder=desc
Use Case: Monitor active issues and recent deployments.

By First Seen (Newest Issues)

GET /api/groups?sortBy=firstSeen&sortOrder=desc
Use Case: Identify new bugs introduced recently.

By Severity

GET /api/groups?sortBy=severity&sortOrder=desc
Use Case: Prioritize critical errors over warnings.

Getting Group Details

To get all occurrences of a specific group:
# Get group fingerprint from /api/groups
group="abc123def456"

# Fetch all occurrences
curl "https://vij.example.com/api/logs?group=$group"
Response: All individual error logs in that group.

Pagination

Navigate through large result sets:
async function getAllGroups() {
  let page = 1;
  let allGroups = [];
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(
      `https://vij.example.com/api/groups?page=${page}&limit=100`
    ).then(r => r.json());

    allGroups = allGroups.concat(response.groups);

    hasMore = page < response.pages;
    page++;
  }

  return allGroups;
}

Performance Optimization

# Good - last 30 days
startDate=$(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ)
GET /api/groups?startDate=$startDate

# Slow - all time
GET /api/groups
# Faster - single app
GET /api/groups?appId=my-app

# Slower - all apps
GET /api/groups
# Faster - high-frequency only
GET /api/groups?minCount=50

# Slower - includes one-off errors
GET /api/groups
# Good for most cases
GET /api/groups?limit=50

# Use larger for batch processing
GET /api/groups?limit=100

Client Implementation

TypeScript

interface GroupsQuery {
  environment?: string;
  appId?: string;
  severity?: 'error' | 'warning' | 'info';
  startDate?: string;
  endDate?: string;
  minCount?: number;
  page?: number;
  limit?: number;
  sortBy?: 'count' | 'lastSeen' | 'firstSeen' | 'severity';
  sortOrder?: 'asc' | 'desc';
}

interface ErrorGroup {
  group: string;
  message: string;
  name: string;
  stack: string;
  count: number;
  severity: string;
  firstSeen: string;
  lastSeen: string;
  affectedApps: string[];
  affectedEnvironments: string[];
  affectedUsers: number;
  recentOccurrences: string[];
}

interface GroupsResponse {
  groups: ErrorGroup[];
  total: number;
  page: number;
  limit: number;
  pages: number;
}

async function fetchGroups(query: GroupsQuery = {}): Promise<GroupsResponse> {
  const params = new URLSearchParams(
    Object.entries(query)
      .filter(([_, v]) => v !== undefined)
      .map(([k, v]) => [k, String(v)])
  );

  const response = await fetch(
    `https://vij.example.com/api/groups?${params}`
  );

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

  return response.json();
}

// Usage
const groups = await fetchGroups({
  environment: 'production',
  severity: 'error',
  minCount: 10,
  sortBy: 'count',
  sortOrder: 'desc',
  limit: 20
});

React Hook

import { useState, useEffect } from 'react';

function useErrorGroups(query: GroupsQuery) {
  const [groups, setGroups] = useState<ErrorGroup[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    async function load() {
      try {
        setLoading(true);
        const data = await fetchGroups(query);
        setGroups(data.groups);
      } catch (err) {
        setError(err as Error);
      } finally {
        setLoading(false);
      }
    }

    load();
  }, [JSON.stringify(query)]);

  return { groups, loading, error };
}

// Usage in component
function ErrorGroupsList() {
  const { groups, loading, error } = useErrorGroups({
    environment: 'production',
    severity: 'error',
    minCount: 10
  });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <ul>
      {groups.map(group => (
        <li key={group.group}>
          {group.message} ({group.count} occurrences)
        </li>
      ))}
    </ul>
  );
}

Exporting Group Data

Export groups for reporting:
# Export as JSON
curl "https://vij.example.com/api/groups?environment=production" > groups.json

# Export as CSV (requires custom endpoint or processing)
curl "https://vij.example.com/api/groups/export?format=csv&environment=production" > groups.csv
Track how groups change over time:
// Daily group count tracking
async function trackGroupTrends() {
  const today = await fetch('/api/groups').then(r => r.json());
  const yesterday = await fetch(
    `/api/groups?endDate=${new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()}`
  ).then(r => r.json());

  const change = today.total - yesterday.total;
  console.log(`Group change: ${change > 0 ? '+' : ''}${change}`);

  if (change > 10) {
    alert(`Warning: ${change} new error groups detected!`);
  }
}