API

The Dawnset REST API.

Three endpoints, one auth model, zero SDK. Pull today’s Morning Briefing, the last 30 days of briefs, or just verify that a stored session is still valid — straight from /api into the dashboard or tooling you already run.

Authentication

Sign in once. Reuse the cookie.

Every endpoint is gated by the same browser session cookie Dawnset sets at /login (via better-auth). There is no API key, no Authorization header, no X-API-Key — just the cookie your browser already carries.

  • What to send

    Nothing. The session cookie travels with every fetch / curl hitting the same origin.

  • Where to start

    Open /login in your browser, or hit the /api/auth/* routes directly.

  • What to expect

    The dashboard, the Slack delivery, and the API all see the same session — sign in once, query from anywhere.

Per-endpoint behavior

EndpointAnonymousSigned in
GET /api/me401 UnauthorizedReturns the signed-in user envelope.
GET /api/briefing200 — rules are skipped, ranking falls back to defaults.Re-ranks against your enabled AlertRule rows.
GET /api/briefing-archive401 UnauthorizedReturns the last 30 days, rules applied per day.

Endpoint catalog

Three endpoints. Six alerts each.

The shapes below match the live zod contracts at src/lib/contracts/me.ts and src/lib/contracts/briefing.ts. The contract is the source of truth — if a field here disagrees with the contract it’s a doc bug, please report it.

01GET/api/me

Returns the signed-in user. Use this from external tooling to check that a stored cookie is still valid.

Auth
Signed-in session required (401 if anonymous)

Response shape

  • user.idstringStable user id (matches the dashboard session).
  • user.emailstringEmail address on the account.
  • user.namestring | nullDisplay name, or null when unset.

Errors

401 No valid session cookie. Sign in via /login or /api/auth/* and retry.

{ "error": "Unauthorized" }
02GET/api/briefing

Today's Morning Briefing. Six ranked alerts across the four connectors — Stripe, GA4, support inbox, and infra. Same shape as the dashboard card and the Slack delivery.

Auth
Anonymous-friendly (rules only loaded when signed in)

Response shape

  • generatedAtstringISO-8601 timestamp the brief was built.
  • cadencestringHuman-readable cadence, e.g. "Every morning, 06:14 local".
  • alertCountnumberAlways 6 — pinned by the contract.
  • alerts[]BriefingAlert[]Ranked alerts (see field table below). Length is fixed at 6.
  • alerts[].idstringStable seed id; same id surfaces across days.
  • alerts[].rank"P0" | "P1" | "P2"Priority band (top 2 = P0).
  • alerts[].ordernumberZero-indexed position within the brief.
  • alerts[].titlestringOne-line alert title.
  • alerts[].detailstringTwo-to-three sentence context.
  • alerts[].source"stripe" | "ga4" | "support" | "infra"Which connector raised the alert.
  • alerts[].surfacedAtstringHH:MM surface time in your local zone.
  • alerts[].revenueImpactUsdnumber | nullEstimated MRR at risk in whole USD — null when not revenue-weighted.
  • alerts[].impactLabelstringPre-formatted label, e.g. "$1,840 MRR" or "Impact: high".
  • alerts[].severity"critical" | "high" | "medium" | "low" | "watch"Severity band.
  • alerts[].urgencyHoursnumberHours before the alert ages out.
  • alerts[].recommendedActionstringThe next concrete step Dawnset suggests.

Errors

500 Internal failure (extremely rare in the seed-data demo). Body is plain text.

"Internal Server Error"
03GET/api/briefing-archive

The last 30 days of Morning Briefings as one payload. Same per-alert shape as /api/briefing, ordered most-recent first.

Auth
Signed-in session required (401 if anonymous)

Response shape

  • windowDays30Always 30 — pinned by the contract.
  • days[]BriefingArchiveDay[]Length is fixed at 30.
  • days[].datestringYYYY-MM-DD in the server's local calendar (day 0 = today).
  • days[].alertsBriefingAlert[]Six ranked alerts; same shape as /api/briefing alerts[].

Errors

401 No valid session cookie. Sign in via /login or /api/auth/* and retry.

"Unauthorized"

Examples

Copy, paste, run.

The three pairs below show the minimum curl request and the matching JSON response. Each request assumes you have already signed in and have a session cookie named dawnset.session in your cookie jar.

GET/api/me

Verify a stored cookie is still valid. 401 means the session has expired — sign in again.

Request

curl
curl https://dawnset.example/api/me \
  --cookie 'dawnset.session=…'

Response — 200 OK

{
  "user": {
    "id": "user_2xKp8MnQrL9vT4wA",
    "email": "founder@example.com",
    "name": "Alex Rivera"
  }
}
GET/api/briefing

Today’s Morning Briefing. Always six ranked alerts; rule re-weighting only applies when the call carries a valid session cookie.

Request

curl
curl https://dawnset.example/api/briefing \
  --cookie 'dawnset.session=…'

Response — 200 OK

{
  "generatedAt": "2026-08-06T06:14:00.000Z",
  "cadence": "Every morning, 06:14 local",
  "alertCount": 6,
  "alerts": [
    {
      "id": "stripe-failed-charge-cluster",
      "rank": "P0",
      "order": 0,
      "title": "Three failed charges from Renewal-API in the last 6h",
      "detail": "Stripe webhook returned 402 — dunning retry cleared 2, 1 still failing on the next attempt.",
      "source": "stripe",
      "surfacedAt": "04:51",
      "revenueImpactUsd": 1840,
      "impactLabel": "$1,840 MRR",
      "severity": "critical",
      "urgencyHours": 6,
      "recommendedAction": "Open the failing customer in Stripe, retry the charge manually, and patch the webhook."
    }
    // … five more alerts, same shape, length pinned to 6 by the contract
  ]
}
GET/api/briefing-archive

The last 30 days, ordered most-recent first. Authentication is required — anonymous calls return 401.

Request

curl
curl https://dawnset.example/api/briefing-archive \
  --cookie 'dawnset.session=…'

Response — 200 OK

{
  "windowDays": 30,
  "days": [
    {
      "date": "2026-08-06",
      "alerts": [ /* today's six ranked alerts — same shape as /api/briefing alerts[] */ ]
    },
    {
      "date": "2026-08-05",
      "alerts": [ /* yesterday's six ranked alerts */ ]
    }
    // … 28 more days, ordered most-recent first, length pinned to 30 by the contract
  ]
}

Build the same thing yourself

Wire your tooling in, then pick the plan that fits.

The contract is stable enough to build against today. Pull a few briefs, sketch a small widget in your internal dashboard, and arrive at pricing already knowing what your team will use.

dawnset@polsia.app · spotted a doc bug? We read every reply.