{
    "openapi": "3.0.3",
    "info": {
        "title": "nebty API",
        "version": "1.0.0",
        "description": "Read access to your brand-protection data in nebty: the brands being monitored, the domains found impersonating them, the scan results behind each finding and your takedown cases. You can also request a takedown.\n\n**Base URL:** `https://app.nebty.de`\n\n**Authentication.** Send your API key in the `Authorization` header as `Authorization: Api-Key <key>`. A key acts as the user it belongs to, and results are limited to what that user may see: the brands the user is a member of, and takedowns the user requested or that belong to one of those brands. Records you cannot see are treated as if they did not exist.\n\n**Identifiers** are UUIDs.\n\n**Pagination.** List endpoints are page-number paginated. Pass `page` (1-based) and `page_size`. The default page size is 50 and the maximum is 100. A paginated response is an object with `count` (total rows), `next` and `previous` (URLs or `null`) and `results`; the total is also sent in the `X-Total-Count` response header. Keep `page_size` and all filters unchanged while walking through pages.\n\n**Compact rows.** The brand list and the per-brand domain and scan lists accept `compact=true` and then return a short summary per row instead of the full record with embedded scan history. Use it for lists and fetch a single domain for the full evidence. It is much lighter for large brands.\n\n**Undocumented fields.** Responses can contain additional fields that are not documented here. They are not part of the public contract and can change or disappear without notice."
    },
    "paths": {
        "/core/api/brands/": {
            "get": {
                "operationId": "listBrands",
                "description": "Returns the brands the API key's user is a member of, ordered by name (case-insensitive).\n\nPaginated: `page` and `page_size` (default 50, maximum 100). See the pagination note in the API description.\n\nQuery parameters:\n\n* `compact=true` returns a short row per brand (`id`, `name`, `domain`, `plan`, `monitoring_enabled`, `brand_markets`) and is the recommended form for listing.\n* `include_stats=true` (or `1`) adds headline figures to each full brand row: domain and scan counts, latest scan date, high-risk count, average risk score and the risk, verdict and HTTP-status breakdowns. It has no effect with `compact=true`.",
                "parameters": [
                    {
                        "in": "query",
                        "name": "compact",
                        "schema": {
                            "type": "boolean"
                        },
                        "description": "Return compact list summaries without embedded history. Use domain detail for evidence."
                    },
                    {
                        "name": "page",
                        "required": false,
                        "in": "query",
                        "description": "A page number within the paginated result set.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "page_size",
                        "required": false,
                        "in": "query",
                        "description": "Number of results to return per page.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "tags": [
                    "Brands"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/PaginatedBrandListItemList"
                                }
                            }
                        },
                        "description": ""
                    },
                    "401": {
                        "description": "The API key is missing, invalid, revoked or belongs to an inactive user.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests or failed authentication attempts. Try again later.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "The requested page or record does not exist or cannot be accessed.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    }
                },
                "summary": "List brands"
            }
        },
        "/core/api/brands/{id}/": {
            "get": {
                "operationId": "getBrand",
                "parameters": [
                    {
                        "in": "path",
                        "name": "id",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "description": "UUID of the brand, as returned in the `id` field of the brand list.",
                        "required": true
                    }
                ],
                "tags": [
                    "Brands"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Brand"
                                }
                            }
                        },
                        "description": ""
                    },
                    "401": {
                        "description": "The API key is missing, invalid, revoked or belongs to an inactive user.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests or failed authentication attempts. Try again later.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "The requested page or record does not exist or cannot be accessed.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    }
                },
                "summary": "Get a brand",
                "description": "Returns one brand: name, domain, plan, monitoring settings, keywords, trusted domains and the API key's user's role in it (`my_role`). Answers `404` if the brand does not exist or the user is not a member of it."
            }
        },
        "/core/api/brands/{id}/domains/": {
            "get": {
                "operationId": "listBrandDomains",
                "description": "Returns the domains associated with a brand, with filtering and sorting. `risk_score` and `verdict` describe the pairing of this brand with the domain, not the domain on its own. The default order is highest risk first.\n\nPaginated: `page` and `page_size` (default 50, maximum 100). See the pagination note in the API description.\n\nUse `compact=true` for a short row per domain (`id`, `name`, `source`, a compact `latest_scan` and `open_takedown`). Without it every row embeds scan history and is much larger; fetch a single domain when you need the evidence.\n\nFilters: `domain_filter` (case-insensitive substring of the name), `http_status_filter` (`online`, `offline`, `all`), `risk_score_filter` (`low`, `medium`, `high`, `all`), `verdict_filter` (`red`, `amber`, `green`, `unknown`, `all`) and `triage_filter`. `visual_match_filter` is deprecated; use `verdict_filter`. Sort with `sort_field` and `sort_direction`. `group_by=apex` returns one row per registrable domain, with its subdomains collapsed, instead of one row per domain. An unsupported `group_by` or `triage_filter` value is rejected with `400`.",
                "parameters": [
                    {
                        "in": "query",
                        "name": "compact",
                        "schema": {
                            "type": "boolean"
                        },
                        "description": "Return compact list summaries without embedded history. Use domain detail for evidence."
                    },
                    {
                        "in": "query",
                        "name": "domain_filter",
                        "schema": {
                            "type": "string"
                        },
                        "description": "Filter domains by name (case-insensitive substring match)"
                    },
                    {
                        "in": "query",
                        "name": "group_by",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "apex",
                                "none"
                            ]
                        },
                        "description": "Collapse the list to one row per registrable apex (eTLD+1) so a third-party domain and all its subdomains take one decision. Applied after every filter. With 'apex' the response items are BrandDomainApexGroup objects, not Domain objects, and the paginated `count` is the number of apexes — summing `domain_count` over all pages reproduces the ungrouped total for the same filter. Default 'none'."
                    },
                    {
                        "in": "query",
                        "name": "http_status_filter",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "all",
                                "offline",
                                "online"
                            ]
                        },
                        "description": "Filter by HTTP status: 'online', 'offline', or 'all'"
                    },
                    {
                        "in": "path",
                        "name": "id",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "description": "UUID of the brand, as returned in the `id` field of the brand list.",
                        "required": true
                    },
                    {
                        "in": "query",
                        "name": "page",
                        "schema": {
                            "type": "integer"
                        },
                        "description": "Page number within the paginated result set."
                    },
                    {
                        "in": "query",
                        "name": "page_size",
                        "schema": {
                            "type": "integer"
                        },
                        "description": "Rows per page. Defaults to 50; clamped to 100 above that."
                    },
                    {
                        "in": "query",
                        "name": "risk_score_filter",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "all",
                                "high",
                                "low",
                                "medium"
                            ]
                        },
                        "description": "Filter by risk score level: 'low', 'medium', 'high', or 'all'"
                    },
                    {
                        "in": "query",
                        "name": "sort_direction",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "asc",
                                "desc"
                            ]
                        },
                        "description": "Sort direction"
                    },
                    {
                        "in": "query",
                        "name": "sort_field",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "domain",
                                "http_status",
                                "review_priority",
                                "risk_score",
                                "visual_match"
                            ]
                        },
                        "description": "Field to sort by. 'review_priority' is the layer-2 pre-ranking of what a human still has to review; unranked pairs sort below every ranked one, with risk breaking ties. Under group_by=apex only 'domain' (apex name) and 'risk_score' (the group's worst host) apply."
                    },
                    {
                        "in": "query",
                        "name": "triage_filter",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "all",
                                "escalated",
                                "filed",
                                "queue"
                            ]
                        },
                        "description": "Filter by the common-word triage outcome, for brands where it is enabled. 'queue' hides the pairs the machine filed as an unrelated entity or a name collision — the review worklist. 'filed' shows only those, for auditing what was hidden. 'escalated' shows only pairs carrying a brand-affinity token. Pairs a human has judged are never hidden by this filter and never listed as machine decisions. Default 'all', which returns every row. Note that the brand statistics endpoints (`/stats/` and `?include_stats=true`) always describe the 'queue' population, so `verdict_stats` matches the row count under 'queue' rather than under 'all'."
                    },
                    {
                        "in": "query",
                        "name": "verdict_filter",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "all",
                                "amber",
                                "green",
                                "red",
                                "unknown"
                            ]
                        },
                        "description": "Filter by the merged customer-facing verdict for this brand's pair with the domain. Accepts the same values as BrandStatsSerializer.verdict_stats' keys, plus 'all' — a stat and its filtered list can never disagree."
                    },
                    {
                        "in": "query",
                        "name": "visual_match_filter",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "all",
                                "no",
                                "unknown",
                                "yes"
                            ]
                        },
                        "description": "DEPRECATED — use verdict_filter. Filter by visual match: 'yes', 'no', 'unknown', or 'all'. Kept so the frontend does not break on deploy; the visual scan is now one of two inputs to the merged verdict, not the customer-facing answer.",
                        "deprecated": true
                    }
                ],
                "tags": [
                    "Domains"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/PaginatedBrandDomainListItemList"
                                }
                            }
                        },
                        "description": ""
                    },
                    "404": {
                        "description": "The requested page or record does not exist or cannot be accessed.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "The API key is missing, invalid, revoked or belongs to an inactive user.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests or failed authentication attempts. Try again later.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid query parameters or request data. Field errors appear directly on the response; malformed JSON has a detail message.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "anyOf": [
                                        {
                                            "$ref": "#/components/schemas/ApiErrorResponse"
                                        },
                                        {
                                            "$ref": "#/components/schemas/FieldValidationError"
                                        }
                                    ]
                                }
                            }
                        }
                    }
                },
                "summary": "List a brand's domains"
            }
        },
        "/core/api/brands/{id}/domains/{domain_id}/": {
            "get": {
                "operationId": "getBrandDomain",
                "description": "Returns one domain associated with the brand, in the full form with scan history and evidence. It is the same record as a row of the non-compact domain list, with the same brand-specific risk score and verdict. Answers `404` if the id is not a UUID or the domain is not associated with this brand.",
                "parameters": [
                    {
                        "in": "path",
                        "name": "domain_id",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "description": "UUID of a domain associated with this brand (as returned in the `id` field of the brand's domain list).",
                        "required": true
                    },
                    {
                        "in": "path",
                        "name": "id",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "description": "UUID of the brand, as returned in the `id` field of the brand list.",
                        "required": true
                    }
                ],
                "tags": [
                    "Domains"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Domain"
                                }
                            }
                        },
                        "description": ""
                    },
                    "404": {
                        "description": "The requested page or record does not exist or cannot be accessed.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "The API key is missing, invalid, revoked or belongs to an inactive user.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests or failed authentication attempts. Try again later.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    }
                },
                "summary": "Get a brand's domain"
            }
        },
        "/core/api/brands/{id}/scans/": {
            "get": {
                "operationId": "listBrandScans",
                "description": "Returns scan results for all domains associated with the brand, newest first. A domain scanned several times appears several times; use the domain endpoints for the current state of one domain.\n\nPaginated: `page` and `page_size` (default 50, maximum 100). See the pagination note in the API description.\n\nUse `compact=true` for a short row per scan (`id`, `domain`, `domain_name`, `risk_score`, `verdict`, `requires_human_review`, `https_head_request_response_code` and timestamps). Without it each row embeds the scan's history and evidence.",
                "parameters": [
                    {
                        "in": "query",
                        "name": "compact",
                        "schema": {
                            "type": "boolean"
                        },
                        "description": "Return compact list summaries without embedded history. Use domain detail for evidence."
                    },
                    {
                        "in": "path",
                        "name": "id",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "description": "UUID of the brand, as returned in the `id` field of the brand list.",
                        "required": true
                    },
                    {
                        "name": "page",
                        "required": false,
                        "in": "query",
                        "description": "A page number within the paginated result set.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "page_size",
                        "required": false,
                        "in": "query",
                        "description": "Number of results to return per page.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "tags": [
                    "Scans"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/PaginatedBrandScanListItemList"
                                }
                            }
                        },
                        "description": ""
                    },
                    "404": {
                        "description": "The requested page or record does not exist or cannot be accessed.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "The API key is missing, invalid, revoked or belongs to an inactive user.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests or failed authentication attempts. Try again later.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    }
                },
                "summary": "List a brand's scans"
            }
        },
        "/core/api/brands/{id}/stats/": {
            "get": {
                "operationId": "getBrandStats",
                "description": "Returns aggregate figures for one brand: domain count, scan count, date of the latest scan, high-risk count, average risk score, `risk_distribution`, `domain_risk_distribution` (domain buckets that sum to `domain_count`; prefer it over the legacy `risk_distribution`), `verdict_stats` (`red`, `amber`, `green`, `unknown` counts, the same values `verdict_filter` accepts on the domain list) and `http_status_stats`.\n\n`visual_match_stats` is deprecated; use `verdict_stats`. The figures are cached for up to five minutes, so a newly found domain can take that long to show up here.",
                "parameters": [
                    {
                        "in": "path",
                        "name": "id",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "description": "UUID of the brand, as returned in the `id` field of the brand list.",
                        "required": true
                    }
                ],
                "tags": [
                    "Brands"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/BrandStats"
                                }
                            }
                        },
                        "description": ""
                    },
                    "404": {
                        "description": "The requested page or record does not exist or cannot be accessed.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "The API key is missing, invalid, revoked or belongs to an inactive user.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests or failed authentication attempts. Try again later.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    }
                },
                "summary": "Get brand statistics"
            }
        },
        "/core/api/takedowns/": {
            "get": {
                "operationId": "listTakedowns",
                "description": "Returns the takedown cases the API key's user can see: those they requested and those belonging to a brand they are a member of. Newest first.\n\nFilters: `brand` (UUID), `status`, `resolution`, `created_after` (inclusive) and `created_before` (exclusive), both ISO 8601. Invalid values, and `created_after` not earlier than `created_before`, are rejected with `400`.\n\nPaginated: `page` and `page_size` (default 50). On this endpoint a `page_size` above 100 is rejected with `400` rather than reduced.\n\nA takedown's `status` is one of `REQUESTED` (waiting for approval), `OPEN`, `IN_PROGRESS`, `PENDING_RESPONSE`, `CLOSED`, `FAILED` or `REJECTED`. `CLOSED` alone does not mean the site was taken down; read `resolution` for the outcome.",
                "parameters": [
                    {
                        "in": "query",
                        "name": "brand",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "description": "Filter takedowns for a specific brand ID"
                    },
                    {
                        "in": "query",
                        "name": "created_after",
                        "schema": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "description": "Created at or after this ISO 8601 timestamp (inclusive)."
                    },
                    {
                        "in": "query",
                        "name": "created_before",
                        "schema": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "description": "Created before this ISO 8601 timestamp (exclusive)."
                    },
                    {
                        "name": "page",
                        "required": false,
                        "in": "query",
                        "description": "A page number within the paginated result set.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "in": "query",
                        "name": "page_size",
                        "schema": {
                            "type": "integer",
                            "maximum": 100,
                            "minimum": 1
                        },
                        "description": "Items per page, 1–100; default 50. Keep this value unchanged while paging."
                    },
                    {
                        "in": "query",
                        "name": "resolution",
                        "schema": {
                            "enum": [
                                "DOMAIN_SUSPENDED",
                                "CONTENT_REMOVED",
                                "SERVER_HOLD",
                                "VOLUNTARY_TAKEDOWN",
                                "UNRESOLVED",
                                "FALSE_POSITIVE"
                            ],
                            "type": "string",
                            "minLength": 1
                        },
                        "description": "Exact outcome. CLOSED alone does not mean successful removal.\n\n* `DOMAIN_SUSPENDED` - Domain Suspended\n* `CONTENT_REMOVED` - Content Removed\n* `SERVER_HOLD` - Server Hold\n* `VOLUNTARY_TAKEDOWN` - Voluntary Takedown\n* `UNRESOLVED` - Unresolved\n* `FALSE_POSITIVE` - False Positive"
                    },
                    {
                        "in": "query",
                        "name": "status",
                        "schema": {
                            "enum": [
                                "REQUESTED",
                                "OPEN",
                                "IN_PROGRESS",
                                "PENDING_RESPONSE",
                                "CLOSED",
                                "FAILED",
                                "REJECTED"
                            ],
                            "type": "string",
                            "minLength": 1
                        },
                        "description": "* `REQUESTED` - Requested (Pending Approval)\n* `OPEN` - Open\n* `IN_PROGRESS` - In Progress\n* `PENDING_RESPONSE` - Pending Response\n* `CLOSED` - Closed\n* `FAILED` - Failed\n* `REJECTED` - Rejected"
                    }
                ],
                "tags": [
                    "Takedowns"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/PaginatedTakedownList"
                                }
                            }
                        },
                        "description": ""
                    },
                    "401": {
                        "description": "The API key is missing, invalid, revoked or belongs to an inactive user.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests or failed authentication attempts. Try again later.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "The requested page or record does not exist or cannot be accessed.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid query parameters or request data. Field errors appear directly on the response; malformed JSON has a detail message.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "anyOf": [
                                        {
                                            "$ref": "#/components/schemas/ApiErrorResponse"
                                        },
                                        {
                                            "$ref": "#/components/schemas/FieldValidationError"
                                        }
                                    ]
                                }
                            }
                        }
                    }
                },
                "summary": "List takedowns"
            },
            "post": {
                "operationId": "createTakedown",
                "description": "Requests a takedown of a website. Send `target_website` (an `http` or `https` URL on a public host; `localhost` and non-public IP addresses are rejected) and optionally `brand` (UUID) and `user_comment`.\n\nIf you pass `brand`, the API key's user must be a member of it, otherwise the request is rejected with `400`.\n\nThe status of the new takedown depends on the brand. With a `brand` that has monitoring enabled, the takedown starts as `OPEN`. Without a `brand`, or with a brand whose monitoring is disabled, it starts as `REQUESTED` and waits for approval by nebty; once approved it becomes `OPEN`, otherwise `REJECTED`. The `status` in the `201` response tells you which.",
                "tags": [
                    "Takedowns"
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/TakedownCreate"
                            }
                        },
                        "application/x-www-form-urlencoded": {
                            "schema": {
                                "$ref": "#/components/schemas/TakedownCreate"
                            }
                        },
                        "multipart/form-data": {
                            "schema": {
                                "$ref": "#/components/schemas/TakedownCreate"
                            }
                        }
                    },
                    "required": true
                },
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "201": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Takedown"
                                }
                            }
                        },
                        "description": ""
                    },
                    "400": {
                        "description": "Invalid query parameters or request data. Field errors appear directly on the response; malformed JSON has a detail message.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "anyOf": [
                                        {
                                            "$ref": "#/components/schemas/ApiErrorResponse"
                                        },
                                        {
                                            "$ref": "#/components/schemas/FieldValidationError"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "The API key is missing, invalid, revoked or belongs to an inactive user.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests or failed authentication attempts. Try again later.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    }
                },
                "summary": "Request a takedown"
            }
        },
        "/core/api/takedowns/{id}/": {
            "get": {
                "operationId": "getTakedown",
                "description": "Returns one takedown case with its abuse contacts and blacklist submissions. Answers `404` if the takedown does not exist or the API key's user can neither see it as its requester nor through a brand membership.",
                "parameters": [
                    {
                        "in": "path",
                        "name": "id",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "description": "UUID of the takedown.",
                        "required": true
                    }
                ],
                "tags": [
                    "Takedowns"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Takedown"
                                }
                            }
                        },
                        "description": ""
                    },
                    "404": {
                        "description": "The requested page or record does not exist or cannot be accessed.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "The API key is missing, invalid, revoked or belongs to an inactive user.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests or failed authentication attempts. Try again later.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid query parameters or request data. Field errors appear directly on the response; malformed JSON has a detail message.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "anyOf": [
                                        {
                                            "$ref": "#/components/schemas/ApiErrorResponse"
                                        },
                                        {
                                            "$ref": "#/components/schemas/FieldValidationError"
                                        }
                                    ]
                                }
                            }
                        }
                    }
                },
                "summary": "Get a takedown"
            }
        },
        "/core/api/takedowns/{takedown_pk}/events/": {
            "get": {
                "operationId": "listTakedownEvents",
                "description": "Returns the event log of a takedown (status changes, notes and other recorded steps), newest first.\n\nPaginated: `page` and `page_size` (default 50, maximum 100). See the pagination note in the API description.\n\nIf the takedown does not exist or is not visible to the API key's user, the result is an empty list, not `404`.",
                "parameters": [
                    {
                        "name": "page",
                        "required": false,
                        "in": "query",
                        "description": "A page number within the paginated result set.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "page_size",
                        "required": false,
                        "in": "query",
                        "description": "Number of results to return per page.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "in": "path",
                        "name": "takedown_pk",
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "required": true,
                        "description": "UUID of the takedown."
                    }
                ],
                "tags": [
                    "Takedowns"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/PaginatedTakedownEventList"
                                }
                            }
                        },
                        "description": ""
                    },
                    "401": {
                        "description": "The API key is missing, invalid, revoked or belongs to an inactive user.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests or failed authentication attempts. Try again later.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "The requested page does not exist. An unknown or inaccessible takedown returns an empty list with 200 instead.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    }
                },
                "summary": "List a takedown's events"
            }
        },
        "/core/api/users/profile/": {
            "get": {
                "operationId": "getProfile",
                "description": "Returns the profile of the user the API key belongs to: id, username, email, account type, plan and two-factor state. A `200` also confirms that the key is valid and active.",
                "tags": [
                    "Account"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UserProfile"
                                }
                            }
                        },
                        "description": ""
                    },
                    "401": {
                        "description": "The API key is missing, invalid, revoked or belongs to an inactive user.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests or failed authentication attempts. Try again later.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiErrorResponse"
                                }
                            }
                        }
                    }
                },
                "summary": "Get the current user"
            }
        }
    },
    "components": {
        "schemas": {
            "AbuseContact": {
                "type": "object",
                "description": "Serializer for abuse contact details.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "contact_type": {
                        "$ref": "#/components/schemas/AbuseContactTypeEnum"
                    },
                    "contact_type_display": {
                        "type": "string",
                        "readOnly": true
                    },
                    "provider_name": {
                        "type": "string",
                        "description": "Provider name, e.g. 'Cloudflare', 'Openprovider'",
                        "maxLength": 255
                    },
                    "abuse_email": {
                        "type": "string",
                        "format": "email",
                        "nullable": true,
                        "description": "Abuse email address.",
                        "maxLength": 254
                    },
                    "abuse_form_url": {
                        "type": "string",
                        "format": "uri",
                        "nullable": true,
                        "description": "URL to abuse report form.",
                        "maxLength": 200
                    },
                    "report_status": {
                        "$ref": "#/components/schemas/AbuseReportStatusEnum"
                    },
                    "report_status_display": {
                        "type": "string",
                        "readOnly": true
                    },
                    "reported_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "response_received_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "case_reference": {
                        "type": "string",
                        "nullable": true,
                        "description": "Provider case reference, e.g. 'Case 00954977'",
                        "maxLength": 255
                    },
                    "notes": {
                        "type": "string",
                        "nullable": true
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    }
                },
                "required": [
                    "contact_type",
                    "contact_type_display",
                    "created_at",
                    "id",
                    "provider_name",
                    "report_status_display",
                    "updated_at"
                ]
            },
            "AbuseContactTypeEnum": {
                "enum": [
                    "REGISTRAR",
                    "HOSTING",
                    "CDN",
                    "BACKEND",
                    "REGISTRY",
                    "OTHER"
                ],
                "type": "string",
                "description": "* `REGISTRAR` - Registrar\n* `HOSTING` - Hosting Provider\n* `CDN` - CDN / Proxy\n* `BACKEND` - Backend Service\n* `REGISTRY` - Domain Registry\n* `OTHER` - Other"
            },
            "AbuseReportStatusEnum": {
                "enum": [
                    "NOT_SENT",
                    "SENT",
                    "ACKNOWLEDGED",
                    "ACTION_TAKEN",
                    "REJECTED",
                    "NO_RESPONSE"
                ],
                "type": "string",
                "description": "* `NOT_SENT` - Not Sent\n* `SENT` - Sent\n* `ACKNOWLEDGED` - Acknowledged\n* `ACTION_TAKEN` - Action Taken\n* `REJECTED` - Rejected\n* `NO_RESPONSE` - No Response"
            },
            "AccountTypeEnum": {
                "enum": [
                    "standard",
                    "partner",
                    "enterprise"
                ],
                "type": "string",
                "description": "* `standard` - Standard\n* `partner` - Partner\n* `enterprise` - Enterprise"
            },
            "ApiErrorResponse": {
                "type": "object",
                "properties": {
                    "detail": {
                        "type": "string"
                    }
                },
                "required": [
                    "detail"
                ]
            },
            "BlacklistSubmission": {
                "type": "object",
                "description": "Serializer for blacklist submission details.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "tier": {
                        "$ref": "#/components/schemas/TierEnum"
                    },
                    "tier_display": {
                        "type": "string",
                        "readOnly": true
                    },
                    "service_name": {
                        "type": "string",
                        "description": "Blacklist service name, e.g. 'Google Safe Browsing', 'Spamhaus'",
                        "maxLength": 255
                    },
                    "submission_url": {
                        "type": "string",
                        "format": "uri",
                        "nullable": true,
                        "description": "URL for submission.",
                        "maxLength": 200
                    },
                    "status": {
                        "$ref": "#/components/schemas/BlacklistSubmissionStatusEnum"
                    },
                    "status_display": {
                        "type": "string",
                        "readOnly": true
                    },
                    "submitted_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "confirmed_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "description": "When listing was confirmed."
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    }
                },
                "required": [
                    "created_at",
                    "id",
                    "service_name",
                    "status_display",
                    "tier",
                    "tier_display",
                    "updated_at"
                ]
            },
            "BlacklistSubmissionStatusEnum": {
                "enum": [
                    "PENDING",
                    "SUBMITTED",
                    "LISTED",
                    "REJECTED"
                ],
                "type": "string",
                "description": "* `PENDING` - Pending\n* `SUBMITTED` - Submitted\n* `LISTED` - Listed\n* `REJECTED` - Rejected"
            },
            "BlankEnum": {
                "enum": [
                    ""
                ]
            },
            "Brand": {
                "type": "object",
                "description": "A monitored brand and its customer-editable monitoring configuration.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "domain": {
                        "type": "string",
                        "description": "The domain of the brand.",
                        "maxLength": 255
                    },
                    "trusted_domains": {
                        "type": "array",
                        "items": {},
                        "description": "Return trusted domain names, using prefetch cache when available.",
                        "readOnly": true
                    },
                    "all_keywords": {
                        "type": "array",
                        "items": {},
                        "readOnly": true
                    },
                    "negative_keywords": {
                        "type": "string",
                        "nullable": true,
                        "description": "Newline-separated terms to exclude from generated variations and domain matches for this brand. Replaces negative_variations and negative_domain_keywords, which are now read-only."
                    },
                    "seat_limit": {
                        "type": "integer",
                        "nullable": true,
                        "readOnly": true
                    },
                    "seats_used": {
                        "type": "integer",
                        "readOnly": true
                    },
                    "my_role": {
                        "type": "string",
                        "nullable": true,
                        "readOnly": true
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "name": {
                        "type": "string",
                        "description": "The name of the brand, e.g. 'Cashlink'.",
                        "maxLength": 255
                    },
                    "has_own_website": {
                        "type": "boolean",
                        "readOnly": true,
                        "description": "Whether the customer has a website of their own. When False, `domain` holds a derived .invalid sentinel that anchors similarity scoring but is NOT treated as an identity term."
                    },
                    "plan": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/PlanEnum"
                            }
                        ],
                        "readOnly": true,
                        "description": "Per-brand subscription tier. Determines seat limit and write capability for members without a multi-brand AccountType. See docs/subscription-plans.md.\n\n* `free` - Free\n* `basic` - Basic\n* `business` - Business\n* `unlimited` - Unlimited"
                    },
                    "last_variation_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "description": "When was the last variation of the domain generated?"
                    },
                    "negative_variations": {
                        "type": "string",
                        "readOnly": true,
                        "nullable": true,
                        "description": "Negative keywords separated by newlines. Variations containing these keywords will be removed."
                    },
                    "negative_domain_keywords": {
                        "type": "string",
                        "readOnly": true,
                        "nullable": true,
                        "description": "Negative domain keywords separated by newlines. Domains containing these keywords will be excluded from monitoring."
                    },
                    "brand_keywords": {
                        "type": "string",
                        "nullable": true,
                        "description": "Brand keywords separated by newlines. These keywords are associated with the brand for monitoring purposes. The brand name is always included automatically."
                    },
                    "monitoring_enabled": {
                        "type": "boolean",
                        "readOnly": true,
                        "description": "Product-level switch and source of truth for tab-locking and the inert-brand scan/AI gates. Defaults True so every normal creation path keeps producing fully-monitored brands. Only the takedown-only onboarding flow sets this False (an inert brand has both this and continuous_scanning / continuous_visual_scan False)."
                    },
                    "origin": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/OriginEnum"
                            }
                        ],
                        "readOnly": true,
                        "description": "Provenance for conversion-funnel analytics. Not the gate for tab-locking (that is monitoring_enabled) — this is the positive segmentation marker.\n\n* `self_signup` - Self Signup\n* `staff_onboarding_takedown` - Staff Onboarding (Takedown)\n* `staff_onboarding_monitoring` - Staff Onboarding (Monitoring)\n* `sales` - Sales\n* `partner` - Partner"
                    },
                    "executives": {
                        "type": "string",
                        "nullable": true,
                        "description": "Executive names separated by newlines. Used for LinkedIn social media scanning and included in brand keywords."
                    },
                    "trusted_social_media_profiles": {
                        "description": "List of trusted social media profiles, e.g. [{\"channel\": \"instagram\", \"username\": \"acmecorp\"}]."
                    },
                    "dismissed_social_media_profiles": {
                        "description": "List of dismissed social media profiles, e.g. [{\"channel\": \"instagram\", \"username\": \"spammer123\"}]."
                    },
                    "trusted_meta_page_ids": {
                        "description": "List of trusted Meta page IDs (Facebook/Instagram pages) that should be skipped during Meta Ad Library relevance scoring, e.g. [\"123456789\"]. Useful for the brand's own pages and known legitimate partners."
                    },
                    "dismissed_meta_page_ids": {
                        "description": "List of dismissed Meta page IDs that should be skipped during Meta Ad Library relevance scoring, e.g. [\"987654321\"]."
                    },
                    "dismissed_meta_ad_ids": {
                        "description": "List of dismissed Meta ad IDs (the ad.id from the Meta Ad Library), e.g. [\"123456789012345\"]. Ads in this list are hidden from the user's view by default and skipped during AI relevance scoring. User-controlled per-ad dismiss."
                    },
                    "company_name": {
                        "type": "string",
                        "readOnly": true,
                        "nullable": true,
                        "description": "Official company/legal entity name (e.g. 'Cashlink Technologies GmbH'). AI-generated via OpenAI web search. Editable in admin for manual override."
                    },
                    "brand_markets": {
                        "readOnly": true,
                        "description": "ISO 3166-1 alpha-2 country codes for the brand's main markets, e.g. ['US', 'DE']. AI-generated."
                    },
                    "triage_enabled": {
                        "type": "boolean",
                        "description": "Per-brand switch for common-word triage. Off by default so a generated profile cannot act on a brand until it has been reviewed by hand (§5)."
                    },
                    "linkedin_company_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "LinkedIn company page URL, e.g. https://www.linkedin.com/company/coinroutes/",
                        "maxLength": 200
                    }
                },
                "required": [
                    "all_keywords",
                    "brand_markets",
                    "company_name",
                    "created_at",
                    "domain",
                    "has_own_website",
                    "id",
                    "monitoring_enabled",
                    "my_role",
                    "name",
                    "negative_domain_keywords",
                    "negative_variations",
                    "origin",
                    "plan",
                    "seat_limit",
                    "seats_used",
                    "trusted_domains",
                    "updated_at"
                ]
            },
            "BrandDomainApexGroup": {
                "type": "object",
                "description": "One row of the apex-grouped brand domain list (``?group_by=apex``).\n\nNot a ModelSerializer: a row is an aggregate over many Domains, not one of\nthem. ``registrable_apex`` falls back to the hostname for rows whose apex\ncolumn is unset, so every domain is represented in exactly one group and\nthe ``domain_count`` values sum to the ungrouped row count.",
                "properties": {
                    "registrable_apex": {
                        "type": "string",
                        "description": "Registrable apex (eTLD+1) shared by every host in this group."
                    },
                    "domain_count": {
                        "type": "integer",
                        "description": "How many hostnames in this brand's list share this apex."
                    },
                    "max_risk_score": {
                        "type": "integer",
                        "nullable": true,
                        "description": "Highest pair risk score among the group's hosts. The worst host decides the group, so collapsing rows can never lower a score."
                    }
                },
                "required": [
                    "domain_count",
                    "max_risk_score",
                    "registrable_apex"
                ]
            },
            "BrandDomainListItem": {
                "anyOf": [
                    {
                        "$ref": "#/components/schemas/Domain"
                    },
                    {
                        "$ref": "#/components/schemas/CompactDomain"
                    },
                    {
                        "$ref": "#/components/schemas/BrandDomainApexGroup"
                    }
                ]
            },
            "BrandListItem": {
                "anyOf": [
                    {
                        "$ref": "#/components/schemas/Brand"
                    },
                    {
                        "$ref": "#/components/schemas/BrandWithStats"
                    },
                    {
                        "$ref": "#/components/schemas/CompactBrand"
                    }
                ]
            },
            "BrandScanListItem": {
                "anyOf": [
                    {
                        "$ref": "#/components/schemas/DomainScan"
                    },
                    {
                        "$ref": "#/components/schemas/CompactDomainScan"
                    }
                ]
            },
            "BrandStats": {
                "type": "object",
                "description": "Serializer for comprehensive brand statistics.",
                "properties": {
                    "domain_risk_distribution": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "integer"
                        },
                        "description": "Domain-based buckets summing to domain_count, including zero risk and unscanned. Prefer these over legacy risk_distribution, which counts scan rows at scores 1+."
                    },
                    "domain_count": {
                        "type": "integer"
                    },
                    "scan_count": {
                        "type": "integer"
                    },
                    "latest_scan_date": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "high_risk_count": {
                        "type": "integer"
                    },
                    "average_risk_score": {
                        "type": "number",
                        "format": "double"
                    },
                    "risk_distribution": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "integer"
                        },
                        "description": "Risk distribution with low_risk_count, medium_risk_count, high_risk_count"
                    },
                    "visual_match_stats": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "integer"
                        },
                        "description": "Deprecated — use verdict_stats. Visual match statistics with visual_matches_found, no_visual_matches, unknown_visual_matches"
                    },
                    "verdict_stats": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "integer"
                        },
                        "description": "Verdict statistics with red, amber, green, unknown counts. Matches the values accepted by the domains list endpoint's verdict_filter query parameter."
                    },
                    "http_status_stats": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "integer"
                        },
                        "description": "HTTP status statistics with live_domains, error_domains, unknown_status"
                    }
                },
                "required": [
                    "average_risk_score",
                    "domain_count",
                    "high_risk_count",
                    "http_status_stats",
                    "latest_scan_date",
                    "risk_distribution",
                    "scan_count",
                    "verdict_stats",
                    "visual_match_stats"
                ]
            },
            "BrandWithStats": {
                "type": "object",
                "description": "Serializer for brand with included comprehensive statistics.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "domain": {
                        "type": "string",
                        "description": "The domain of the brand.",
                        "maxLength": 255
                    },
                    "domain_count": {
                        "type": "integer",
                        "readOnly": true
                    },
                    "scan_count": {
                        "type": "integer",
                        "readOnly": true
                    },
                    "latest_scan_date": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "readOnly": true
                    },
                    "high_risk_count": {
                        "type": "integer",
                        "readOnly": true
                    },
                    "average_risk_score": {
                        "type": "number",
                        "format": "double",
                        "readOnly": true
                    },
                    "risk_distribution": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "integer"
                        },
                        "readOnly": true
                    },
                    "visual_match_stats": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "integer"
                        },
                        "readOnly": true
                    },
                    "verdict_stats": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "integer"
                        },
                        "readOnly": true
                    },
                    "http_status_stats": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "integer"
                        },
                        "readOnly": true
                    },
                    "trusted_domains": {
                        "type": "array",
                        "items": {},
                        "description": "Return trusted domain names, using prefetch cache when available.",
                        "readOnly": true
                    },
                    "seat_limit": {
                        "type": "integer",
                        "nullable": true,
                        "readOnly": true
                    },
                    "seats_used": {
                        "type": "integer",
                        "readOnly": true
                    },
                    "my_role": {
                        "type": "string",
                        "nullable": true,
                        "readOnly": true
                    },
                    "negative_keywords": {
                        "type": "string",
                        "nullable": true,
                        "description": "Newline-separated terms to exclude from generated variations and domain matches for this brand. Replaces negative_variations and negative_domain_keywords, which are now read-only."
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "name": {
                        "type": "string",
                        "description": "The name of the brand, e.g. 'Cashlink'.",
                        "maxLength": 255
                    },
                    "has_own_website": {
                        "type": "boolean",
                        "readOnly": true,
                        "description": "Whether the customer has a website of their own. When False, `domain` holds a derived .invalid sentinel that anchors similarity scoring but is NOT treated as an identity term."
                    },
                    "plan": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/PlanEnum"
                            }
                        ],
                        "readOnly": true,
                        "description": "Per-brand subscription tier. Determines seat limit and write capability for members without a multi-brand AccountType. See docs/subscription-plans.md.\n\n* `free` - Free\n* `basic` - Basic\n* `business` - Business\n* `unlimited` - Unlimited"
                    },
                    "last_variation_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "description": "When was the last variation of the domain generated?"
                    },
                    "negative_variations": {
                        "type": "string",
                        "readOnly": true,
                        "nullable": true,
                        "description": "Negative keywords separated by newlines. Variations containing these keywords will be removed."
                    },
                    "negative_domain_keywords": {
                        "type": "string",
                        "readOnly": true,
                        "nullable": true,
                        "description": "Negative domain keywords separated by newlines. Domains containing these keywords will be excluded from monitoring."
                    },
                    "brand_keywords": {
                        "type": "string",
                        "nullable": true,
                        "description": "Brand keywords separated by newlines. These keywords are associated with the brand for monitoring purposes. The brand name is always included automatically."
                    },
                    "monitoring_enabled": {
                        "type": "boolean",
                        "readOnly": true,
                        "description": "Product-level switch and source of truth for tab-locking and the inert-brand scan/AI gates. Defaults True so every normal creation path keeps producing fully-monitored brands. Only the takedown-only onboarding flow sets this False (an inert brand has both this and continuous_scanning / continuous_visual_scan False)."
                    },
                    "origin": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/OriginEnum"
                            }
                        ],
                        "readOnly": true,
                        "description": "Provenance for conversion-funnel analytics. Not the gate for tab-locking (that is monitoring_enabled) — this is the positive segmentation marker.\n\n* `self_signup` - Self Signup\n* `staff_onboarding_takedown` - Staff Onboarding (Takedown)\n* `staff_onboarding_monitoring` - Staff Onboarding (Monitoring)\n* `sales` - Sales\n* `partner` - Partner"
                    },
                    "executives": {
                        "type": "string",
                        "nullable": true,
                        "description": "Executive names separated by newlines. Used for LinkedIn social media scanning and included in brand keywords."
                    },
                    "trusted_social_media_profiles": {
                        "description": "List of trusted social media profiles, e.g. [{\"channel\": \"instagram\", \"username\": \"acmecorp\"}]."
                    },
                    "dismissed_social_media_profiles": {
                        "description": "List of dismissed social media profiles, e.g. [{\"channel\": \"instagram\", \"username\": \"spammer123\"}]."
                    },
                    "trusted_meta_page_ids": {
                        "description": "List of trusted Meta page IDs (Facebook/Instagram pages) that should be skipped during Meta Ad Library relevance scoring, e.g. [\"123456789\"]. Useful for the brand's own pages and known legitimate partners."
                    },
                    "dismissed_meta_page_ids": {
                        "description": "List of dismissed Meta page IDs that should be skipped during Meta Ad Library relevance scoring, e.g. [\"987654321\"]."
                    },
                    "dismissed_meta_ad_ids": {
                        "description": "List of dismissed Meta ad IDs (the ad.id from the Meta Ad Library), e.g. [\"123456789012345\"]. Ads in this list are hidden from the user's view by default and skipped during AI relevance scoring. User-controlled per-ad dismiss."
                    },
                    "company_name": {
                        "type": "string",
                        "nullable": true,
                        "description": "Official company/legal entity name (e.g. 'Cashlink Technologies GmbH'). AI-generated via OpenAI web search. Editable in admin for manual override."
                    },
                    "brand_markets": {
                        "description": "ISO 3166-1 alpha-2 country codes for the brand's main markets, e.g. ['US', 'DE']. AI-generated."
                    },
                    "triage_enabled": {
                        "type": "boolean",
                        "description": "Per-brand switch for common-word triage. Off by default so a generated profile cannot act on a brand until it has been reviewed by hand (§5)."
                    },
                    "linkedin_company_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "LinkedIn company page URL, e.g. https://www.linkedin.com/company/coinroutes/",
                        "maxLength": 200
                    }
                },
                "required": [
                    "average_risk_score",
                    "created_at",
                    "domain",
                    "domain_count",
                    "has_own_website",
                    "high_risk_count",
                    "http_status_stats",
                    "id",
                    "latest_scan_date",
                    "monitoring_enabled",
                    "my_role",
                    "name",
                    "negative_domain_keywords",
                    "negative_variations",
                    "origin",
                    "plan",
                    "risk_distribution",
                    "scan_count",
                    "seat_limit",
                    "seats_used",
                    "trusted_domains",
                    "updated_at",
                    "verdict_stats",
                    "visual_match_stats"
                ]
            },
            "ClosedAtSourceEnum": {
                "enum": [
                    "MEASURED",
                    "DERIVED_FROM_EVENT",
                    "DERIVED_FROM_NOTE"
                ],
                "type": "string",
                "description": "* `MEASURED` - Measured at closure\n* `DERIVED_FROM_EVENT` - Derived from status-change event\n* `DERIVED_FROM_NOTE` - Derived from last case note (±1 day)"
            },
            "CompactBrand": {
                "type": "object",
                "description": "Brand discovery without keywords, billing identifiers or internal data.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "name": {
                        "type": "string",
                        "readOnly": true,
                        "description": "The name of the brand, e.g. 'Cashlink'."
                    },
                    "domain": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true,
                        "description": "The domain of the brand, e.g. 'cashlink.de'."
                    },
                    "plan": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/PlanEnum"
                            }
                        ],
                        "readOnly": true,
                        "description": "Per-brand subscription tier. Determines seat limit and write capability for members without a multi-brand AccountType. See docs/subscription-plans.md.\n\n* `free` - Free\n* `basic` - Basic\n* `business` - Business\n* `unlimited` - Unlimited"
                    },
                    "monitoring_enabled": {
                        "type": "boolean",
                        "readOnly": true,
                        "description": "Product-level switch and source of truth for tab-locking and the inert-brand scan/AI gates. Defaults True so every normal creation path keeps producing fully-monitored brands. Only the takedown-only onboarding flow sets this False (an inert brand has both this and continuous_scanning / continuous_visual_scan False)."
                    },
                    "brand_markets": {
                        "readOnly": true,
                        "description": "ISO 3166-1 alpha-2 country codes for the brand's main markets, e.g. ['US', 'DE']. AI-generated."
                    }
                },
                "required": [
                    "brand_markets",
                    "domain",
                    "id",
                    "monitoring_enabled",
                    "name",
                    "plan"
                ]
            },
            "CompactDomain": {
                "type": "object",
                "description": "Serializer for Domain model.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "name": {
                        "type": "string",
                        "maxLength": 255
                    },
                    "source": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/DomainSourceEnum"
                            }
                        ],
                        "description": "How this domain was discovered/added to the system\n\n* `VARIATION` - Generated from Variation\n* `CERTSTREAM` - CertStream API\n* `SUBDOMAIN_SCAN` - Subdomain Scan\n* `MANUAL` - Manual Entry\n* `AD_LIBRARY` - Meta Ad Library"
                    },
                    "latest_scan": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/CompactDomainScan"
                            }
                        ],
                        "readOnly": true
                    },
                    "open_takedown": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/OpenTakedown"
                            }
                        ],
                        "nullable": true,
                        "readOnly": true
                    },
                    "last_closed_takedown": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/LastClosedTakedown"
                            }
                        ],
                        "nullable": true,
                        "readOnly": true
                    }
                },
                "required": [
                    "id",
                    "last_closed_takedown",
                    "latest_scan",
                    "name",
                    "open_takedown"
                ]
            },
            "CompactDomainScan": {
                "type": "object",
                "description": "Bounded scan summary; evidence and history live in domain detail.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "domain": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "domain_name": {
                        "type": "string",
                        "readOnly": true
                    },
                    "risk_score": {
                        "type": "integer",
                        "readOnly": true
                    },
                    "verdict": {
                        "type": "string",
                        "description": "The single customer-facing verdict, resolved for the requesting brand.\n\nUnlike ``risk_score``/``visual_match`` there is no shared ``DomainScan``\ncolumn to fall back to: the merged verdict has only ever lived on the\npair (``BrandDomainAssociation.refresh_merged_verdict``), so a caller\nwith no brand in context — or a historical scan row, which carries no\npair-specific verdict of its own — gets \"unknown\" rather than another\nbrand's answer.",
                        "readOnly": true
                    },
                    "requires_human_review": {
                        "type": "boolean",
                        "readOnly": true
                    },
                    "https_head_request_response_code": {
                        "type": "integer",
                        "nullable": true
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    }
                },
                "required": [
                    "created_at",
                    "domain",
                    "domain_name",
                    "id",
                    "requires_human_review",
                    "risk_score",
                    "updated_at",
                    "verdict"
                ]
            },
            "Domain": {
                "type": "object",
                "description": "Serializer for Domain model.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "name": {
                        "type": "string",
                        "maxLength": 255
                    },
                    "source": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/DomainSourceEnum"
                            }
                        ],
                        "description": "How this domain was discovered/added to the system\n\n* `VARIATION` - Generated from Variation\n* `CERTSTREAM` - CertStream API\n* `SUBDOMAIN_SCAN` - Subdomain Scan\n* `MANUAL` - Manual Entry\n* `AD_LIBRARY` - Meta Ad Library"
                    },
                    "parent": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true
                    },
                    "associated_brands": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "readOnly": true
                    },
                    "latest_scan": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/DomainScan"
                            }
                        ],
                        "readOnly": true
                    },
                    "scans": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/DomainScan"
                        },
                        "readOnly": true
                    },
                    "open_takedown": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/OpenTakedown"
                            }
                        ],
                        "nullable": true,
                        "readOnly": true
                    },
                    "last_closed_takedown": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/LastClosedTakedown"
                            }
                        ],
                        "nullable": true,
                        "readOnly": true
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    }
                },
                "required": [
                    "associated_brands",
                    "created_at",
                    "id",
                    "last_closed_takedown",
                    "latest_scan",
                    "name",
                    "open_takedown",
                    "scans",
                    "updated_at"
                ]
            },
            "DomainScan": {
                "type": "object",
                "description": "Serializer for DomainScan model.\n\n``risk_score`` and ``visual_match`` are answers about a brand↔domain *pair*,\nnot about the domain (#954). When the caller puts a ``brand`` in the\nserializer context — every brand-scoped endpoint does — both are rendered\nfrom that brand's association, so a domain two customers monitor shows each\nof them the verdict computed against their own brand.\n\nWithout a brand in context the shared columns are rendered unchanged, which\nis what the domain-global endpoints and the admin want. That column now\nmeans \"the worst score across every brand monitoring this domain\" rather\nthan \"the longest-monitored brand's score\" — see\n``DomainScan.get_risk_score``.\n\n``verdict``/``verdict_reasoning``/``requires_human_review`` are the single\ncustomer-facing verdict merged from the visual and content detectors\n(``BrandDomainAssociation.refresh_merged_verdict``) and are what the\nfrontend should read going forward. ``visual_match`` is kept in the payload\nas **deprecated** — do not remove it — because it still governs code paths\nmid-migration; new consumers should read ``verdict`` instead.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "domain": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "mx_records": {
                        "type": "string",
                        "nullable": true
                    },
                    "a_records": {
                        "type": "string",
                        "nullable": true
                    },
                    "cname_records": {
                        "type": "string",
                        "nullable": true
                    },
                    "visual_match": {
                        "type": "boolean",
                        "nullable": true,
                        "readOnly": true
                    },
                    "verdict": {
                        "type": "string",
                        "description": "The single customer-facing verdict, resolved for the requesting brand.\n\nUnlike ``risk_score``/``visual_match`` there is no shared ``DomainScan``\ncolumn to fall back to: the merged verdict has only ever lived on the\npair (``BrandDomainAssociation.refresh_merged_verdict``), so a caller\nwith no brand in context — or a historical scan row, which carries no\npair-specific verdict of its own — gets \"unknown\" rather than another\nbrand's answer.",
                        "readOnly": true
                    },
                    "verdict_reasoning": {
                        "type": "string",
                        "nullable": true,
                        "readOnly": true
                    },
                    "requires_human_review": {
                        "type": "boolean",
                        "readOnly": true
                    },
                    "visual_scan_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "description": "When *this brand* last had the domain visually scanned.\n\nThe shared timestamp is whichever brand scanned the domain most\nrecently, so on a shared hostname it is somebody else's. NULL on the\npair means this brand has not scanned it yet, which is exactly what the\ncooldown treats it as.",
                        "readOnly": true
                    },
                    "screenshot_url": {
                        "type": "string",
                        "format": "uri",
                        "nullable": true,
                        "description": "URL to the screenshot of the domain stored in Hetzner S3-compatible Object Storage",
                        "maxLength": 2000
                    },
                    "https_head_request_response_code": {
                        "type": "integer",
                        "nullable": true
                    },
                    "certificate": {
                        "type": "string",
                        "nullable": true
                    },
                    "scans_executed": {
                        "type": "integer"
                    },
                    "is_alerting_event": {
                        "type": "boolean",
                        "nullable": true,
                        "description": "Whether *this brand* still has an unsent alert for the domain.\n\nThe shared flag means \"somebody has one\", so after this brand's alert\nwas sent it would keep reporting true while another brand's is pending.",
                        "readOnly": true
                    },
                    "completeness": {
                        "type": "integer",
                        "readOnly": true,
                        "description": "A completeness of 0 means that the domain has no records set. The higher this number, the more likely the domain is actually used."
                    },
                    "risk_score": {
                        "type": "integer",
                        "readOnly": true
                    },
                    "scan_history": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "additionalProperties": {}
                        },
                        "description": "The domain's scan timeline, most recent first.\n\nOn a domain that has only ever had single-brand history, the whole\nhistory is this brand's own and renders untouched. Once branded rows\nexist, the domain has had a multi-brand period: the requesting brand then\nsees only its own rows, and NULL-brand rows are redacted because current\nassociation count can no longer prove which brand they belonged to.",
                        "readOnly": true
                    },
                    "user_feedback_type": {
                        "nullable": true,
                        "description": "User feedback: false_positive overrides risk to 0, comment is informational. Resolved for the requesting brand.\n\n* `false_positive` - False Positive\n* `comment` - Comment",
                        "readOnly": true,
                        "oneOf": [
                            {
                                "$ref": "#/components/schemas/FeedbackTypeEnum"
                            },
                            {
                                "$ref": "#/components/schemas/NullEnum"
                            }
                        ]
                    },
                    "user_feedback_comment": {
                        "type": "string",
                        "description": "Free text comment from the user.",
                        "readOnly": true
                    },
                    "user_feedback_by": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true,
                        "description": "User who submitted the feedback.",
                        "readOnly": true
                    },
                    "user_feedback_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "description": "When the feedback was submitted.",
                        "readOnly": true
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    }
                },
                "required": [
                    "completeness",
                    "created_at",
                    "domain",
                    "id",
                    "is_alerting_event",
                    "requires_human_review",
                    "risk_score",
                    "scan_history",
                    "updated_at",
                    "user_feedback_at",
                    "user_feedback_by",
                    "user_feedback_comment",
                    "user_feedback_type",
                    "verdict",
                    "verdict_reasoning",
                    "visual_match",
                    "visual_scan_at"
                ]
            },
            "DomainSourceEnum": {
                "enum": [
                    "VARIATION",
                    "CERTSTREAM",
                    "SUBDOMAIN_SCAN",
                    "MANUAL",
                    "AD_LIBRARY"
                ],
                "type": "string",
                "description": "* `VARIATION` - Generated from Variation\n* `CERTSTREAM` - CertStream API\n* `SUBDOMAIN_SCAN` - Subdomain Scan\n* `MANUAL` - Manual Entry\n* `AD_LIBRARY` - Meta Ad Library"
            },
            "FeedbackTypeEnum": {
                "enum": [
                    "false_positive",
                    "comment"
                ],
                "type": "string",
                "description": "* `false_positive` - False Positive\n* `comment` - Comment"
            },
            "LastClosedTakedown": {
                "type": "object",
                "description": "The requesting brand's newest FINISHED takedown case for a hostname.\n\nLean on purpose, like ``OpenTakedownSerializer``: enough to draw a\n\"Takedown: <result>\" badge and link to the case. The full case lives at\n``/core/api/takedowns/{id}/``.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "description": "Takedown id; fetch the case at GET /core/api/takedowns/{id}/."
                    },
                    "status": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/LastClosedTakedownStatusEnum"
                            }
                        ],
                        "description": "How the case ended: CLOSED (worked to a conclusion, see `resolution`) or FAILED (abandoned, no resolution).\n\n* `CLOSED` - Closed\n* `FAILED` - Failed"
                    },
                    "resolution": {
                        "nullable": true,
                        "description": "Outcome of a CLOSED case. FALSE_POSITIVE means the analysts found no infringement. Null for FAILED, and for a CLOSED case that was closed without a resolution.\n\n* `DOMAIN_SUSPENDED` - Domain Suspended\n* `CONTENT_REMOVED` - Content Removed\n* `SERVER_HOLD` - Server Hold\n* `VOLUNTARY_TAKEDOWN` - Voluntary Takedown\n* `UNRESOLVED` - Unresolved\n* `FALSE_POSITIVE` - False Positive",
                        "oneOf": [
                            {
                                "$ref": "#/components/schemas/TakedownResolutionEnum"
                            },
                            {
                                "$ref": "#/components/schemas/NullEnum"
                            }
                        ]
                    },
                    "closed_at": {
                        "type": "string",
                        "format": "date-time",
                        "description": "When the case finished. For CLOSED this is the case's `closed_at`; FAILED and undated legacy cases use a stable result timestamp."
                    }
                },
                "required": [
                    "closed_at",
                    "id",
                    "resolution",
                    "status"
                ]
            },
            "LastClosedTakedownStatusEnum": {
                "enum": [
                    "CLOSED",
                    "FAILED"
                ],
                "type": "string",
                "description": "* `CLOSED` - Closed\n* `FAILED` - Failed"
            },
            "NullEnum": {
                "enum": [
                    null
                ]
            },
            "OpenTakedown": {
                "type": "object",
                "description": "The requesting brand's running takedown case for a hostname.\n\nLean on purpose: id and status only. The full case lives at\n``/core/api/takedowns/{id}/`` and a second payload of it here would be a\ncontract to keep in sync for a badge.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "description": "Takedown id; fetch the case at GET /core/api/takedowns/{id}/."
                    },
                    "status": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/TakedownStatusEnum"
                            }
                        ],
                        "description": "Current status of the running case.\n\n* `REQUESTED` - Requested (Pending Approval)\n* `OPEN` - Open\n* `IN_PROGRESS` - In Progress\n* `PENDING_RESPONSE` - Pending Response\n* `CLOSED` - Closed\n* `FAILED` - Failed\n* `REJECTED` - Rejected"
                    }
                },
                "required": [
                    "id",
                    "status"
                ]
            },
            "OriginEnum": {
                "enum": [
                    "self_signup",
                    "staff_onboarding_takedown",
                    "staff_onboarding_monitoring",
                    "sales",
                    "partner"
                ],
                "type": "string",
                "description": "* `self_signup` - Self Signup\n* `staff_onboarding_takedown` - Staff Onboarding (Takedown)\n* `staff_onboarding_monitoring` - Staff Onboarding (Monitoring)\n* `sales` - Sales\n* `partner` - Partner"
            },
            "PaginatedBrandListItemList": {
                "type": "object",
                "required": [
                    "count",
                    "results"
                ],
                "properties": {
                    "count": {
                        "type": "integer",
                        "example": 123
                    },
                    "next": {
                        "type": "string",
                        "nullable": true,
                        "format": "uri",
                        "example": "http://api.example.org/accounts/?page=4"
                    },
                    "previous": {
                        "type": "string",
                        "nullable": true,
                        "format": "uri",
                        "example": "http://api.example.org/accounts/?page=2"
                    },
                    "results": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/BrandListItem"
                        }
                    }
                }
            },
            "PaginatedBrandScanListItemList": {
                "type": "object",
                "required": [
                    "count",
                    "results"
                ],
                "properties": {
                    "count": {
                        "type": "integer",
                        "example": 123
                    },
                    "next": {
                        "type": "string",
                        "nullable": true,
                        "format": "uri",
                        "example": "http://api.example.org/accounts/?page=4"
                    },
                    "previous": {
                        "type": "string",
                        "nullable": true,
                        "format": "uri",
                        "example": "http://api.example.org/accounts/?page=2"
                    },
                    "results": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/BrandScanListItem"
                        }
                    }
                }
            },
            "PaginatedTakedownEventList": {
                "type": "object",
                "required": [
                    "count",
                    "results"
                ],
                "properties": {
                    "count": {
                        "type": "integer",
                        "example": 123
                    },
                    "next": {
                        "type": "string",
                        "nullable": true,
                        "format": "uri",
                        "example": "http://api.example.org/accounts/?page=4"
                    },
                    "previous": {
                        "type": "string",
                        "nullable": true,
                        "format": "uri",
                        "example": "http://api.example.org/accounts/?page=2"
                    },
                    "results": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/TakedownEvent"
                        }
                    }
                }
            },
            "PaginatedTakedownList": {
                "type": "object",
                "required": [
                    "count",
                    "results"
                ],
                "properties": {
                    "count": {
                        "type": "integer",
                        "example": 123
                    },
                    "next": {
                        "type": "string",
                        "nullable": true,
                        "format": "uri",
                        "example": "http://api.example.org/accounts/?page=4"
                    },
                    "previous": {
                        "type": "string",
                        "nullable": true,
                        "format": "uri",
                        "example": "http://api.example.org/accounts/?page=2"
                    },
                    "results": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Takedown"
                        }
                    }
                }
            },
            "PlanEnum": {
                "enum": [
                    "free",
                    "basic",
                    "business",
                    "unlimited"
                ],
                "type": "string",
                "description": "* `free` - Free\n* `basic` - Basic\n* `business` - Business\n* `unlimited` - Unlimited"
            },
            "Takedown": {
                "type": "object",
                "description": "Serializer for takedown request details.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "target_website": {
                        "type": "string",
                        "format": "uri",
                        "description": "The target website URL for the takedown request",
                        "maxLength": 500
                    },
                    "requesting_user_email": {
                        "type": "string",
                        "readOnly": true
                    },
                    "user_comment": {
                        "type": "string",
                        "nullable": true,
                        "description": "Optional comment from the requesting user"
                    },
                    "brand": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true,
                        "description": "Brand associated with this takedown. All users with access to this brand can view the takedown."
                    },
                    "brand_display": {
                        "type": "string",
                        "readOnly": true,
                        "nullable": true
                    },
                    "brand_name": {
                        "type": "string",
                        "nullable": true,
                        "description": "Legacy free-text brand name (deprecated, use brand FK instead)",
                        "maxLength": 255
                    },
                    "status": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/TakedownStatusEnum"
                            }
                        ],
                        "readOnly": true,
                        "description": "Current status of the takedown request\n\n* `REQUESTED` - Requested (Pending Approval)\n* `OPEN` - Open\n* `IN_PROGRESS` - In Progress\n* `PENDING_RESPONSE` - Pending Response\n* `CLOSED` - Closed\n* `FAILED` - Failed\n* `REJECTED` - Rejected"
                    },
                    "status_display": {
                        "type": "string",
                        "readOnly": true
                    },
                    "is_open": {
                        "type": "boolean",
                        "readOnly": true
                    },
                    "closed_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true,
                        "nullable": true,
                        "description": "Timestamp when the takedown case was closed."
                    },
                    "closed_at_source": {
                        "readOnly": true,
                        "description": "How closed_at was arrived at: MEASURED (stamped at closure or entered by staff), DERIVED_FROM_EVENT (reconstructed from the status-change audit event, exact), DERIVED_FROM_NOTE (reconstructed from the last case note, +/-1 day), or blank when no closing date is known. Reports averaging resolution time should state how many inputs were reconstructed.\n\n* `MEASURED` - Measured at closure\n* `DERIVED_FROM_EVENT` - Derived from status-change event\n* `DERIVED_FROM_NOTE` - Derived from last case note (±1 day)",
                        "oneOf": [
                            {
                                "$ref": "#/components/schemas/ClosedAtSourceEnum"
                            },
                            {
                                "$ref": "#/components/schemas/BlankEnum"
                            }
                        ]
                    },
                    "resolution": {
                        "readOnly": true,
                        "nullable": true,
                        "description": "Outcome of the takedown case.\n\n* `DOMAIN_SUSPENDED` - Domain Suspended\n* `CONTENT_REMOVED` - Content Removed\n* `SERVER_HOLD` - Server Hold\n* `VOLUNTARY_TAKEDOWN` - Voluntary Takedown\n* `UNRESOLVED` - Unresolved\n* `FALSE_POSITIVE` - False Positive",
                        "oneOf": [
                            {
                                "$ref": "#/components/schemas/TakedownResolutionEnum"
                            },
                            {
                                "$ref": "#/components/schemas/NullEnum"
                            }
                        ]
                    },
                    "resolution_display": {
                        "type": "string",
                        "readOnly": true,
                        "nullable": true
                    },
                    "registrar": {
                        "type": "string",
                        "nullable": true,
                        "description": "Domain registrar, e.g. 'Openprovider', 'Sav.com'",
                        "maxLength": 255
                    },
                    "domain_created_at": {
                        "type": "string",
                        "format": "date",
                        "nullable": true,
                        "description": "Domain registration date from WHOIS."
                    },
                    "domain_expires_at": {
                        "type": "string",
                        "format": "date",
                        "nullable": true,
                        "description": "Domain expiry date from WHOIS."
                    },
                    "domain_status": {
                        "type": "string",
                        "nullable": true,
                        "description": "WHOIS status, e.g. 'clientTransferProhibited', 'serverHold'",
                        "maxLength": 255
                    },
                    "ip_address": {
                        "type": "string",
                        "nullable": true,
                        "description": "Resolved IP address (may be proxy IP)."
                    },
                    "origin_ip": {
                        "type": "string",
                        "nullable": true,
                        "description": "Real server IP behind CDN/proxy."
                    },
                    "asn": {
                        "type": "string",
                        "nullable": true,
                        "description": "AS number, e.g. 'AS13335'",
                        "maxLength": 20
                    },
                    "hosting_provider": {
                        "type": "string",
                        "nullable": true,
                        "description": "Hosting provider, e.g. 'Hostinger', 'Hetzner'",
                        "maxLength": 255
                    },
                    "cdn_proxy": {
                        "type": "string",
                        "nullable": true,
                        "description": "CDN/proxy provider, e.g. 'Cloudflare'",
                        "maxLength": 255
                    },
                    "is_proxied": {
                        "type": "boolean",
                        "description": "Whether the domain is behind a CDN/proxy."
                    },
                    "backend_provider": {
                        "type": "string",
                        "nullable": true,
                        "description": "Backend service, e.g. 'Supabase', 'Vercel'",
                        "maxLength": 255
                    },
                    "backend_project_id": {
                        "type": "string",
                        "nullable": true,
                        "description": "Project ID at backend provider.",
                        "maxLength": 255
                    },
                    "ssl_issued_at": {
                        "type": "string",
                        "format": "date",
                        "nullable": true,
                        "description": "SSL certificate issue date."
                    },
                    "ssl_expires_at": {
                        "type": "string",
                        "format": "date",
                        "nullable": true,
                        "description": "SSL certificate expiry date."
                    },
                    "ssl_issuer": {
                        "type": "string",
                        "nullable": true,
                        "description": "Certificate issuer, e.g. 'Let's Encrypt', 'Cloudflare'",
                        "maxLength": 255
                    },
                    "a_entry": {
                        "type": "string",
                        "nullable": true,
                        "description": "DNS A record information (DEPRECATED - use dns_raw)"
                    },
                    "ns_entry": {
                        "type": "string",
                        "nullable": true,
                        "description": "DNS NS record information (DEPRECATED - use dns_raw)"
                    },
                    "dns_raw": {
                        "nullable": true,
                        "description": "Full DNS records as JSON. Expected structure: {\"a\": [...], \"aaaa\": [...], \"cname\": [...], \"mx\": [...], \"ns\": [...], \"txt\": [...], \"soa\": [...], \"caa\": [...]}"
                    },
                    "virustotal_score": {
                        "type": "string",
                        "nullable": true,
                        "description": "Score, e.g. '16/93'",
                        "maxLength": 20
                    },
                    "virustotal_url": {
                        "type": "string",
                        "format": "uri",
                        "nullable": true,
                        "description": "Link to VirusTotal report.",
                        "maxLength": 200
                    },
                    "urlscan_url": {
                        "type": "string",
                        "format": "uri",
                        "nullable": true,
                        "description": "Link to urlscan.io result.",
                        "maxLength": 200
                    },
                    "screenshot_url": {
                        "type": "string",
                        "format": "uri",
                        "nullable": true,
                        "description": "URL to screenshot in Hetzner S3 Object Storage.",
                        "maxLength": 2000
                    },
                    "screenshot_taken_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "description": "When the screenshot was taken."
                    },
                    "abuse_contacts": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/AbuseContact"
                        },
                        "readOnly": true
                    },
                    "blacklist_submissions": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/BlacklistSubmission"
                        },
                        "readOnly": true
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    }
                },
                "required": [
                    "abuse_contacts",
                    "blacklist_submissions",
                    "brand_display",
                    "closed_at",
                    "closed_at_source",
                    "created_at",
                    "id",
                    "is_open",
                    "requesting_user_email",
                    "resolution",
                    "resolution_display",
                    "status",
                    "status_display",
                    "target_website",
                    "updated_at"
                ]
            },
            "TakedownCreate": {
                "type": "object",
                "description": "Serializer for creating new takedown requests.",
                "properties": {
                    "target_website": {
                        "type": "string",
                        "format": "uri",
                        "description": "The target website URL for the takedown request",
                        "maxLength": 500
                    },
                    "user_comment": {
                        "type": "string",
                        "nullable": true,
                        "description": "Optional comment from the requesting user"
                    },
                    "brand": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true,
                        "description": "Brand associated with this takedown. All users with access to this brand can view the takedown."
                    }
                },
                "required": [
                    "target_website"
                ]
            },
            "TakedownEvent": {
                "type": "object",
                "description": "Serializer for takedown event details.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "takedown": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "event_type": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/TakedownEventTypeEnum"
                            }
                        ],
                        "description": "Type of event that occurred\n\n* `STATUS_CHANGED` - Status Changed\n* `ABUSE_REPORT_SENT` - Abuse Report Sent\n* `ABUSE_RESPONSE_RECEIVED` - Abuse Response Received\n* `BLACKLIST_SUBMITTED` - Blacklist Submitted\n* `BLACKLIST_CONFIRMED` - Blacklist Confirmed\n* `DNS_UPDATED` - DNS Records Updated\n* `INFRASTRUCTURE_UPDATED` - Infrastructure Updated\n* `SCREENSHOT_TAKEN` - Screenshot Taken\n* `NOTE` - Note\n* `OTHER` - Other\n* `DOMAIN_RESCANNED` - Domain Rescanned\n* `DOMAIN_MARKED_FALSE_POSITIVE` - Domain Marked False Positive\n* `POST_CLOSE_SKIPPED` - Post-Close Work Skipped"
                    },
                    "event_type_display": {
                        "type": "string",
                        "readOnly": true
                    },
                    "description": {
                        "type": "string",
                        "nullable": true,
                        "description": "Human-readable description of the event"
                    },
                    "old_value": {
                        "type": "string",
                        "nullable": true,
                        "description": "Previous value (for status changes, etc.)",
                        "maxLength": 255
                    },
                    "new_value": {
                        "type": "string",
                        "nullable": true,
                        "description": "New value (for status changes, etc.)",
                        "maxLength": 255
                    },
                    "created_by": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true,
                        "nullable": true,
                        "description": "User who created this event (if applicable)"
                    },
                    "created_by_email": {
                        "type": "string",
                        "readOnly": true,
                        "nullable": true
                    },
                    "metadata": {
                        "nullable": true,
                        "description": "Additional event-specific data as JSON"
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    }
                },
                "required": [
                    "created_at",
                    "created_by",
                    "created_by_email",
                    "event_type",
                    "event_type_display",
                    "id",
                    "takedown",
                    "updated_at"
                ]
            },
            "TakedownEventTypeEnum": {
                "enum": [
                    "STATUS_CHANGED",
                    "ABUSE_REPORT_SENT",
                    "ABUSE_RESPONSE_RECEIVED",
                    "BLACKLIST_SUBMITTED",
                    "BLACKLIST_CONFIRMED",
                    "DNS_UPDATED",
                    "INFRASTRUCTURE_UPDATED",
                    "SCREENSHOT_TAKEN",
                    "NOTE",
                    "OTHER",
                    "DOMAIN_RESCANNED",
                    "DOMAIN_MARKED_FALSE_POSITIVE",
                    "POST_CLOSE_SKIPPED"
                ],
                "type": "string",
                "description": "* `STATUS_CHANGED` - Status Changed\n* `ABUSE_REPORT_SENT` - Abuse Report Sent\n* `ABUSE_RESPONSE_RECEIVED` - Abuse Response Received\n* `BLACKLIST_SUBMITTED` - Blacklist Submitted\n* `BLACKLIST_CONFIRMED` - Blacklist Confirmed\n* `DNS_UPDATED` - DNS Records Updated\n* `INFRASTRUCTURE_UPDATED` - Infrastructure Updated\n* `SCREENSHOT_TAKEN` - Screenshot Taken\n* `NOTE` - Note\n* `OTHER` - Other\n* `DOMAIN_RESCANNED` - Domain Rescanned\n* `DOMAIN_MARKED_FALSE_POSITIVE` - Domain Marked False Positive\n* `POST_CLOSE_SKIPPED` - Post-Close Work Skipped"
            },
            "TakedownResolutionEnum": {
                "enum": [
                    "DOMAIN_SUSPENDED",
                    "CONTENT_REMOVED",
                    "SERVER_HOLD",
                    "VOLUNTARY_TAKEDOWN",
                    "UNRESOLVED",
                    "FALSE_POSITIVE"
                ],
                "type": "string",
                "description": "* `DOMAIN_SUSPENDED` - Domain Suspended\n* `CONTENT_REMOVED` - Content Removed\n* `SERVER_HOLD` - Server Hold\n* `VOLUNTARY_TAKEDOWN` - Voluntary Takedown\n* `UNRESOLVED` - Unresolved\n* `FALSE_POSITIVE` - False Positive"
            },
            "TakedownStatusEnum": {
                "enum": [
                    "REQUESTED",
                    "OPEN",
                    "IN_PROGRESS",
                    "PENDING_RESPONSE",
                    "CLOSED",
                    "FAILED",
                    "REJECTED"
                ],
                "type": "string",
                "description": "* `REQUESTED` - Requested (Pending Approval)\n* `OPEN` - Open\n* `IN_PROGRESS` - In Progress\n* `PENDING_RESPONSE` - Pending Response\n* `CLOSED` - Closed\n* `FAILED` - Failed\n* `REJECTED` - Rejected"
            },
            "TierEnum": {
                "enum": [
                    "P1",
                    "P2",
                    "P3"
                ],
                "type": "string",
                "description": "* `P1` - P1 - Immediate (GSB, Netcraft, SmartScreen)\n* `P2` - P2 - Distribution (Spamhaus, Openphish, SpamCop)\n* `P3` - P3 - Prevention (VirusTotal, Symantec, ScamAdviser)"
            },
            "UserProfile": {
                "type": "object",
                "description": "Serializer for user profile information.",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "username": {
                        "type": "string",
                        "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
                        "pattern": "^[\\w.@+-]+$",
                        "maxLength": 150
                    },
                    "email": {
                        "type": "string",
                        "format": "email",
                        "readOnly": true
                    },
                    "pending_email": {
                        "type": "string",
                        "format": "email",
                        "readOnly": true,
                        "description": "Unconfirmed replacement address; the current email remains active."
                    },
                    "is_active": {
                        "type": "boolean",
                        "readOnly": true,
                        "title": "Active",
                        "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
                    },
                    "account_type": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/AccountTypeEnum"
                            }
                        ],
                        "readOnly": true,
                        "description": "Multi-brand account tier. Standard users derive their capabilities from the brands they have access to. Partner/Enterprise users have elevated write access across every brand they're a member of.\n\n* `standard` - Standard\n* `partner` - Partner\n* `enterprise` - Enterprise"
                    },
                    "display_plan": {
                        "type": "string",
                        "nullable": true,
                        "readOnly": true
                    },
                    "can_access_billing": {
                        "type": "boolean",
                        "readOnly": true
                    },
                    "can_start_customer_view": {
                        "type": "boolean",
                        "readOnly": true
                    },
                    "two_factor_enabled": {
                        "type": "boolean",
                        "description": "Enable email-based two-factor authentication for this user. Mandatory for staff and superusers."
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "readOnly": true
                    }
                },
                "required": [
                    "account_type",
                    "can_access_billing",
                    "can_start_customer_view",
                    "created_at",
                    "display_plan",
                    "email",
                    "id",
                    "is_active",
                    "pending_email",
                    "updated_at",
                    "username"
                ]
            },
            "PaginatedBrandDomainListItemList": {
                "type": "object",
                "required": [
                    "count",
                    "results"
                ],
                "properties": {
                    "count": {
                        "type": "integer",
                        "example": 123
                    },
                    "next": {
                        "type": "string",
                        "nullable": true,
                        "format": "uri",
                        "example": "http://api.example.org/accounts/?page=4"
                    },
                    "previous": {
                        "type": "string",
                        "nullable": true,
                        "format": "uri",
                        "example": "http://api.example.org/accounts/?page=2"
                    },
                    "results": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/BrandDomainListItem"
                        }
                    }
                }
            },
            "FieldValidationError": {
                "type": "object",
                "minProperties": 1,
                "description": "Field names mapped directly to validation error messages.",
                "additionalProperties": {
                    "anyOf": [
                        {
                            "type": "string"
                        },
                        {
                            "type": "array",
                            "items": {
                                "type": "string"
                            }
                        }
                    ]
                },
                "example": {
                    "target_website": [
                        "Target host is not a public address."
                    ]
                }
            }
        },
        "securitySchemes": {
            "ApiKeyAuth": {
                "type": "apiKey",
                "in": "header",
                "name": "Authorization",
                "description": "Static API key, sent in the `Authorization` header with the scheme `Api-Key`:\n\n```\nAuthorization: Api-Key nbty_<your_key>\n```\n\nThe key is the whole credential after `Api-Key ` (not `Bearer`). A key belongs to one nebty user and carries that user's current rights, including the right to request takedowns.\n\n**Getting a key.** Brand admins, partner and enterprise accounts with a verified email address create their key in the dashboard under **Settings > API key**. The full key is shown only once, so store it right away. Replacing the key makes the old one stop working at once, and so does revoking it. Creating, replacing and revoking a key work only from the dashboard, not with an API key. If your account cannot create a key, contact nebty.\n\nA missing, revoked or unknown key is answered with `401`."
            }
        }
    },
    "servers": [
        {
            "url": "https://app.nebty.de",
            "description": "Production"
        }
    ],
    "tags": [
        {
            "name": "Brands",
            "description": "The brands you monitor and their headline statistics."
        },
        {
            "name": "Domains",
            "description": "Domains that nebty associated with a brand, with the risk score and verdict computed for that brand."
        },
        {
            "name": "Scans",
            "description": "Scan results for the domains associated with a brand."
        },
        {
            "name": "Takedowns",
            "description": "Takedown cases against infringing websites: list, inspect, follow the event log and request new ones."
        },
        {
            "name": "Account",
            "description": "The user the API key belongs to."
        }
    ]
}
