Record a decision about one sending source
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.
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 code is internal_error. The
request_id field can be quoted to SendOps support to investigate.
application/problem+json