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.

ConditionHTTP statusError
Invalid JSON400invalid_json_request
Missing or invalid message400invalid_request
Query string parameters400query_parameters_not_supported
Invalid trusted boundary400invalid_trusted_boundary
Invalid XARF request400invalid_xarf_request
Invalid abuse_report request object400invalid_abuse_report_request
XARF evidence over 5MB400xarf_evidence_too_large
Sending requested without server config503abuse_report_not_configured