What changed on this domain's DMARC configuration, and when
The notable moments in one domain's DMARC life, newest first: policy changes, record edits that did NOT move the policy, source decisions, acknowledgements, and the customer's own notes.
The unit here is an EVENT, not a stored annotation, and the difference matters. One real change usually produces two stored rows. A live-DNS poll sees the new record and records it with the exact text either side and a tight timestamp; a day or two later the receivers' aggregate reports arrive describing the same day, and the report-derived detector reconstructs the same transition with the policy alone and a date bucketed to midnight UTC. Both rows are correct and both are kept.
This endpoint folds them, so one change is one event. Nothing is hidden
by the folding: every event lists its sightings, naming the annotation
id, the origin and the timestamp behind each. Read anchor_origin to
know which sighting supplied occurred_at and the record text — a dns
anchor means a poll timestamp, a reports anchor means a day bucket,
and the second is the only kind available for a change made before the
domain was onboarded.
Two annotations fold into one event when they share a kind, share the
same old_policy → new_policy pair, come from different origins, and
fall within three days of each other. Notes, acknowledgements and source
decisions never fold: each is a separate act by a separate person.
This surface carries no email addresses. The dashboard's timeline
names the colleague who published a change or wrote a note; this one
does not, because api.domain_auth.view promises no personal data and a
read-only DMARC token should not double as a way to enumerate an org's
staff. Read anchor_origin to tell a moment a person created (user)
from one a detector observed.
Path parameters
domain string required The domain, by NAME (acme.com). A UUID is also accepted. Matched case-insensitively, and scoped to the calling org — a domain belonging to another org is a 404, never a 403.
Responses
Errors follow the RFC 7807 problem format — see the error reference.
events array<object> required Each entry in events:
id string<uuid> required The ANCHOR sighting's annotation id — a row that really exists, not a synthetic identifier.
kind string enum required policy_change moved the published p= or sp=. setup_change is a record edit that did NOT move the policy — a rewritten rua tag, a new reporting address — and it is a separate kind rather than a variant because it is how reporting silently stops, which is the commonest way a healthy domain becomes an unmonitored one.
One of: policy_change, setup_change, source_decisions, acknowledgement, note
anchor_origin string enum required Which sighting supplied occurred_at and the record text. The dns sighting wins whenever there is one: its timestamp comes from a poll that either saw the record or did not, where the report-derived one is a day bucket assembled from mail receivers had already judged.
One of: dns, reports, user, system
occurred_at string<date-time> required observed_at string<date-time> required The EARLIEST observation across the sightings — when we first knew.
old_policy string enum required The p= before the change. "" means no policy was published — a real prior state, not a missing value, and the state a first publish moves out of.
One of: ``, none, quarantine, reject
new_policy string enum required One of: ``, none, quarantine, reject
old_record string required The _dmarc TXT before the change, verbatim as observed. Empty when no sighting carried record text — the ordinary state of a change only the reports saw.
new_record string required The _dmarc TXT after the change, verbatim as observed.
tightening boolean required Whether this was a move UP the ladder. Always false for a setup_change however much of the record it altered: a rua rewrite is not a rung, and treating one as a rung would put a domain into a fourteen-day watch window for an edit receivers do not act on.
Derived here rather than left to the caller, because re-deriving it from the two policy fields means reproducing the rule that an absent record ranks BELOW none — and a caller who misses that gets both the first publish and the total loss of a record backwards.
weakening boolean required A move DOWN the ladder, including a record that disappeared entirely — which is the worst version of it and the one nothing else in the product can see.
note string optional The customer's own text, for a note event.
sightings array<object> required The annotations behind this event, oldest first. Always at least one.
Each entry in sightings:
annotation_id string<uuid> required The stored row.
origin string enum required Where the fact came from. dns is a live lookup of the published record: exact text, a tight timestamp, and it describes only the moment of the poll. reports is reconstructed from the policy receivers said they applied: it lags 24-48 hours, is coarse to the day, and is the ONLY origin that can describe a change made before the domain was onboarded. user is a person. system is us.
One of: dns, reports, user, system
occurred_at string<date-time> required When the thing happened, read from this sighting's evidence.
observed_at string<date-time> required When SendOps noticed. Differs from occurred_at by 24-48 hours whenever the evidence is a DMARC report, and reading the second as the first would place a policy change after mail that was already judged under it.
has_record_text boolean required Whether this sighting carried the _dmarc TXT either side. Always false for a report-derived sighting: receivers report the policy they applied, never the record that set it.
annotations integer required How many stored rows the events were folded from. Reported beside the event count rather than left implicit, so a caller can see the folding happened: 12 annotations, 7 events is the difference between a domain both detectors watch and one only the reports reach.
code is internal_error. The
request_id field can be quoted to SendOps support to investigate.
application/problem+json