---
title: "Diagnose why mail from a domain is not arriving"
method: GET
path: "/api/v1/deliverability/diagnose"
tags: ["Deliverability"]
---

# Diagnose why mail from a domain is not arriving

`GET /api/v1/deliverability/diagnose`

## Request

- Base URL: `https://api.sendly.now`
- URL: `https://api.sendly.now/api/v1/deliverability/diagnose`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `better-auth.session_token`
  - OAuth 2 (authorization code, authorization URL `https://app.sendly.now/api/auth/oauth2/authorize`, token URL `https://app.sendly.now/api/auth/oauth2/token`, scopes: `deliverability:read`)

Reads the sending domain's DNS identity, the project's delivery outcomes over the last `window_days`, and — when an `address` is given — that recipient's suppression state, then publishes `findings`: what is actually wrong, worst first, each with a stable `code` and the fix. Branch on `code`, never on the prose.

Everything here is read from data the platform already holds. The DNS statuses are the cached results of the verification refresh job, NOT a live lookup — `identity.last_checked_at` says when they were filled, and a domain that has never been checked reports nulls with a `dns_never_checked` finding rather than failures.

`recent_delivery` is PROJECT-WIDE, not per-domain, because an email row records no sending domain; the field says so in its own `scope`. Rates are suppressed as meaningless below 20 sends in the window.

Requires the `deliverability:read` scope — Check why mail from one of your domains is not arriving.

## Query parameters

- `domain` string, required: A sending domain in this project, e.g. `example.com`.
- `address` string, email: Optionally, one RECIPIENT address to check as well. Adds its suppression state — the single most common reason a specific person stops receiving mail while everyone else still does.
- `window_days` integer: How far back the delivery counters look. 1–30 days; defaults to 7.

## Response `200`

The diagnosis, with findings

- DeliverabilityDiagnosisV1: A composed answer to why mail from one domain may not be arriving: its DNS identity, the project's recent delivery outcomes, one address's suppression state, and the findings drawn from them.
  - `address` string, nullable, required
  - `checked_at` string, date-time, required
  - `domain` string, required
  - `findings` DeliverabilityFindingV1[], required: What is wrong, worst first. An empty array means nothing here explains a delivery problem.
    - `code` string, required: Stable identifier for this finding, e.g. `domain_not_verified`. Branch on this, not on `summary`.
    - `remedy` string, required: What to do about it.
    - `severity` 'blocking' | 'degraded' | 'info', required: `blocking`: mail from this domain cannot be delivered as configured. `degraded`: it delivers, but inbox placement or sender reputation is at risk. `info`: worth knowing, nothing to fix.
    - `summary` string, required: What is wrong, in one sentence.
  - `identity` DeliverabilityIdentityV1, required: The sending identity's DNS health, as last refreshed.
    - `dkim_status` 'NOT_CHECKED' | 'PENDING' | 'VERIFIED' | 'FAILED' | 'null', nullable, required: DKIM signing. This is the one that decides whether Sendly will send from the domain at all.
    - `dmarc_status` 'NOT_CHECKED' | 'PENDING' | 'VERIFIED' | 'FAILED' | 'null', nullable, required: The DMARC policy published at `_dmarc.<domain>`.
    - `last_checked_at` string, date-time, nullable, required: When the DNS refresh job last looked. These statuses are a CACHE, not a live lookup.
    - `mail_from_domain` string, nullable, required
    - `mail_from_domain_status` string, nullable, required: Raw SES `CustomMailFromStatus` (`Pending`/`Success`/`Failed`/`TemporaryFailure`), or `NotConfigured`. `Failed` means SES silently fell back to `amazonses.com`, which is why the value is reported rather than folded into a boolean.
    - `mx_status` 'NOT_CHECKED' | 'PENDING' | 'VERIFIED' | 'FAILED' | 'null', nullable, required: Inbound receiving only. Null unless the domain has receiving enabled.
    - `registered` boolean, required: Whether this project has a domain record at all. False makes every other field null.
    - `spf_status` 'NOT_CHECKED' | 'PENDING' | 'VERIFIED' | 'FAILED' | 'null', nullable, required: SPF alignment for the sending identity.
    - `verified` boolean, required
  - `recent_delivery` DeliverabilityRecentDeliveryV1, required: Delivery outcomes over the requested window.
    - `bounce_rate` number, nullable, required: Bounced ÷ sent (0–1), or null when nothing was sent in the window.
    - `bounced` integer, required
    - `complained` integer, required
    - `complaint_rate` number, nullable, required
    - `delivered` integer, required
    - `failed` integer, required
    - `scope` 'project', required: PROJECT-WIDE, not per-domain, and said so rather than implied. `Email` has no sending-domain column, so narrowing these counters to one domain would mean a scan over every row the project has ever sent. A project that sends from one domain — most of them — can read these as that domain's.
    - `sent` integer, required
    - `window_days` integer, required
  - `suppression` DeliverabilitySuppressionV1, nullable, required: Null unless the request named an `address`.
    - `reason` 'HARD_BOUNCE' | 'COMPLAINT' | 'MANUAL' | 'UNSUBSCRIBE' | 'null', nullable, required
    - `source` 'SES_WEBHOOK' | 'API' | 'DASHBOARD' | 'null', nullable, required
    - `suppressed` boolean, required
    - `suppressed_at` string, date-time, nullable, required

## Other responses

- `401`: `invalid_api_key` or `invalid_session` — missing or invalid credentials.
- `403`: `scope_missing`, `project_access_denied`, or `project_disabled`.
- `422`: `validation_error` — query, path, or body parameters did not match the schema.
- `429`: `rate_limited` — see `Retry-After` and the `RateLimit` headers.
- `500`: `internal_error`.

---

[API](https://skmtc.dev/sendly/apis/sendly-api.md) · [All operations](https://skmtc.dev/sendly/apis/sendly-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sendly/apis/sendly-api/revisions/ce7cfb7f7c7e?raw)
