What you will build

The other guides go deep on one subject. This one stays at the top: is everything fine, and if not, what to do first. The API lets you:

  • post a weekly digest to a team channel: global score, the few actions that matter, what changed;
  • build a status tile in your own dashboard or BI, next to your other metrics;
  • track recommendations like tickets, and close them once the fix is in.
Endpoint reference This guide shows which calls to chain and what to read in each response. Every field is documented in the API reference, under Overview, Recommendations and TLSRPT, generated from the API itself.

Before you start

  • An API key created from the dashboard, under Account, Developer, on a plan that includes API access.
  • Something to supervise: verified domains or IPs. The more sources are connected (DMARC, TLS reports, ESP, Postmaster), the more complete the picture.
export SA_KEY="sa_live_your_key"
export SA_API="https://senderaudit.com/api/v1"
import os, requests

API = "https://senderaudit.com/api/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['SA_KEY']}"

Step 1: the state of the account in one call

GET /overview returns what the dashboard home shows, for every domain and IP of the account, or for one resource group with ?group=.

curl -s "$SA_API/overview" -H "Authorization: Bearer $SA_KEY" | jq '{global, kpis}'
ov = session.get(f"{API}/overview").json()
g, k = ov["global"], ov["kpis"]
print(f'Score {g["score"]}/100 ({g["verdict"]}), {g["attention_count"]} of {g["assets_count"]} assets with actions to take')

The verdict

global sums up the account:

FieldWhat it is
scoreAverage of the health scores of every domain and IP, out of 100. Capped at 50 while one of them is in crisis
verdictgood, attention (warnings to deal with) or critical (a failure, or a critical blocklisting)
crisisThe asset on a critical blocklist, if any: type, id, name
trend_30dScore change over 30 days
assets_count, attention_countDomains and IPs, and how many of them have at least one action to take

The KPIs

kpis holds four rates over the last 30 days, each with its value over the 30 days before (_prev) to show a trend. All are between 0 and 1, null when there is no data.

FieldWhat it isComes from
dmarc_pass_rateShare of the DMARC volume that is legitimate, identified relays includedDMARC reports
esp_bounce_rateBounces over sentConnected ESP
esp_spam_rateComplaints over deliveredConnected ESP
critical_actionsNumber of actions with a failure statusHealth cards

What to do, and on what

  • actions: every fix across all domains and IPs, sorted by gain, the points it would bring back. Each one names its asset (asset_type, asset_id, asset_name), with a label, a status (fail or warn) and a summary. The first five make a good to-do list.
  • assets: one line per domain and IP with its score, status (healthy, attention, critical, crisis), counts of failures and warnings, and a sparkline.
  • activity: the latest notable events (blocklisting, DMARC policy change, new sending source…), the same as domain events.
  • sources: which data sources are connected (domains, ips, dmarc, tlsrpt, esp, gpt), and a precision of partial, good or complete. A partial precision means the score rests on few signals: say so in your report.

A weekly digest

lines = [f'*Deliverability: {g["score"]}/100, {g["verdict"]}*']
if g["crisis"]:
    lines.append(f'Blocklisted: {g["crisis"]["name"]}')
if k["dmarc_pass_rate"] is not None:
    lines.append(f'DMARC compliance {k["dmarc_pass_rate"]:.1%} (was {k["dmarc_pass_rate_prev"] or 0:.1%})')
for a in ov["actions"][:5]:
    lines.append(f'• {a["asset_name"]}: {a["label"]} (+{a["gain"]})')
message = "\n".join(lines)  # post it to your chat tool's incoming webhook

Step 2: track the recommendations

Health cards say what is wrong right now. Recommendations go further: every night, Sender Audit evaluates rules on the history of each domain (a DMARC failure spike, a policy left at none for too long, a legitimate sender that started failing, a complaint rate above Gmail’s limit, an SPF record over 10 lookups…) and on correlations between signals.

GET /recommendations lists them, most severe first. Filters: domain, group, severity and status (active by default, resolved, dismissed, all).

curl -s "$SA_API/recommendations?severity=critical" -H "Authorization: Bearer $SA_KEY"
for rec in session.get(f"{API}/recommendations").json():
    print(rec["severity"], rec["domain"], rec["rule_id"], rec["evidence_json"])
FieldWhat it is
rule_idThe rule that fired, stable over time: auth_dmarc_fail_spike, auth_spf_invalid, auth_spf_lookup_limit, auth_legit_sender_failing, rep_rbl_critical, eng_spam_rate_high, eng_bounce_spike…
severitycritical, warning, info or positive
categoryauthentication, reputation or engagement
evidence_jsonThe figures behind it, for example the spam rate or the score before and after
action_urlWhere to fix it in the dashboard, relative to https://senderaudit.com
detected_at, expires_atWhen it was last confirmed, and when it lapses if it is not confirmed again

rule_id plus domain identifies a recommendation: it is the key to use to open one ticket per recommendation in your own tracker, and to avoid duplicates.

A recommendation resolves itself when its condition disappears. You can also close it:

Step 3: TLS reporting

TLS reports (TLS-RPT, RFC 8460) are the inbound counterpart of DMARC: servers that send to you report whether they managed to deliver over an encrypted connection, and why not when they failed. They matter as soon as you publish MTA-STS or DANE.

GET /tlsrpt/address returns your collection address and the ready-made record, txt_record, to publish at _smtp._tls.example.com:

v=TLSRPTv1; rua=mailto:your-address@...

GET /domains/{id}/setup proposes the same record, completed with what is already published.

Then:

  • GET /tlsrpt/summary: success_rate as a percentage (0 to 100), totals of successful and failed sessions, and the five most frequent failure types (top_failures). Filters domain and group.
  • GET /tlsrpt/stats: successes and failures per day, over days.
  • GET /tlsrpt/reports lists the reports, order=failures puts those with failures first; GET /tlsrpt/reports/{id} details each failure: type (certificate-expired, sts-policy-fetch-error…), sending and receiving servers, number of sessions.
tls = session.get(f"{API}/tlsrpt/summary").json()
if tls["success_rate"] is not None and tls["success_rate"] < 99:
    print(f'TLS success {tls["success_rate"]} %:', [(f["result_type"], f["session_count"]) for f in tls["top_failures"]])

A sudden drop usually means an expired certificate on one of your MX, or an MTA-STS policy that no longer matches them.

Limits and errors

SituationAnswer
Too many calls on an endpoint429, with a Retry-After header
Recommendation id unknown on the account404
Missing or invalid key401
Plan without API access403