openapi: 3.1.0
info:
  title: Nexren Shield Customer REST API
  version: 1.0.0
  description: "High-performance edge security, DDoS mitigation, Origin Lock, and cache control API for Nexren Shield.\n\n**Idempotency:** POST/PUT/PATCH/DELETE requests may send an `Idempotency-Key` header (1-128 characters of `[A-Za-z0-9_.:-]`). The first response for the same token, method, path and key is stored for 24 hours and replayed on retries with `Idempotent-Replayed: true`. Reusing a key with a different body returns 422; a concurrent duplicate returns 409. 5xx responses are not stored."
  contact:
    name: Nexren Shield Security Operations
    url: "https://shield.nexren.info"
    email: support@nexren.com
servers:
  - url: "https://shield.nexren.info/api"
    description: Nexren Shield Production API Gateway
security:
  - bearerAuth:[]
  - apiKeyAuth:[]
paths:
  /v1/whoami:
    get:
      summary: Inspect Active API Token & Auth Status
      description: Returns token metadata, granted scopes, client IP, and rate limit status.
      tags:
        - Identity
      responses:
        200:
          description: Token and auth details
        401:
          description: Unauthorized
  /v1/identity:
    get:
      summary: Get Authenticated Identity Profile
      description: Returns the authenticated user profile, organization info, and active plan.
      tags:
        - Identity
      responses:
        200:
          description: Identity profile details
        401:
          description: Unauthorized
  /v1/sites:
    get:
      summary: List Protected Websites
      description: Returns list of all sites accessible to the authenticated user.
      tags:
        - Sites
      parameters:
        - name: includePending
          in: query
          schema:
            type: boolean
          description: Include sites in pending setup
        - name: search
          in: query
          schema:
            type: string
          description: Filter by domain keyword
      responses:
        200:
          description: List of sites
        401:
          description: Unauthorized
        403:
          description: "Forbidden - Insufficient scope (requires sites:read)"
    post:
      summary: Create / Onboard New Site
      description: "Registers a new domain for edge protection. The caller must prove DNS control first: a CNAME pointing to the Shield target AND a TXT record at _nexren-challenge.<domain> carrying the caller's own verification token (both are required). Reserved test TLDs (.test, .invalid, .local) are exempt; platform admins may pass skipDnsCheck. When the domain's www/apex sibling or parent domain is registered under another account, the 201 response carries `relatedSite` (relation + hostname, never the other owner)."
      tags:
        - Sites
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - domain
              properties:
                domain:
                  type: string
                  example: api.example.com
                originUrl:
                  type: string
                  example: "https://198.51.100.25:443"
                mode:
                  type: string
                  enum:
                    - Off
                    - Monitor
                    - Balanced
                    - Under Attack
                  default: Balanced
                enabled:
                  type: boolean
                  default: true
      responses:
        201:
          description: Site created successfully
        400:
          description: Bad request - invalid domain or origin
        403:
          description: Forbidden - the hostname or its www/apex sibling belongs to a site you are only a team member of
        409:
          description: Conflict - domain already registered
        422:
          description: "dns_verification_required - CNAME and TXT ownership records not found for the caller's token"
  /v1/sites/{id}:
    get:
      summary: Get Site Configuration & Status
      description: Retrieves complete configuration and health status for a site.
      tags:
        - Sites
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Site configuration
        404:
          description: Site not found
    patch:
      summary: Update Site Settings
      description: Modifies origin URL, protection mode, or enabled state for a site.
      tags:
        - Sites
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                originUrl:
                  type: string
                mode:
                  type: string
                  enum:
                    - Off
                    - Monitor
                    - Balanced
                    - Under Attack
                enabled:
                  type: boolean
      responses:
        200:
          description: Site updated
        404:
          description: Site not found
    delete:
      summary: Delete Site
      description: Deletes a site and removes all edge configurations.
      tags:
        - Sites
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Site deleted
        404:
          description: Site not found
  /v1/sites/{id}/purge-cache:
    post:
      summary: Purge Edge Cache
      description: "Evicts the site's edge static-asset cache (all entries, or only the given paths with every query/encoding variant; a trailing * is a prefix match). Takes effect on the edge within effectiveWithinMs (about 1 s). entries/bytes are real numbers: the cache contents in the edge's last stats report (at most ~30 s old, timestamp countsAsOf). When they are not known, entries/bytes/purgedSize are null, countsExact is false and countsUnknownReason explains why; a made-up 0 is never returned. A full-site purge happens ONLY for {\"purgeAll\": true} (without paths) or for an empty body / {}. Anything else is either a path purge or a 400 that purges nothing: invalid JSON, a null or non-object body, an unknown field (e.g. \"path\"), paths that is null, not an array, empty or only blank strings, '*' or '/*' as a path (use purgeAll:true), a non-boolean purgeAll (null included), purgeAll:true together with paths, or purgeAll:false without paths."
      tags:
        - Sites
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              description: Optional. An empty body or {} purges the whole site.
              properties:
                purgeAll:
                  type: boolean
                  description: "true purges the whole site and must not be combined with paths; false requires paths. No default: omit the field instead of sending null."
                paths:
                  type: array
                  items:
                    type: string
                  minItems: 1
                  maxItems: 100
                  description: "URL paths to purge, e.g. /static/app.js or /assets/* (trailing * = prefix match). Not null; '*' and '/*' are rejected (use purgeAll:true)."
            examples:
              wholeSite:
                summary: Purge the whole site
                value:
                  purgeAll: true
              somePaths:
                summary: Purge specific paths
                value:
                  paths:
                    - /static/app.js
                    - /assets/*
      responses:
        200:
          description: Purge scheduled
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  message:
                    type: string
                  purgedAt:
                    type: integer
                  purgedSize:
                    type: string
                    nullable: true
                  scope:
                    type: string
                    enum:
                      - all
                      - paths
                  paths:
                    type: array
                    items:
                      type: string
                  entries:
                    type: integer
                    nullable: true
                    description: null when unknown
                  bytes:
                    type: integer
                    nullable: true
                    description: null when unknown
                  countsExact:
                    type: boolean
                  countsUnknownReason:
                    type: string
                    nullable: true
                    enum:
                      - no-edge-report
                      - report-predates-last-purge
                      - per-path-counts-not-reported
                      - report-truncated
                      - null
                  countsAsOf:
                    type: integer
                    nullable: true
                    description: Time (ms) of the report the counts describe
                  cacheEnabled:
                    type: boolean
                  generation:
                    type: integer
                  effectiveWithinMs:
                    type: integer
        400:
          description: "Invalid body (nothing is purged): malformed JSON, null or non-object body, unknown field, null/non-array/empty paths, '*' or '/*' as a path, non-boolean purgeAll, purgeAll:true with paths, or purgeAll:false without paths"
        404:
          description: Site not found
  /v1/sites/{id}/members:
    get:
      summary: List Site Team
      description: "Lists the site owner and the members this site is shared with. Each member carries `permissions` (per-area `view` / `edit` map), `role` (derived label: viewer | editor | manager when the set equals a preset, otherwise custom), username, invitedBy, createdAt and `manageable` (whether you may change/remove them). The response also returns your own `myPermissions`, `grantablePermissions`, the `permissionAreas` catalog and the `presets`. Emails are included only for callers with team:edit. Requires sites:read and the site permission team:view. Every site payload also carries `myRole` (owner | viewer | editor | manager | custom) and `myPermissions`."
      tags:
        - Sites
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Owner, members (with permissions), maxMembers, canManage, grantablePermissions, permissionAreas, presets
        403:
          description: "Insufficient site permission (team:view)"
        404:
          description: Site not found
    post:
      summary: Add Site Member
      description: "Shares the site with another existing Nexren Shield account, identified by its exact username or email, with a chosen permission set. Send `permissions` (per-area map, e.g. {\"analytics\":\"view\",\"security\":\"edit\"} or {\"security\":{\"view\":true,\"edit\":true}}) or a `preset` (viewer | editor | manager; `role` is accepted as an alias). Edit implies view; `edit` with an explicit `view: false`, unknown areas, unsupported levels, unmet prerequisites (visitorIps needs analytics:view, maintenanceHtml needs maintenance:edit) and an empty set are rejected with 400. Requires sites:write and team:edit. You can only grant permissions you hold yourself, and only the site owner can grant team:edit. Errors never reveal whether an account exists. Rate limited."
      tags:
        - Sites
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - identifier
              properties:
                identifier:
                  type: string
                  description: Exact username or email of an existing account
                permissions:
                  type: object
                  description: "Per-area permission map. Areas (modules): analytics (traffic & logs), visitorIps, security (WAF/DDoS/bot, mode, timed mode, WAF sensitivity, traffic profile, verification lifetime), rules (custom rules, IP lists, country/TLS, scoped WAF exceptions), access (Zero Trust), origin, ssl, cache, captcha, maintenance, maintenanceHtml, notifications (channels, owner alerts, all-my-sites scope), analysis, team. Values: none | view | edit, or {view: boolean, edit: boolean}."
                  additionalProperties:
                    oneOf:
                      - type: string
                        enum:
                          - none
                          - view
                          - edit
                      - type: object
                        properties:
                          view:
                            type: boolean
                          edit:
                            type: boolean
                preset:
                  type: string
                  enum:
                    - manager
                    - editor
                    - viewer
                  description: "Used when permissions is omitted: viewer = view only (every module view, visitor IPs masked, no team), editor = edits security/rules/maintenance/notifications/cache/analysis, manager = full admin (every module edit except team management, which only the owner grants)"
                role:
                  type: string
                  enum:
                    - manager
                    - editor
                    - viewer
                  description: Deprecated alias of preset
      responses:
        201:
          description: Member added
        400:
          description: Invalid permissions (code invalid_permissions)
        403:
          description: Insufficient site permission, or granting permissions beyond your own
        404:
          description: User not found or cannot be added (generic)
        409:
          description: Already a member, or member limit reached (the limit is checked before the account lookup, so it never reveals whether an account exists)
        429:
          description: Too many invite attempts
  /v1/sites/{id}/members/{userId}:
    patch:
      summary: Change Member Permissions
      description: "Replaces a member's permission set (`permissions` or `preset`; `role` accepted as an alias of preset). You need team:edit; unless you are the owner, the member must hold no permission you lack and must not have team:edit, the new set must be within your own, and you cannot grant team:edit. Nobody can change their own permissions. Every change re-validates the site's credential bindings: channels whose supplier lost notifications:edit and a Zero Trust 2FA credential whose supplier lost access:edit are detached (`detached`, `warnings`). The response includes the `diff` (added / removed permission tokens), which is also written to the audit log. Requires sites:write."
      tags:
        - Sites
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: userId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                permissions:
                  type: object
                preset:
                  type: string
                  enum:
                    - manager
                    - editor
                    - viewer
                role:
                  type: string
                  enum:
                    - manager
                    - editor
                    - viewer
      responses:
        200:
          description: Permissions changed
        400:
          description: Invalid permissions
        403:
          description: Insufficient site permission
        404:
          description: Member not found
    delete:
      summary: Remove Member / Leave Site
      description: "Removes a member (needs team:edit and, unless you are the owner, the member's permissions must be within yours and exclude team:edit) or, with your own user id or `me`, leaves the site. Channels and credentials the member bound to the site are detached immediately; `review` counts the Zero Trust access rules/tunnels the member added, which stay active on the site. Requires sites:write."
      tags:
        - Sites
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: userId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Member removed
        403:
          description: Insufficient site permission
        404:
          description: Member not found
  /v1/sites/{id}/origin:
    get:
      summary: Get Origin Configuration
      description: Retrieves origin IP/URL and Origin Lock status.
      tags:
        - Origin & Lock
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Origin details
        404:
          description: Site not found
    put:
      summary: Update Origin Configuration
      description: Updates origin IP/URL and Origin Lock header / secret.
      tags:
        - Origin & Lock
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                originUrl:
                  type: string
                originLockEnabled:
                  type: boolean
      responses:
        200:
          description: Origin updated
        404:
          description: Site not found
  /v1/sites/{id}/origin/lock/toggle:
    post:
      summary: Toggle Origin Lock
      description: Toggles cryptographic Origin Lock enforcement on or off for the site.
      tags:
        - Origin & Lock
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - enabled
              properties:
                enabled:
                  type: boolean
      responses:
        200:
          description: Origin lock toggled successfully
        404:
          description: Site not found
  /v1/policies/{id}:
    get:
      summary: Get Site Policy
      description: Retrieves protection mode and threat response rules for the site. A timed mode exposes `modeRevertTo` (the mode it will return to) and `modeRevertAt` (epoch ms); both are empty/0 for a permanent mode. Maintenance fields include `maintenanceEta` (UTC epoch ms as a string), `maintenanceContact` and `maintenanceAutoEnd`.
      tags:
        - Policies
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Policy details
        404:
          description: Site not found
    put:
      summary: Update Site Policy
      description: "Modifies protection mode ('Off', 'Monitor', 'Balanced', 'Under Attack') and challenge settings. Send `modeDurationMinutes` with `mode` to switch temporarily: the control plane reverts to the previous mode when the duration ends (checked every 30 s; plan limits apply, e.g. it never reverts into Under Attack on a plan without it). `modeDurationMinutes: 0` (or a mode change without a duration) makes the current mode permanent and cancels a pending revert. Also accepted on PATCH. Also per-site customizations: traffic profile, verification lifetime, WAF sensitivity."
      tags:
        - Policies
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                mode:
                  type: string
                  enum:
                    - Off
                    - Monitor
                    - Balanced
                    - Under Attack
                wafEnabled:
                  type: boolean
                botProtectionEnabled:
                  type: boolean
                challengeEnabled:
                  type: boolean
                rateLimitEnabled:
                  type: boolean
                trafficProfile:
                  type: string
                  enum:
                    - web
                    - api
                    - webhook
                    - realtime
                  default: web
                  description: "How unverified traffic is handled. 'web' serves the HTML verification page (default). 'api', 'webhook' and 'realtime' never serve HTML challenges: challenge decisions are logged and a strict per-IP rate limit applies instead (api 20, webhook 5, realtime 30 req/s; halved under attack; 429 JSON with Retry-After). WAF blocks, IP blacklist, geo blocks, Zero Trust and DDoS protections still apply. 'webhook' additionally does not treat a missing User-Agent or cookie-less POST as a bot signal."
                profilePaths:
                  type: string
                  description: "Newline-separated path patterns the traffic profile applies to (e.g. '/api/*\\n/webhook/*'). Empty = whole site. Other paths keep the 'web' behaviour. Max 100 lines, each starting with '/'."
                challengeTtlMinutes:
                  type: integer
                  minimum: 5
                  maximum: 43200
                  default: 30
                  description: Lifetime of the verification (clearance) cookie in minutes (5 minutes – 30 days). Capped at 15 minutes while Under Attack or automatic mitigation is active. Lowering it invalidates longer cookies immediately.
                modeDurationMinutes:
                  type: integer
                  minimum: 0
                  maximum: 10080
                  description: 0 = permanent (default); otherwise 5–10080 minutes (7 days) after which the previous mode is restored automatically. Send together with `mode`.
                maintenanceEta:
                  type: string
                  description: "Estimated completion as UTC epoch ms or ISO 8601 with a timezone (e.g. 2026-10-09T18:00:00Z). Empty string clears it."
                maintenanceContact:
                  type: string
                  description: E-mail address or https URL shown on the maintenance page. Empty string clears it.
                maintenanceAutoEnd:
                  type: boolean
                  description: Turn maintenance off automatically when maintenanceEta passes.
                wafSensitivity:
                  type: string
                  enum:
                    - low
                    - medium
                    - high
                  default: medium
                  description: "WAF score thresholds: low = block 85 / challenge 55, medium = 70 / 40, high = 55 / 30. Under Attack keeps the block threshold at most 45."
      responses:
        200:
          description: Policy updated (includes modeRevertTo / modeRevertAt)
        400:
          description: Invalid field value
        403:
          description: Insufficient site role or plan
        404:
          description: Site not found
    patch:
      summary: Patch Site Policy
      description: "Same as PUT: partial update, only the fields present in the body are changed."
      tags:
        - Policies
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                mode:
                  type: string
                  enum:
                    - Off
                    - Monitor
                    - Balanced
                    - Under Attack
                wafEnabled:
                  type: boolean
                botProtectionEnabled:
                  type: boolean
                challengeEnabled:
                  type: boolean
                rateLimitEnabled:
                  type: boolean
                trafficProfile:
                  type: string
                  enum:
                    - web
                    - api
                    - webhook
                    - realtime
                  default: web
                  description: "How unverified traffic is handled. 'web' serves the HTML verification page (default). 'api', 'webhook' and 'realtime' never serve HTML challenges: challenge decisions are logged and a strict per-IP rate limit applies instead (api 20, webhook 5, realtime 30 req/s; halved under attack; 429 JSON with Retry-After). WAF blocks, IP blacklist, geo blocks, Zero Trust and DDoS protections still apply. 'webhook' additionally does not treat a missing User-Agent or cookie-less POST as a bot signal."
                profilePaths:
                  type: string
                  description: "Newline-separated path patterns the traffic profile applies to (e.g. '/api/*\\n/webhook/*'). Empty = whole site. Other paths keep the 'web' behaviour. Max 100 lines, each starting with '/'."
                challengeTtlMinutes:
                  type: integer
                  minimum: 5
                  maximum: 43200
                  default: 30
                  description: Lifetime of the verification (clearance) cookie in minutes (5 minutes – 30 days). Capped at 15 minutes while Under Attack or automatic mitigation is active. Lowering it invalidates longer cookies immediately.
                modeDurationMinutes:
                  type: integer
                  minimum: 0
                  maximum: 10080
                  description: 0 = permanent (default); otherwise 5–10080 minutes (7 days) after which the previous mode is restored automatically. Send together with `mode`.
                maintenanceEta:
                  type: string
                  description: "Estimated completion as UTC epoch ms or ISO 8601 with a timezone (e.g. 2026-10-09T18:00:00Z). Empty string clears it."
                maintenanceContact:
                  type: string
                  description: E-mail address or https URL shown on the maintenance page. Empty string clears it.
                maintenanceAutoEnd:
                  type: boolean
                  description: Turn maintenance off automatically when maintenanceEta passes.
                wafSensitivity:
                  type: string
                  enum:
                    - low
                    - medium
                    - high
                  default: medium
                  description: "WAF score thresholds: low = block 85 / challenge 55, medium = 70 / 40, high = 55 / 30. Under Attack keeps the block threshold at most 45."
      responses:
        200:
          description: Policy updated
        400:
          description: Invalid field value
        403:
          description: Insufficient site role or plan
        404:
          description: Site not found
  /v1/sites/{id}/waf/rules:
    get:
      summary: List Site WAF Rules
      description: Returns custom WAF rules defined for the site.
      tags:
        - Policies
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: List of WAF rules
    post:
      summary: Create Site WAF Rule
      description: Creates a new custom WAF rule.
      tags:
        - Policies
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        201:
          description: WAF rule created
  /v1/sites/{id}/waf/rules/{ruleId}:
    delete:
      summary: Delete Site WAF Rule
      description: Deletes a custom WAF rule.
      tags:
        - Policies
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ruleId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: WAF rule deleted
  /v1/events:
    get:
      summary: Query Security Events
      description: Returns paginated stream of security events, challenge outcomes, and blocked threats.
      tags:
        - Events
      parameters:
        - name: siteId
          in: query
          schema:
            type: string
          description: Filter by site ID
        - name: action
          in: query
          schema:
            type: string
            enum:
              - block
              - challenge
              - allow
          description: Filter by event action
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
          description: Number of events (max 100)
      responses:
        200:
          description: Security events log
        401:
          description: Unauthorized
  /v1/analytics/summary:
    get:
      summary: Get Summary Metrics
      description: Request counts, estimated bandwidth and mitigation statistics. summary.cachedRatio is the measured edge cache hit ratio (hits / (hits + misses) reported by the edge processes since they started), null when there were no cache-eligible requests.
      tags:
        - Analytics
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum:
              - 1h
              - 24h
              - 7d
              - 30d
            default: 24h
      responses:
        200:
          description: Analytics summary
        401:
          description: Unauthorized
  /v1/tokens:
    get:
      summary: List API Tokens
      description: Returns all API tokens owned by the authenticated user with masked prefix previews.
      tags:
        - Tokens
      responses:
        200:
          description: List of API tokens
        401:
          description: Unauthorized
    post:
      summary: Create API Token
      description: Creates a new scoped API token. Secret key is returned only once in response.
      tags:
        - Tokens
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  example: Terraform Key
                scopes:
                  type: array
                  items:
                    type: string
                  example:
                    - "sites:read"
                    - "sites:write"
                    - "analytics:read"
                expiresInDays:
                  type: integer
                  default: 30
                ipRestrictions:
                  type: string
                  example: 198.51.100.10
      responses:
        201:
          description: Token created
        400:
          description: Invalid scope or name
  /v1/tokens/{id}:
    delete:
      summary: Delete / Revoke API Token
      description: Permanently deletes and revokes an API token.
      tags:
        - Tokens
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Token revoked
        404:
          description: Token not found
  /v1/sites/{id}/origin/lock/snippets:
    get:
      summary: Get Origin Lock Web Server Snippets
      description: "Returns ready-to-paste firewall and web server configuration snippets (Nginx, PHP, Apache, Caddy, Cloudflare, UFW, Iptables) configured with the site's unique cryptographic header and secret."
      tags:
        - Origin & Lock
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Origin Lock code snippets
        404:
          description: Site not found
  /v1/sites/{id}/origin/lock/verify:
    post:
      summary: Verify Origin Lock Live
      description: Executes live cryptographic handshake probe to verify origin web server blocks direct IP access while allowing Nexren Shield edge proxy.
      tags:
        - Origin & Lock
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Origin Lock verification result
        404:
          description: Site not found
  /v1/sites/{id}/tunnels:
    get:
      summary: List Site Tunnels
      description: Lists all Nexren Tunnels configured for zero-open-port reverse origin tunneling on this site.
      tags:
        - Tunnels
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: List of site tunnels
        404:
          description: Site not found
    post:
      summary: Create Nexren Tunnel
      description: Provisions a new encrypted WebSocket/QUIC reverse tunnel token for exposing origins behind strict firewalls and NATs without public IPs.
      tags:
        - Tunnels
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  example: Primary App Cluster
                localTarget:
                  type: string
                  example: "http://127.0.0.1:3000"
      responses:
        201:
          description: Tunnel created with secret token
        400:
          description: Bad request
  /v1/sites/{id}/tunnels/{tunnelId}:
    get:
      summary: Get Site Tunnel Details
      description: Retrieves live connection status, RTT latency, stream count, and credentials for a tunnel.
      tags:
        - Tunnels
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: tunnelId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Tunnel details
        404:
          description: Tunnel not found
    delete:
      summary: Revoke Site Tunnel
      description: Immediately revokes and disconnects an active Nexren Tunnel.
      tags:
        - Tunnels
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: tunnelId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Tunnel revoked
        404:
          description: Tunnel not found
  /v1/sites/{id}/origin-kind:
    post:
      summary: Switch Site Origin Kind
      description: "Switches origin routing mode between direct IP/URL ('direct') and reverse tunnel ('tunnel')."
      tags:
        - Tunnels
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - originKind
              properties:
                originKind:
                  type: string
                  enum:
                    - direct
                    - tunnel
      responses:
        200:
          description: Origin kind switched
        400:
          description: Invalid kind
  /v1/tunnels:
    get:
      summary: List All User Tunnels
      description: Lists all Nexren Tunnels across all accessible sites for the authenticated account.
      tags:
        - Tunnels
      responses:
        200:
          description: List of all user tunnels
        401:
          description: Unauthorized
  /v1/sites/{id}/waf/exceptions:
    get:
      summary: List Scoped WAF Exceptions
      description: Returns scoped WAF exceptions in the legacy line format (`<ruleId> <path> [methods]`). Since v4.10.231 they are stored as custom rules (action `skip` with `wafRules`); this view lists custom rules that only have a glob path (+ methods) condition, plus any legacy line that could not be migrated.
      tags:
        - WAF
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: List of WAF exceptions
        404:
          description: Site not found
    put:
      summary: Update Scoped WAF Exceptions
      description: Replaces all scoped exceptions (<ruleId> <path> [methods]); written as custom rules (`skip` + `wafRules`), other custom rules are kept. Whole-site wildcards are strictly rejected for safety. Site manager role. POST /v1/sites/{id}/waf/exceptions appends one line the same way.
      tags:
        - WAF
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                raw:
                  type: string
                  example: "930120 /api/v1/upload POST\n941100 /admin/* *"
      responses:
        200:
          description: Exceptions updated
        400:
          description: Syntax or wildcard violation
  /v1/sites/{id}/waf/exceptions/{ruleId}:
    delete:
      summary: Delete WAF Exception Rule
      description: Removes the rule ID from every scoped exception (custom rules with `wafRules`) of the site. Site manager role.
      tags:
        - WAF
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ruleId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Exception deleted
        404:
          description: Site not found
  /v1/sites/{id}/custom-rules:
    get:
      summary: List Custom Rules
      description: "Ordered custom rules (first match wins; `log` records and continues; `override` and WAF-rule-scoped `skip` are non-final phases), the built-in templates and the per-site limit (100). Viewer role and policies:read or sites:read."
      tags:
        - Custom Rules
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Rules, templates and limits
        404:
          description: Site not found
    put:
      summary: Replace Custom Rules (ordered)
      description: "Replaces the whole ordered list. Site manager role and policies:write or sites:write. `allow` skips WAF, bot, rate limiting and verification — but NOT the global/site IP blacklist, country rule, maintenance, Zero Trust or kernel/DDoS flood protection. Relaxing rules (`allow`, `skip`) with a path condition are not applied to ambiguous paths (encoded separators, `..`, `;`). No user regex: paths match by prefix, exact or `*`-glob."
      tags:
        - Custom Rules
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - rules
              properties:
                rules:
                  type: array
                  maxItems: 100
                  items:
                    $ref: "#/components/schemas/CustomRule"
      responses:
        200:
          description: Saved rules (ids assigned)
        400:
          description: Validation error
        404:
          description: Site not found or role below manager
    post:
      summary: Add Custom Rule
      description: "Adds one rule at `position` (default: end). Site manager role."
      tags:
        - Custom Rules
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                rule:
                  $ref: "#/components/schemas/CustomRule"
                position:
                  type: integer
                  minimum: 0
      responses:
        201:
          description: Rule created
        400:
          description: Validation error or limit reached
  /v1/sites/{id}/custom-rules/order:
    post:
      summary: Reorder Custom Rules
      description: `ids` must list every rule id exactly once, in the new evaluation order. Site manager role.
      tags:
        - Custom Rules
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - ids
              properties:
                ids:
                  type: array
                  items:
                    type: string
      responses:
        200:
          description: Reordered rules
        400:
          description: ids is not a permutation of the current rule ids
  /v1/sites/{id}/custom-rules/{ruleId}:
    get:
      summary: Get Custom Rule
      tags:
        - Custom Rules
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ruleId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Rule
        404:
          description: Not found
    patch:
      summary: Update Custom Rule
      description: Merges the given fields into the rule; its position is kept. Site manager role.
      tags:
        - Custom Rules
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ruleId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomRule"
      responses:
        200:
          description: Updated rule
        400:
          description: Validation error
        404:
          description: Not found
    put:
      summary: Replace Custom Rule
      description: Replaces the rule in place (position kept). Site manager role.
      tags:
        - Custom Rules
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ruleId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomRule"
      responses:
        200:
          description: Updated rule
        400:
          description: Validation error
        404:
          description: Not found
    delete:
      summary: Delete Custom Rule
      tags:
        - Custom Rules
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ruleId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Deleted
        404:
          description: Not found
  /v1/sites/{id}/exemptions:
    get:
      summary: Unified Exemptions View
      description: Every exemption source in one list — IP-excluded paths, Zero Trust excluded paths (with the implicit effect of scope `all`), the IP allow-list action, ZT session bypasses and custom rules (`allow`/`skip`, WAF-rule-scoped `skip`, relaxing `override`) — each with what it skips.
      tags:
        - Custom Rules
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Exemption rows
        404:
          description: Site not found
  /v1/sites/{id}/explain:
    get:
      summary: Why Was This Request Blocked?
      description: "Looks up a request id in this site's event log and replays today's policy step by step (blacklist, IP access, country, maintenance, Zero Trust, custom rules, exemptions, DDoS, override rule, WAF, bot), with a suggested fix (e.g. a custom-rule draft that skips only the matched WAF rule ids, or a `skip waf` draft). Only this site's events; the client IP is masked for the viewer role. Lookup is by request id only. Scope events:read, policies:read or sites:read."
      tags:
        - Custom Rules
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: requestId
          in: query
          required: true
          schema:
            type: string
            pattern: ^[A-Za-z0-9_-]{8,80}$
      responses:
        200:
          description: Event, network data and trace
        400:
          description: Malformed request id
        404:
          description: No event for this request id on this site
  /v1/sites/{id}/waf/exceptions/validate:
    post:
      summary: Validate Scoped WAF Exceptions Syntax
      description: Dry-run syntax validator for exception rules without persisting changes.
      tags:
        - WAF
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - raw
              properties:
                raw:
                  type: string
                  example: 930120 /api/upload POST
      responses:
        200:
          description: Validation result
  /v1/sites/{id}/ddos:
    get:
      summary: Get Rate Limiting & DDoS Mitigation Settings
      description: Retrieves rate limiting RPS, burst headroom, challenge sensitivity, and live mitigation state.
      tags:
        - DDoS & Rate Limiting
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: DDoS and rate limit configuration
        404:
          description: Site not found
    put:
      summary: Update Rate Limiting & DDoS Settings
      description: Updates RPS limit, burst capacity, mitigation mode, and sensitivity threshold.
      tags:
        - DDoS & Rate Limiting
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                rateLimitRps:
                  type: integer
                  example: 100
                rateLimitBurst:
                  type: integer
                  example: 150
                ddosSensitivity:
                  type: string
                  enum:
                    - low
                    - medium
                    - high
                    - paranoid
                challengeBotDDoS:
                  type: boolean
      responses:
        200:
          description: DDoS settings updated
        400:
          description: Invalid parameters
  /v1/sites/{id}/ddos/mitigate:
    post:
      summary: Trigger Immediate DDoS Mitigation
      description: "Forces site into emergency 'Under Attack' mode with mandatory JS PoW/CAPTCHA challenge."
      tags:
        - DDoS & Rate Limiting
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Mitigation engaged
        404:
          description: Site not found
  /v1/sites/{id}/ddos/reset:
    post:
      summary: Reset Rate Limit Windows
      description: Clears sliding-window rate counters and ban-lists for the site.
      tags:
        - DDoS & Rate Limiting
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Rate limits reset
        404:
          description: Site not found
  /v1/notifications/channels:
    get:
      summary: List Notification Channels
      description: "Returns every notification channel visible to the token owner. Each channel has a `scope`: `site` (one site; `siteId` set), `my_sites` (every site the token owner owns or holds the `notifications:edit` site permission on, resolved live at delivery time — never other tenants' sites) or `platform` (platform administrators only; `siteFilter` all_sites/selected_sites/exclude_sites). Targets are always masked (`targetPreview`). Site channels are readable with `notifications:view`; `my_sites` channels only by their creator."
      tags:
        - Notifications
      parameters:
        - name: scope
          in: query
          schema:
            type: string
            enum:
              - all
              - site
              - my_sites
              - platform
            default: all
        - name: siteId
          in: query
          schema:
            type: string
          description: Only site channels of this site and multi-site channels that cover it
      responses:
        200:
          description: Channel list
          content:
            application/json:
              schema:
                type: object
                properties:
                  channels:
                    type: array
                    items:
                      $ref: "#/components/schemas/NotificationChannel"
                  total:
                    type: integer
        400:
          description: Invalid scope
        404:
          description: Site not found
    post:
      summary: Create Notification Channel
      description: "Creates a channel. `scope: site` (default) needs `siteId` and site role 'manager'. `scope: my_sites` covers every site you own or manage. `scope: platform` requires a platform administrator. Non-admin accounts may only bind their own credentials and send email only to their own account address."
      tags:
        - Notifications
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotificationChannelInput"
      responses:
        201:
          description: Channel created
          content:
            application/json:
              schema:
                type: object
                properties:
                  channel:
                    $ref: "#/components/schemas/NotificationChannel"
        400:
          description: Validation error
        403:
          description: Scope or recipient not allowed
        404:
          description: Site not found
  /v1/notifications/channels/{id}:
    get:
      summary: Get Notification Channel
      tags:
        - Notifications
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Channel
          content:
            application/json:
              schema:
                type: object
                properties:
                  channel:
                    $ref: "#/components/schemas/NotificationChannel"
        404:
          description: Not found
    patch:
      summary: Update Notification Channel
      description: "Merges the given fields. `scope` can switch only between `my_sites` and `platform` (platform: administrators only); a site channel stays site-scoped — recreate it to widen its scope."
      tags:
        - Notifications
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotificationChannelInput"
      responses:
        200:
          description: Updated channel
        400:
          description: Validation error
        403:
          description: Not allowed
        404:
          description: Not found
    delete:
      summary: Delete Notification Channel
      tags:
        - Notifications
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Deleted
        404:
          description: Not found
  /v1/notifications/channels/{id}/test:
    post:
      summary: Send Test Notification
      tags:
        - Notifications
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Test attempted
        404:
          description: Not found
  /v1/sites/{id}/notifications/channels:
    get:
      summary: List Channels Covering a Site
      description: Site channels of the site plus the `my_sites`/`platform` channels visible to you that cover it.
      tags:
        - Notifications
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: scope
          in: query
          schema:
            type: string
            enum:
              - all
              - site
              - my_sites
              - platform
            default: all
      responses:
        200:
          description: Channel list
        404:
          description: Site not found
    post:
      summary: Create Site Channel
      description: "Creates a `site`-scoped channel on this site (site role 'manager')."
      tags:
        - Notifications
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotificationChannelInput"
      responses:
        201:
          description: Channel created
        403:
          description: Insufficient role
        404:
          description: Site not found
  /v1/sites/{id}/notifications/owner-alerts:
    get:
      summary: Get Owner Alert Defaults
      description: "When the site has no notification channel subscribed to a critical event (tunnel offline/online, certificate expiring/renewal failed, quota 80%/100%, origin lock broken, DDoS start), Shield emails the site owner's account address. Returns that setting, the optional daily summary flag and the list of covered events."
      tags:
        - Notifications
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Owner alert settings
        404:
          description: Site not found
    patch:
      summary: Update Owner Alert Defaults
      description: "Turns the owner email fallback (default on) and the daily summary email (default off, sent around 09:00 Europe/Istanbul) on or off. Requires site role 'manager'."
      tags:
        - Notifications
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                ownerAlertsEnabled:
                  type: boolean
                ownerDigestEnabled:
                  type: boolean
      responses:
        200:
          description: Updated settings
        400:
          description: No boolean field given
        403:
          description: Insufficient site role
        404:
          description: Site not found
  /v1/sites/{id}/maintenance:
    get:
      summary: Get Maintenance Mode Status
      description: Retrieves maintenance mode toggle state, custom HTML template, ETA, and IP bypass list.
      tags:
        - Maintenance Mode
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Maintenance configuration
        404:
          description: Site not found
    put:
      summary: Update Maintenance Mode Configuration
      description: Updates maintenance mode enabled state, custom HTML page, bypass CIDR IPs, and estimated completion time.
      tags:
        - Maintenance Mode
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
                customHtml:
                  type: string
                bypassIps:
                  type: array
                  items:
                    type: string
                eta:
                  type: string
                  description: "Estimated completion: UTC epoch ms or ISO 8601 with a timezone (e.g. 2026-10-09T18:00:00Z); null or empty clears it. Shown on the maintenance page in UTC and drives Retry-After."
                contact:
                  type: string
                  description: E-mail address or https URL shown on the maintenance page; null or empty clears it.
                autoEnd:
                  type: boolean
                  description: Turn maintenance off automatically when the ETA passes (default false).
                statusCode:
                  type: integer
                  default: 503
      responses:
        200:
          description: Maintenance mode updated
        400:
          description: Bad request
  /v1/sites/{id}/maintenance/toggle:
    post:
      summary: Toggle Maintenance Mode
      description: Quick toggle to switch maintenance mode on or off.
      tags:
        - Maintenance Mode
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
      responses:
        200:
          description: Maintenance mode toggled
  /v1/sites/{id}/maintenance/preview:
    get:
      summary: Preview Maintenance Page
      description: Renders the maintenance HTML template as it will be served to visitors during maintenance.
      tags:
        - Maintenance Mode
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: HTML maintenance page
        404:
          description: Site not found
  /v1/sites/{id}/zt/access-rules:
    get:
      summary: List Zero Trust Access Rules
      description: Returns path-based Zero Trust access rules protecting private routes and admin portals.
      tags:
        - Zero Trust
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: List of access rules
        404:
          description: Site not found
    post:
      summary: Create Zero Trust Access Rule
      description: Creates an access policy rule requiring TOTP 2FA, corporate email domain verification, or IP whitelist.
      tags:
        - Zero Trust
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - pathPattern
              properties:
                name:
                  type: string
                  example: Admin Portal 2FA
                pathPattern:
                  type: string
                  example: /admin/*
                action:
                  type: string
                  enum:
                    - allow
                    - block
                    - authenticate
                  default: authenticate
                require2fa:
                  type: boolean
                  default: false
                allowedEmails:
                  type: array
                  items:
                    type: string
                allowedIps:
                  type: array
                  items:
                    type: string
      responses:
        201:
          description: Access rule created
        400:
          description: Bad request
  /v1/sites/{id}/zt/access-rules/{ruleId}:
    get:
      summary: Get Zero Trust Access Rule
      description: Retrieves single access policy rule by ID.
      tags:
        - Zero Trust
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ruleId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Access rule details
        404:
          description: Rule not found
    put:
      summary: Update Zero Trust Access Rule
      description: Updates an existing Zero Trust access policy rule.
      tags:
        - Zero Trust
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ruleId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                pathPattern:
                  type: string
                action:
                  type: string
                  enum:
                    - allow
                    - block
                    - authenticate
                require2fa:
                  type: boolean
      responses:
        200:
          description: Access rule updated
        404:
          description: Rule not found
    delete:
      summary: Delete Zero Trust Access Rule
      description: Deletes a Zero Trust access policy rule.
      tags:
        - Zero Trust
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ruleId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Access rule deleted
        404:
          description: Rule not found
  /v1/sites/{id}/zt/users/{userId}/verify-2fa:
    post:
      summary: Verify User 2FA Token
      description: Validates a 6-digit TOTP code against enrolled Zero Trust user secret.
      tags:
        - Zero Trust
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: userId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - token
              properties:
                token:
                  type: string
                  example: 123456
      responses:
        200:
          description: 2FA verification result
        400:
          description: Invalid token
  /v1/sites/{id}/zt/sessions:
    get:
      summary: Get Zero Trust Sessions
      description: Lists active authenticated Zero Trust sessions and timeout policies, including `excludedPaths` and `excludedPathsScope`.
      tags:
        - Zero Trust
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Active sessions and policy
        404:
          description: Site not found
    patch:
      summary: Update Zero Trust Session & Exemption Policy
      description: "Site manager role. `excludedPathsScope`: `all` (default, historical) — a path excluded from Zero Trust is ALSO exempt from IP/country rules, verification, flood verification and bot decisions; `zt_only` — the path skips only Zero Trust. Same body is accepted on PUT and on /zt/exemptions."
      tags:
        - Zero Trust
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                durationMinutes:
                  type: integer
                authMethod:
                  type: string
                  enum:
                    - email_only
                    - totp_only
                    - both
                exemptionScope:
                  type: string
                  enum:
                    - cookie_only
                    - ip_only
                    - hybrid
                bypassRateLimit:
                  type: boolean
                bypassWaf:
                  type: boolean
                ipWhitelist:
                  type: string
                excludedPaths:
                  type: string
                  description: One path pattern per line
                excludedPathsScope:
                  type: string
                  enum:
                    - all
                    - zt_only
                protectedPaths:
                  type: string
      responses:
        200:
          description: Updated policy
        400:
          description: Invalid value
        404:
          description: Site not found or role below manager
  /v1/sites/{id}/zt/sessions/revoke:
    post:
      summary: Revoke Zero Trust Sessions
      description: Revokes active Zero Trust sessions for a specific user email or all users across the site.
      tags:
        - Zero Trust
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  example: dev@example.com
      responses:
        200:
          description: Sessions revoked
        404:
          description: Site not found
  /v1/sites/{id}/incidents:
    get:
      summary: List Site DDoS Incidents
      description: Returns recorded volumetric and L7 flood attack mitigation incidents for the site.
      tags:
        - Threats & Incidents
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 30
      responses:
        200:
          description: List of incidents
        404:
          description: Site not found
  /v1/incidents:
    get:
      summary: List All DDoS Incidents
      description: Returns recorded DDoS attack incidents across all sites accessible to the account.
      tags:
        - Threats & Incidents
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 30
      responses:
        200:
          description: List of all DDoS incidents
        401:
          description: Unauthorized
  /v1/threats/live:
    get:
      summary: Real-Time Live Threat Feed
      description: Returns live stream of blocked attack traffic, matched WAF rules, attacker IP masking, and geo-location.
      tags:
        - Threats & Incidents
      parameters:
        - name: siteId
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
      responses:
        200:
          description: Live threat feed
        401:
          description: Unauthorized
  /v1/threats/summary:
    get:
      summary: Threat Intelligence Summary
      description: Returns aggregated attack distribution breakdown by category (SQLi, XSS, RCE, DDoS, Bot, Scanner).
      tags:
        - Threats & Incidents
      parameters:
        - name: siteId
          in: query
          schema:
            type: string
        - name: period
          in: query
          schema:
            type: string
            enum:
              - 1h
              - 24h
              - 7d
              - 30d
            default: 24h
      responses:
        200:
          description: Threat summary metrics
        401:
          description: Unauthorized
  /v1/sites/{id}/audit-logs:
    get:
      summary: List Site Audit Logs
      description: Returns security audit trail of configuration changes, key rotations, and actions on this site.
      tags:
        - Audit Logs
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
        - name: category
          in: query
          schema:
            type: string
      responses:
        200:
          description: Site audit logs
        404:
          description: Site not found
  /v1/audit-logs:
    get:
      summary: List Account Audit Logs
      description: Returns security audit trail across all accessible sites and API activities.
      tags:
        - Audit Logs
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
        - name: category
          in: query
          schema:
            type: string
        - name: search
          in: query
          schema:
            type: string
      responses:
        200:
          description: Account audit logs
        401:
          description: Unauthorized
  /v1/audit-logs/{logId}:
    get:
      summary: Get Audit Log By ID
      description: Retrieves complete audit record details including client IP, actor, diff, and timestamp.
      tags:
        - Audit Logs
      parameters:
        - name: logId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Audit log entry
        404:
          description: Audit log not found
components:
  schemas:
    NotificationChannel:
      type: object
      properties:
        id:
          type: string
          description: Site channels start with `nch_`, multi-site channels with `notif_rule_`
        scope:
          type: string
          enum:
            - site
            - my_sites
            - platform
        siteId:
          type:
            - string
            - null
          description: Set only for `site` scope
        siteFilter:
          type: string
          enum:
            - all_sites
            - selected_sites
            - exclude_sites
          description: `platform` scope only
        siteIds:
          type: array
          items:
            type: string
          description: `platform` scope with selected/exclude filter
        coveredSiteCount:
          type: integer
          description: "`my_sites` scope: sites currently covered"
        name:
          type: string
        platform:
          type: string
          enum:
            - email
            - telegram
            - discord
            - slack
            - webhook
        enabled:
          type: boolean
        targetPreview:
          type: string
          description: Masked destination; secrets are never returned
        events:
          type: array
          items:
            type: string
        lastStatus:
          type: string
        lastSentAt:
          type: integer
        sentCount:
          type: integer
        createdAt:
          type: integer
    NotificationChannelInput:
      type: object
      properties:
        scope:
          type: string
          enum:
            - site
            - my_sites
            - platform
          default: site
        siteId:
          type: string
          description: Required for `site` scope (or use /v1/sites/{id}/notifications/channels)
        siteFilter:
          type: string
          enum:
            - all_sites
            - selected_sites
            - exclude_sites
          default: all_sites
          description: `platform` scope only
        siteIds:
          type: array
          items:
            type: string
        name:
          type: string
          maxLength: 80
        platform:
          type: string
          enum:
            - email
            - telegram
            - discord
            - slack
            - webhook
        credentialId:
          type: string
          description: Saved credential to deliver through (your own; multi-site scopes only)
        target:
          oneOf:
            - type: string
            - type: object
          description: Destination when no credential is given
        events:
          type: array
          items:
            type: string
        minSeverity:
          type: string
          enum:
            - all
            - high_critical
            - critical_only
          description: Multi-site scopes only
        language:
          type: string
          enum:
            - tr
            - en
        enabled:
          type: boolean
          default: true
    CustomRule:
      type: object
      required:
        - name
        - action
        - match
      properties:
        id:
          type: string
          pattern: ^cr_[a-z]{6,24}$
          description: Assigned by the server when missing or invalid
        name:
          type: string
          maxLength: 80
        enabled:
          type: boolean
          default: true
        action:
          type: string
          enum:
            - block
            - challenge
            - allow
            - skip
            - log
            - override
          description: "`override` (replaces the former path rules) is a separate, non-final phase: the first matching override rule changes mode/WAF/bot/verification for the request and may add a per-rule rate limit; final rules are still evaluated."
        skip:
          type: array
          items:
            type: string
            enum:
              - waf
              - bot
              - rate_limit
              - challenge
          description: Required (non-empty) when action is skip
        wafRules:
          type: array
          maxItems: 50
          items:
            type: string
            pattern: "^([A-Za-z0-9_.:-]{1,64}|\\*)$"
          description: "Only with action skip and skip = [\"waf\"]: skip only these WAF rule ids instead of the whole WAF (replaces the former scoped WAF exceptions). Not final — every matching rule of this kind accumulates."
        override:
          type: object
          description: Only with action override. Omitted fields inherit the site setting. Relaxing fields (waf/bot off, Off/Monitor mode, challenge never) are not applied on ambiguous paths when the rule has a path condition.
          properties:
            mode:
              type: string
              enum:
                - Off
                - Monitor
                - Balanced
                - Under Attack
            waf:
              type: string
              enum:
                - on
                - off
            botProtection:
              type: string
              enum:
                - on
                - off
            challenge:
              type: string
              enum:
                - always
                - never
            rateLimit:
              type: object
              required:
                - requests
                - windowSec
              properties:
                requests:
                  type: integer
                  minimum: 1
                  maximum: 100000
                windowSec:
                  type: integer
                  minimum: 1
                  maximum: 3600
                action:
                  type: string
                  enum:
                    - block
                    - challenge
                  default: block
        match:
          type: object
          description: All given conditions must match (AND). At least one condition.
          properties:
            path:
              type: object
              properties:
                op:
                  type: string
                  enum:
                    - prefix
                    - exact
                    - glob
                values:
                  type: array
                  maxItems: 50
                  items:
                    type: string
            methods:
              type: array
              items:
                type: string
                enum:
                  - GET
                  - HEAD
                  - POST
                  - PUT
                  - PATCH
                  - DELETE
                  - OPTIONS
                  - TRACE
                  - CONNECT
            userAgent:
              type: object
              properties:
                op:
                  type: string
                  enum:
                    - contains
                    - not_contains
                values:
                  type: array
                  items:
                    type: string
            asn:
              type: object
              properties:
                op:
                  type: string
                  enum:
                    - in
                    - not_in
                values:
                  type: array
                  items:
                    type: integer
            country:
              type: object
              properties:
                op:
                  type: string
                  enum:
                    - in
                    - not_in
                values:
                  type: array
                  items:
                    type: string
                    pattern: ^[A-Z]{2}$
            network:
              type: object
              properties:
                op:
                  type: string
                  enum:
                    - is
                    - is_not
                values:
                  type: array
                  items:
                    type: string
                    enum:
                      - datacenter
                      - tor
            headers:
              type: array
              maxItems: 5
              items:
                type: object
                properties:
                  name:
                    type: string
                  op:
                    type: string
                    enum:
                      - contains
                      - equals
                      - present
                      - absent
                  value:
                    type: string
            query:
              type: array
              maxItems: 5
              items:
                type: object
                properties:
                  name:
                    type: string
                    description: "\"*\" = any parameter (contains/equals only)"
                  op:
                    type: string
                    enum:
                      - contains
                      - equals
                      - present
                      - absent
                  value:
                    type: string
            ip:
              type: object
              properties:
                op:
                  type: string
                  enum:
                    - in
                    - not_in
                values:
                  type: array
                  maxItems: 100
                  items:
                    type: string
                    description: IP or CIDR
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: nx_live_<entropy>
      description: Standard HTTP Authorization header with Bearer token format
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: Alternative custom header passing the raw API token secret
    scopes:
      sites:read: View websites and configuration
      sites:write: Create and update websites and settings
      sites:delete: Delete websites
      policies:read: Read security policy and mode settings
      policies:write: Modify security policy and mode settings
      analytics:read: Read analytics and traffic metrics
      events:read: Read security event logs
      tokens:read: List API tokens
      tokens:write: Create and revoke API tokens
