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