BETA
Skip to content

Statistics ​

Retrieve aggregated email sending statistics for your team. There are two endpoints with different response shapes — GET /mail/stats returns rolled-up totals for a fixed period, while GET /mail/stats/daily returns one row per day.

Base URL: https://app.krafter.dev/api/v1

Email Stats ​

Returns aggregate email counts grouped by status for a given time period.

GET /mail/stats

Required scope: mail:read

Query Parameters ​

ParameterTypeDefaultDescription
periodstring"7d"Time period. One of: "1d", "7d", "30d", "90d". Other values fall back to "7d".

Example Request ​

bash
curl "https://app.krafter.dev/api/v1/mail/stats?period=30d" \
  -H "Authorization: Bearer kr_live_abc123def456"

Example Response ​

json
// 200 OK
{
  "data": {
    "period": "30d",
    "since": "2026-01-16T12:00:00Z",
    "total": 1250,
    "by_status": {
      "queued": 5,
      "sent": 20,
      "delivered": 1180,
      "bounced": 30,
      "complained": 2,
      "failed": 13
    }
  }
}

The by_status object contains counts keyed by status string. Only statuses with non-zero counts are included. total is the sum across all statuses.


Daily Stats ​

Returns daily email counts broken down by status for the requested number of days. Each item in the array represents a single day.

GET /mail/stats/daily

Required scope: mail:read

Query Parameters ​

ParameterTypeDefaultDescription
daysinteger30Number of days to look back. Capped server-side at 365.

This endpoint accepts only days. It does not accept from_date or to_date query parameters.

Example Request ​

bash
curl "https://app.krafter.dev/api/v1/mail/stats/daily?days=7" \
  -H "Authorization: Bearer kr_live_abc123def456"

Example Response ​

json
// 200 OK
{
  "data": [
    {
      "date": "2026-02-09",
      "queued": 0,
      "sent": 3,
      "delivered": 42,
      "bounced": 1,
      "complained": 0,
      "failed": 0
    },
    {
      "date": "2026-02-10",
      "queued": 0,
      "sent": 5,
      "delivered": 38,
      "bounced": 0,
      "complained": 0,
      "failed": 1
    }
  ]
}

Response Fields (per day) ​

FieldTypeDescription
datestringThe date in YYYY-MM-DD format (UTC).
queuedintegerEmails created on this date still queued.
sentintegerEmails handed off to the upstream provider.
deliveredintegerEmails confirmed delivered.
bouncedintegerEmails that bounced.
complainedintegerSpam complaints recorded.
failedintegerSends that failed (post-retry).

INFO

Daily stats are returned in ascending date order. Past days are served from a pre-aggregated email_daily_stats table updated by a daily cron job. Today's row is computed live from the emails table — counts may shift slightly until the next aggregation run.

INFO

The daily-stats response does not currently include opened or clicked columns. Use GET /emails/:id/events to inspect open and click activity per email, or watch the email.opened / email.clicked webhooks for real-time tracking.


Computing Rates Client-Side ​

Both endpoints return raw counts. Compute rates in your client code:

typescript
type DailyRow = {
  date: string;
  queued: number;
  sent: number;
  delivered: number;
  bounced: number;
  complained: number;
  failed: number;
};

function rates(row: DailyRow) {
  const total = row.sent + row.delivered + row.bounced + row.failed;
  return {
    delivery_rate: total > 0 ? ((row.delivered / total) * 100).toFixed(2) : "0.00",
    bounce_rate: total > 0 ? ((row.bounced / total) * 100).toFixed(2) : "0.00",
    complaint_rate: row.delivered > 0 ? ((row.complained / row.delivered) * 100).toFixed(2) : "0.00",
  };
}

Built by Krafter Studio