Record a decision about one sending source

Search Documentation

Search across all developer documentation

domain-auth

Record a decision about one sending source

PATCH /v1/domain-auth/domains/{domain}/sources/{sourceId}
Auth required api.domain_auth.manage

Records that a sending source is the org's own or one they authorised (confirmed), is not theirs (rejected), or withdraws an earlier answer (unreviewed).

Rejecting does not mute. A rejected source resolves to unknown and keeps alerting: the customer has said it is not theirs and it is still sending as their domain, which is the finding rather than its resolution. The response carries the post-decision alerts and review_state for exactly this reason — do not treat a 200 as "cleared".

Confirming does not always change anything. A source classified sendops or sendops_unaligned cannot be overridden by a decision in either direction, because SendOps holds the DKIM tokens and the SES identity configuration. The decision is stored; the class and the alerting are unchanged, and the underlying fault still needs its record.

unreviewed is a WEAKER statement than rejected, not a stronger one: it clears the decision along with its author and timestamp, and the source returns to undecided.

Reversible in every direction, so it is classified non-destructive and a test-environment credential may call it. It records a person, so a client-credentials token is refused.

This endpoint does not publish DNS, change a DMARC policy, or reissue a reporting address. No endpoint on this API does.

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.

sourceId string<uuid> required

The source group's id, from the sources list.

Request body

Content type: application/json

status string enum required

confirmed — this sender is yours or one you authorised. rejected — it is not yours; this does NOT mute it, and the source keeps alerting. unreviewed — withdraw an earlier answer and return the source to undecided; a WEAKER statement than rejected, not a stronger one.

One of: confirmed, rejected, unreviewed

label string optional

Optional human label, e.g. "Zendesk". Do not pass the network_name or top_host from the sources list — an AS name describes who announces the route, so it would record a judgement about an entire network.

Constraints: length 0–200

Responses

Errors follow the RFC 7807 problem format — see the error reference.

200 The source after the decision. Traffic decoration (traffic, ip_count, network_name, top_host) is NOT recomputed here — it costs a ClickHouse scan of the domain's IPs — so those fields come back empty. Re-read the sources list if you need them. application/json
id string<uuid> required
display_name string required

The customer's label, else a provider name, else the raw group key. Never a bare IP.

group_kind string enum required

What the row is keyed on. provider is a fingerprint and survives IP churn; prefix is a /24 or /48 and will churn.

One of: provider, asn, prefix

group_key string required
class string enum required

What SendOps classified, before the customer's decision.

One of: sendops, sendops_relayed, sendops_unaligned, unknown

effective_class string enum required

The classification resolved against the decision — the field to reason with. sendops and sendops_unaligned cannot be overridden by a decision in either direction.

One of: sendops, sendops_relayed, sendops_unaligned, known, unknown

confidence string enum required

How strongly the evidence supports class. certain is reachable only by a DKIM selector match — nothing inferred from an IP range reaches it, because SES is shared infrastructure.

One of: certain, high, medium, low

status string enum required

One of: unreviewed, confirmed, rejected

review_state string enum required

One of: none, awaiting_decision, open_finding

alerts boolean required

Whether this source raises the customer's attention. A REJECTED source still alerts.

decided_at string<date-time> optional
first_seen_at string<date-time> optional
last_seen_at string<date-time> optional
volume_30d integer<int64> required
ip_count integer required
network_name string optional

DESCRIBES the group; never IDENTIFIES it. An AS name says who announces the route, so treating it as the sender's identity would scope a decision to an entire cloud provider.

top_host string optional
top_host_others integer optional
traffic object | null required
diagnosis object | null required
annotations array<object> optional

What we can say this source IS, as opposed to what is wrong with it (SND-1395). Absent or empty for most rows, and that is the correct answer rather than a gap: a group nothing matched has nothing said about it. Derived at read time, never stored, and it gates nothing — no verdict, alert, class or review state reads it.

Each entry in annotations:

kind string enum required

identity names what a sender is. infrastructure describes the machinery a message travelled over. caution warns that an identification is weaker than it looks and DEMANDS NO ACTION — it carries no doc_url on purpose, because the only way to "complete" it would be to confirm the source, which is the mistake it exists to prevent. gap names a configuration hole and links the vendor's fix.

One of: identity, infrastructure, caution, gap

title string required

A short noun phrase. Never an instruction.

body string required

One plain sentence, situation first, written for someone who has never heard the word "alignment".

doc_url string optional

The VENDOR's own page where one exists, empty otherwise. Empty is common, and on a caution it is deliberate.

confidence string enum required

confirmed means a vendor document or our own live DNS says it; strong means several credible secondary sources agree. Shown as-is — anything weaker is folklore and never reaches a table, so there is no third value and no rounding up.

One of: confirmed, strong

shared_pool object | null optional
401 Missing, malformed, or unknown API key application/problem+json
403 Key lacks the required scope or plan limit violated application/problem+json
404 Resource not found application/problem+json
422 Query parameter or path value failed validation application/problem+json
429 Per-org rate limit exceeded application/problem+json
500 Unexpected server-side failure. The code is internal_error. The request_id field can be quoted to SendOps support to investigate. application/problem+json