Skip to main content

POST /v1/public/pages/{slug}/report

Submits an abuse report for a public page. This endpoint is unauthenticated. The response is always 202 Accepted regardless of whether the slug exists — this is intentional. The endpoint never reveals page existence. Reports are forwarded to PostHog (and optionally Slack) for operator review. No database write occurs; takedown is handled by operator action through the standard unpublish mechanism.

Authentication

None. This is a public endpoint.

Path parameters

ParameterTypeDescription
slugstringPage slug (pg_...) from the public URL.

Request body

{
  "reason": "spam",
  "detail": "This page is impersonating a financial institution."
}
FieldTypeRequiredDescription
reasonstringNoReport category. One of: "spam", "phishing", "malware", "abuse", "copyright", "other". Unrecognized values are stored as "other".
detailstringNoFree-text description. Truncated to 2,000 characters server-side.

Response

{
  "received": true
}
HTTP status: 202 Accepted.
FieldTypeDescription
receivedbooleanAlways true. Indicates the report was accepted for review, not that the page was taken down.

Error codes

CodeStatusWhen
rate_limited429Too many reports from this IP for this slug.
All other responses are 202. A 202 for a missing slug is indistinguishable from a 202 for a valid one — this is intentional to prevent slug enumeration.

Notes

  • No oracle: 202 is returned for any slug, including ones that do not exist.
  • Async takedown: Submitting a report does not immediately take down the page. Operator review is required. Confirmed violations are handled via DELETE /v1/artifacts/{id}/publish.
  • Reason coercion: If reason is missing, not a string, or not one of the recognized values, it is stored as "other".

Example

curl -X POST https://api.artifacta.io/v1/public/pages/pg_aB3xK9mP1qR5sT2u/report \
  -H "Content-Type: application/json" \
  -d '{"reason": "phishing", "detail": "Impersonating a bank login page."}'