Monitoring & Analytics

CallMeLater provides comprehensive monitoring and analytics tools to help you track the performance and status of your scheduled requests. This guide covers how to monitor execution, analyze performance, and troubleshoot issues.

Real-time Monitoring

Execution Status

Every scheduled request goes through several states:
StatusDescription
scheduledRequest is scheduled and waiting to execute
completedRequest executed successfully (2xx response)
failedRequest failed to execute or returned error status
cancelledRequest was cancelled before execution

Getting Request Logs

Retrieve logs for your scheduled requests:
curl -H "x-api-key: your_api_key_here" \
  "https://api.callmelater.xyz/stats/logs"
const getLogs = async (filters = {}) => {
  const params = new URLSearchParams(filters);

  const response = await fetch(
    `https://api.callmelater.xyz/stats/logs?${params}`,
    {
      headers: {
        "x-api-key": "your_api_key_here",
      },
    }
  );

  return response.json();
};

// Get all logs
const allLogs = await getLogs();

// Filter by status
const failedLogs = await getLogs({ status: "failed" });

// Filter by URL
const webhookLogs = await getLogs({
  targetUrl: "https://api.example.com/webhook",
});

Log Data Structure

Each log entry contains detailed information about the execution:
{
  "invocationId": "inv_123abc",
  "scheduleId": "sch_456def",
  "createdAt": "2024-03-19T10:00:00Z",
  "scheduledAt": "2024-03-20T15:00:00Z",
  "targetUrl": "https://api.example.com/webhook",
  "targetMethod": "POST",
  "status": "completed",
  "statusCode": 200,
  "responseTimeMs": 245,
  "errorMessage": ""
}

Filtering and Searching

Available Filters

Filter logs using query parameters:
const getFilteredLogs = async () => {
  const filters = {
    status: "failed", // Filter by execution status
    targetUrl: "https://...", // Filter by target URL
    from: "2024-03-01T00:00:00Z", // Start date
    to: "2024-03-31T23:59:59Z", // End date
    limit: 100, // Number of results (max 100)
    order: "desc", // Sort order (asc/desc)
  };

  return getLogs(filters);
};

Advanced Filtering Examples

// Get failed requests from last 24 hours
const recentFailures = await getLogs({
  status: "failed",
  from: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),
  order: "desc",
});

// Get slow requests (filter in memory after fetching)
const allLogs = await getLogs({ limit: 100 });
const slowRequests = allLogs.logs.filter((log) => log.responseTimeMs > 5000);

// Get logs for specific webhook endpoint
const webhookLogs = await getLogs({
  targetUrl: "https://api.example.com/webhook",
  from: "2024-03-01T00:00:00Z",
});

Performance Analytics

Usage Statistics

Get aggregated statistics for your account:
curl -H "x-api-key: your_api_key_here" \
  "https://api.callmelater.xyz/stats"
const getUsageStats = async (timeRange = {}) => {
  const params = new URLSearchParams(timeRange);

  const response = await fetch(`https://api.callmelater.xyz/stats?${params}`, {
    headers: {
      "x-api-key": "your_api_key_here",
    },
  });

  return response.json();
};

// Get stats for last 30 days (default)
const monthlyStats = await getUsageStats();

// Get stats for custom period
const customStats = await getUsageStats({
  from: "2024-03-01T00:00:00Z",
  to: "2024-03-31T23:59:59Z",
});

Statistics Response

{
  "totalInvocations": 1250,
  "successfulInvocations": 1198,
  "failedInvocations": 52,
  "averageResponseTime": 342.5,
  "timeRange": {
    "from": "2024-02-20T00:00:00Z",
    "to": "2024-03-20T00:00:00Z"
  }
}

Key Metrics

Success Rate

Percentage of requests that completed successfully

Response Time

Average time for target endpoints to respond

Failure Rate

Percentage of requests that failed or timed out

Volume Trends

Number of requests scheduled over time

Credit Monitoring

Check Remaining Credits

const checkCredits = async () => {
  const response = await fetch(
    "https://api.callmelater.xyz/stats/remaining-credits",
    {
      headers: {
        "x-api-key": "your_api_key_here",
      },
    }
  );

  const { creditsRemaining } = await response.json();
  return creditsRemaining;
};

// Set up credit monitoring
const monitorCredits = async () => {
  const credits = await checkCredits();

  if (credits < 10) {
    console.warn("Low credits warning:", credits);
    // Send alert to your monitoring system
  }

  return credits;
};

Credit Usage Patterns

const analyzeCreditUsage = async () => {
  const logs = await getLogs({ limit: 100 });
  const stats = await getUsageStats();

  const analysis = {
    creditsUsedThisMonth: stats.totalInvocations,
    successRate: (stats.successfulInvocations / stats.totalInvocations) * 100,
    avgDailyUsage: stats.totalInvocations / 30,
    topTargetUrls: getTopUrls(logs.logs),
  };

  return analysis;
};

const getTopUrls = (logs) => {
  const urlCounts = logs.reduce((acc, log) => {
    acc[log.targetUrl] = (acc[log.targetUrl] || 0) + 1;
    return acc;
  }, {});

  return Object.entries(urlCounts)
    .sort(([, a], [, b]) => b - a)
    .slice(0, 5);
};

Error Tracking

Common Error Patterns

Monitor and categorize different types of failures:
const analyzeErrors = async () => {
  const failedLogs = await getLogs({ status: "failed" });

  const errorCategories = {
    timeout: [],
    clientError: [], // 4xx
    serverError: [], // 5xx
    networkError: [],
  };

  failedLogs.logs.forEach((log) => {
    if (log.errorMessage.includes("timeout")) {
      errorCategories.timeout.push(log);
    } else if (log.statusCode >= 400 && log.statusCode < 500) {
      errorCategories.clientError.push(log);
    } else if (log.statusCode >= 500) {
      errorCategories.serverError.push(log);
    } else {
      errorCategories.networkError.push(log);
    }
  });

  return errorCategories;
};

Error Response Examples

{
  "invocationId": "inv_789xyz",
  "status": "failed",
  "statusCode": 404,
  "errorMessage": "Not Found",
  "responseTimeMs": 150
}
{
  "invocationId": "inv_456def",
  "status": "failed",
  "statusCode": 0,
  "errorMessage": "Request timeout after 30 seconds",
  "responseTimeMs": 30000
}

Alerting and Notifications

Basic Monitoring Setup

class CallMeLaterMonitor {
  constructor(apiKey, alertThresholds = {}) {
    this.apiKey = apiKey;
    this.thresholds = {
      lowCredits: 10,
      highFailureRate: 0.1, // 10%
      slowResponseTime: 5000, // 5 seconds
      ...alertThresholds,
    };
  }

  async checkHealth() {
    const [credits, stats] = await Promise.all([
      this.getCredits(),
      this.getStats(),
    ]);

    const alerts = [];

    // Check credit levels
    if (credits < this.thresholds.lowCredits) {
      alerts.push({
        type: "LOW_CREDITS",
        message: `Only ${credits} credits remaining`,
        severity: "warning",
      });
    }

    // Check failure rate
    const failureRate = stats.failedInvocations / stats.totalInvocations;
    if (failureRate > this.thresholds.highFailureRate) {
      alerts.push({
        type: "HIGH_FAILURE_RATE",
        message: `Failure rate: ${(failureRate * 100).toFixed(1)}%`,
        severity: "error",
      });
    }

    // Check response times
    if (stats.averageResponseTime > this.thresholds.slowResponseTime) {
      alerts.push({
        type: "SLOW_RESPONSES",
        message: `Average response time: ${stats.averageResponseTime}ms`,
        severity: "warning",
      });
    }

    return { healthy: alerts.length === 0, alerts };
  }

  async getCredits() {
    const response = await fetch(
      "https://api.callmelater.xyz/stats/remaining-credits",
      {
        headers: { "x-api-key": this.apiKey },
      }
    );
    const { creditsRemaining } = await response.json();
    return creditsRemaining;
  }

  async getStats() {
    const response = await fetch("https://api.callmelater.xyz/stats", {
      headers: { "x-api-key": this.apiKey },
    });
    return response.json();
  }
}

// Usage
const monitor = new CallMeLaterMonitor("your_api_key_here");

// Run health check
const health = await monitor.checkHealth();
if (!health.healthy) {
  console.log("Alerts:", health.alerts);
  // Send to your alerting system
}

Dashboard Integration

Building Custom Dashboards

const getDashboardData = async () => {
  const [credits, stats, recentLogs] = await Promise.all([
    checkCredits(),
    getUsageStats(),
    getLogs({ limit: 20, order: "desc" }),
  ]);

  return {
    overview: {
      creditsRemaining: credits,
      totalInvocations: stats.totalInvocations,
      successRate: (stats.successfulInvocations / stats.totalInvocations) * 100,
      avgResponseTime: stats.averageResponseTime,
    },
    recentActivity: recentLogs.logs.map((log) => ({
      time: log.createdAt,
      url: log.targetUrl,
      status: log.status,
      responseTime: log.responseTimeMs,
    })),
    trends: {
      daily: calculateDailyTrends(recentLogs.logs),
      hourly: calculateHourlyTrends(recentLogs.logs),
    },
  };
};

Best Practices

Regular Monitoring

Set up automated health checks to run every few minutes

Alert Thresholds

Configure appropriate thresholds for your use case

Log Retention

Regularly export important logs for long-term analysis

Performance Trends

Track metrics over time to identify performance trends

Monitoring Checklist

  • ✅ Monitor credit levels daily
  • ✅ Track success/failure rates
  • ✅ Alert on high failure rates
  • ✅ Monitor response times
  • ✅ Review error logs weekly
  • ✅ Export data for compliance
  • ✅ Set up redundant monitoring