HTTP interface
API
Submit RFC-822 messages as JSON and receive grouped abuse-report evidence.
The web API accepts a JSON POST request. Query string parameters are not supported; all options belong in the request body.
Minimal request
{
"message": "Return-Path: <spam@example.net>\nFrom: ...\n\nbody"
}
Example with curl:
jq -n --rawfile message spam.eml '{message: $message}' \
| curl -s -H 'Content-Type: application/json' \
--data-binary @- http://localhost:5000
Response shape
{
"complaints": {
"abuse@example.com": [
{
"type": "link",
"host": "landing.example",
"ips": ["203.0.113.42"],
"whois-abuse-email": "abuse@example.com",
"link": "https://landing.example/path"
}
]
},
"warnings": []
}
complaints is keyed by the abuse email address to contact. Each item contains the evidence that led sc to that recipient, such as a Received header, IP address, hosted link, expanded link, host, and lookup result.
warnings contains non-fatal failures, such as cache errors or abuse lookups that timed out.
When server-side sending uses aliases or provider API routes, complaints is keyed by the final recipient address. The original RDAP contact remains in each complaint as whois-abuse-email.
Trusted boundary override
You can override only trusted_boundary for one request:
{
"message": "Return-Path: <spam@example.net>\nFrom: ...\n\nbody",
"trusted_boundary": {
"name": "mx.example.com"
}
}
XARF reports
Add an xarf object to include XARF reports in the response. reporter and link_type are required.
{
"message": "Return-Path: <spam@example.net>\nFrom: ...\n\nbody",
"xarf": {
"reporter": {
"org": "Example Org",
"contact": "abuse-reports@example.com",
"domain": "example.com"
},
"link_type": "fraud",
"smtp_source_port": 25
}
}
Allowed hosted-link XARF types are phishing, malware, fraud, exposed_data, brand_infringement, remote_compromise, and suspicious_registration.
Server-side sending
If abuse_report is configured in sc.yaml, a request can ask the server to send abuse report email:
{
"message": "Return-Path: <spam@example.net>\nFrom: ...\n\nbody",
"abuse_report": {
"send": true
}
}
Sending is never automatic. The request must include abuse_report.send as JSON boolean true.
If the request includes an abuse_report object but omits send: true, the server analyzes the message without sending and includes a status block showing that delivery was not performed.
{
"abuse_report": {
"requested": true,
"sent": false,
"recipients": {}
}
}
When sending is requested, delivery status is returned per recipient and per delivery channel:
{
"abuse_report": {
"requested": true,
"sent": true,
"recipients": {
"abuse@example.net": {
"status": "sent",
"complaint_count": 1
}
},
"deliveries": [
{
"channel": "email",
"status": "sent",
"recipient": "abuse@example.net",
"complaint_count": 1
}
]
}
}
Provider API deliveries use channel: "api" and include provider, original_abuse_email, recipient, complaint_count, and, when the provider returns one, provider_report_id. Failed email or API deliveries do not change the HTTP status; they set abuse_report.sent to false and include an error field on the failed delivery.
Cloudflare API routes are configured in sc.yaml, not per request. They apply only to matching link complaints. Matching Received-header complaints are still sent by email.
Error responses
Malformed requests return JSON error bodies with stable error codes.
| Condition | HTTP status | Error |
|---|---|---|
| Invalid JSON | 400 | invalid_json_request |
Missing or invalid message | 400 | invalid_request |
| Query string parameters | 400 | query_parameters_not_supported |
| Invalid trusted boundary | 400 | invalid_trusted_boundary |
| Invalid XARF request | 400 | invalid_xarf_request |
Invalid abuse_report request object | 400 | invalid_abuse_report_request |
| XARF evidence over 5MB | 400 | xarf_evidence_too_large |
| Sending requested without server config | 503 | abuse_report_not_configured |