Skip to content

Report Incident#

The incident report endpoint provides a way to submit incidents to PhishFort analysts for multiple scenarios including:

  • Submitting an incident for takedown.
  • Submitting an incident for monitoring (eg. the incident is suspicious but its current state does not warrant a takedown).

If submitting an incident on behalf of a sub-client, please make sure to use the clientId parameter as described in the Requesting a takedown for a specific sub-client example.

Endpoints#

POST /v1/incident/tkd — Takedown request

POST /v1/incident/monitor — Monitoring request

Parameters#

Parameter Type Required Description
url string Conditional The URL or domain of the incident. Required for domain/url incidents unless subject is provided.
incidentType string Conditional Required if not a domain/url incident. Values: email, phone, ipv4.
subject string Conditional Required when incidentType is email, phone, or ipv4. The subject value of the incident.
reportedBy string No Email address of the submitter. Required for the reporter to receive status emails. Highly recommended.
clientId string No Sub-client ID to report on behalf of. Only for clients with managed sub-clients.
comment string No Comment displayed to the analyst handling the case. Up to 5,000 characters.
attachments file[] No Multipart file field. Repeat the attachments field to upload multiple files.

Conditional requirements

  • For domain/url incidents: provide url.
  • For email, phone, or ipv4 incidents: provide both incidentType and subject.
  • The comment field, if provided, cannot be null or empty, and is limited to 5,000 characters. A longer comment returns 400 and the incident is not created; see Limits.
  • The phone incident type is validated against E.164 format.

URL and domain corrections#

A reported url or domain is corrected before it is saved and before it is checked for duplicates:

Correction Sent Saved as
Leading and trailing spaces are removed " https://example.com " https://example.com
Defanged URLs are restored hxxps://example[.]com https://example.com
The scheme and host are lowercased; the path and query are kept as sent HTTPS://Example.com/Login https://example.com/Login

Defanged forms that are restored: hxxp / hxxps schemes (any letter case), [.], [:] and [://]. A value with spaces inside it, or several values separated by commas, is still rejected with 400.

Duplicate matching uses the corrected value, so hxxps://example[.]com, HTTPS://Example.com and https://example.com are treated as the same incident.

Attachment limits

Multipart incident reports can include up to 12 files by repeating the attachments field, and the total request size must stay under 10 MB. See Limits for details.

Request Example (Requesting a takedown)#

curl -X POST 'https://capi.phishfort.com/v1/incident/tkd' \
  -H 'accept: application/json' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://somethingbad.com", "reportedBy": "john.doe@company.com"}'
import requests

response = requests.post(
    "https://capi.phishfort.com/v1/incident/tkd",
    headers={
        "accept": "application/json",
        "x-api-key": "YOUR_API_KEY",
    },
    json={
        "url": "https://somethingbad.com",
        "reportedBy": "john.doe@company.com",
    },
)
print(response.json())
const response = await fetch("https://capi.phishfort.com/v1/incident/tkd", {
  method: "POST",
  headers: {
    accept: "application/json",
    "x-api-key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://somethingbad.com",
    reportedBy: "john.doe@company.com",
  }),
});
const data = await response.json();
console.log(data);

Request Example (Requesting a takedown for a specific sub-client)#

curl -X POST 'https://capi.phishfort.com/v1/incident/tkd' \
  -H 'accept: application/json' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://somethingbad.com", "clientId": "client_id_here", "reportedBy": "john.doe@company.com"}'
import requests

response = requests.post(
    "https://capi.phishfort.com/v1/incident/tkd",
    headers={
        "accept": "application/json",
        "x-api-key": "YOUR_API_KEY",
    },
    json={
        "url": "https://somethingbad.com",
        "clientId": "client_id_here",
        "reportedBy": "john.doe@company.com",
    },
)
print(response.json())
const response = await fetch("https://capi.phishfort.com/v1/incident/tkd", {
  method: "POST",
  headers: {
    accept: "application/json",
    "x-api-key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://somethingbad.com",
    clientId: "client_id_here",
    reportedBy: "john.doe@company.com",
  }),
});
const data = await response.json();
console.log(data);

Request Example (Requesting with subject)#

curl -X POST 'https://capi.phishfort.com/v1/incident/tkd' \
  -H 'accept: application/json' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"subject": "192.168.1.1", "incidentType": "ipv4", "reportedBy": "john.doe@company.com"}'
import requests

response = requests.post(
    "https://capi.phishfort.com/v1/incident/tkd",
    headers={
        "accept": "application/json",
        "x-api-key": "YOUR_API_KEY",
    },
    json={
        "subject": "192.168.1.1",
        "incidentType": "ipv4",
        "reportedBy": "john.doe@company.com",
    },
)
print(response.json())
const response = await fetch("https://capi.phishfort.com/v1/incident/tkd", {
  method: "POST",
  headers: {
    accept: "application/json",
    "x-api-key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    subject: "192.168.1.1",
    incidentType: "ipv4",
    reportedBy: "john.doe@company.com",
  }),
});
const data = await response.json();
console.log(data);

Where incidentType can be any of the following: phone, email, ipv4. The subject field should have the value of the appropriate incidentType. Please note that all the values will be validated according to their format where phone incidentType will be validated against E.164 format.

Request Example (Report with comment)#

curl -X POST 'https://capi.phishfort.com/v1/incident/monitor' \
  -H 'accept: application/json' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://somethingsuspicious.com", "comment": "This is extremely important", "reportedBy": "john.doe@company.com"}'
import requests

response = requests.post(
    "https://capi.phishfort.com/v1/incident/monitor",
    headers={
        "accept": "application/json",
        "x-api-key": "YOUR_API_KEY",
    },
    json={
        "url": "https://somethingsuspicious.com",
        "comment": "This is extremely important",
        "reportedBy": "john.doe@company.com",
    },
)
print(response.json())
const response = await fetch(
  "https://capi.phishfort.com/v1/incident/monitor",
  {
    method: "POST",
    headers: {
      accept: "application/json",
      "x-api-key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url: "https://somethingsuspicious.com",
      comment: "This is extremely important",
      reportedBy: "john.doe@company.com",
    }),
  }
);
const data = await response.json();
console.log(data);

Request Example (Report with attachments)#

curl -X POST 'https://capi.phishfort.com/v1/incident/tkd' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: multipart/form-data' \
  --form 'url="https://somethingbad.com"' \
  --form 'reportedBy="john.doe@company.com"' \
  --form 'attachments=@"/path/to/file-1.pdf"' \
  --form 'attachments=@"/path/to/file-2.png"'
import requests

with open("/path/to/file-1.pdf", "rb") as file_1, open("/path/to/file-2.png", "rb") as file_2:
    response = requests.post(
        "https://capi.phishfort.com/v1/incident/tkd",
        headers={"x-api-key": "YOUR_API_KEY"},
        data={
            "url": "https://somethingbad.com",
            "reportedBy": "john.doe@company.com",
        },
        files=[
            ("attachments", file_1),
            ("attachments", file_2),
        ],
    )
print(response.json())
const fs = require("fs");
const FormData = require("form-data");

const form = new FormData();
form.append("url", "https://somethingbad.com");
form.append("reportedBy", "john.doe@company.com");
form.append("attachments", fs.createReadStream("/path/to/file-1.pdf"));
form.append("attachments", fs.createReadStream("/path/to/file-2.png"));

const response = await fetch("https://capi.phishfort.com/v1/incident/tkd", {
  method: "POST",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    ...form.getHeaders(),
  },
  body: form,
});
const data = await response.json();
console.log(data);

Request Example (Requesting the incident be monitored)#

curl -X POST 'https://capi.phishfort.com/v1/incident/monitor' \
  -H 'accept: application/json' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://somethingsuspicious.com", "reportedBy": "john.doe@company.com"}'
import requests

response = requests.post(
    "https://capi.phishfort.com/v1/incident/monitor",
    headers={
        "accept": "application/json",
        "x-api-key": "YOUR_API_KEY",
    },
    json={
        "url": "https://somethingsuspicious.com",
        "reportedBy": "john.doe@company.com",
    },
)
print(response.json())
const response = await fetch(
  "https://capi.phishfort.com/v1/incident/monitor",
  {
    method: "POST",
    headers: {
      accept: "application/json",
      "x-api-key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url: "https://somethingsuspicious.com",
      reportedBy: "john.doe@company.com",
    }),
  }
);
const data = await response.json();
console.log(data);

Response Example#

If successful, the response normally contains a success message, an id used by later incident endpoints, and a dashboard url. Treat id as an opaque string: its format can vary and integrations must not infer an internal storage system from it. Use it with the Single Incident Query API.

{
    "message": "Successfully reported the incident",
    "id": "inc_A1b2C3d4E5f6G7h8I9j0",
    "url": "https://dashboard.phishfort.com/incident/inc_A1b2C3d4E5f6G7h8I9j0"
}

Duplicate Reports#

Prevent duplicate submissions in your integration; the report endpoints do not accept an idempotency key. A duplicate can be returned in either of these forms:

  • 409 Conflict with a message that the incident already exists.
  • 200 OK with no id or url when the report is accepted by an older processing path:
{
    "message": "Successfully reported but this incident may be a duplicate"
}

Always check both the HTTP status and the presence of id. Do not retry automatically and do not parse the human-readable error message as a stable identifier. Route an unlinked duplicate for manual handling.

GET /v1/incident/subject/{subject} can resolve email, phone, and ipv4 incidents, but it is not a general lookup-by-URL or idempotency endpoint. Integrations must persist the successful report response's id alongside their own ticket, case, or alert record.

Rate Limit#

Submissions are limited to 60 per minute per account, counted across all report endpoints and across all keys and sub-clients of the account. A submission beyond the budget is not created and receives:

  • 429 Too Many Requests with a Retry-After header holding the seconds until the budget resets.
{
    "error": true,
    "message": "Too many incident reports: this account has reached its limit of 60 reports per minute. Retry after 30 seconds."
}

Wait for the Retry-After period, then resend the same report. See Limits for the full rules and the recommended retry behavior.

Using the Returned ID Right Away#

The id in the response is usable immediately for reads, such as Single Incident Query. Write calls that reference it — adding an attachment, adding a comment, requesting a review, or approving a takedown — made within the first few minutes may receive a 409 instead of a 404 while the incident finishes linking to its full record. The response carries a Retry-After header (typically 30 seconds); wait and retry. This only applies in the minutes just after reporting — a 409 on an older id means something else is wrong.