{
  "openapi": "3.1.0",
  "info": {
    "title": "xOTT Access & Sales API",
    "version": "1.1.0",
    "description": "Commercial subscriber, package, reseller, distributed-session, device-adapter, and self-service API. Permanent playlist URLs are returned by output endpoints; short-lived signed media authorization remains internal. Staff endpoints accept an interactive JWT or an independently scoped xott_sk API key. Mutating reseller purchases require an Idempotency-Key. API keys belong in Authorization headers, never URLs. Country, ASN, and ISP policy attributes are trusted only when the controller is configured with the corresponding XOTT_TRUSTED_*_HEADER and a trusted reverse proxy overwrites that header."
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "tags": [
    {
      "name": "Staff"
    },
    {
      "name": "Packages"
    },
    {
      "name": "Subscribers"
    },
    {
      "name": "Security"
    },
    {
      "name": "Resellers"
    },
    {
      "name": "Customers"
    },
    {
      "name": "Adapters"
    },
    {
      "name": "Sessions"
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "EPG"
    },
    {
      "name": "Commerce"
    }
  ],
  "components": {
    "securitySchemes": {
      "staffBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT or xott_sk API key",
        "description": "Staff integration keys are valid only below /api/access, remain bounded by the owner staff role, and require the documented domain scope."
      },
      "resellerBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT or xott_rk API key"
      },
      "customerBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "SecurityPolicy": {
        "type": "object",
        "properties": {
          "allowedCountries": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[A-Z]{2}$"
            }
          },
          "allowedCidrs": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allowedAsns": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^\\d+$"
            }
          },
          "blockedAsns": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^\\d+$"
            }
          },
          "blockedIsps": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "blockedUserAgents": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "maxDevices": {
            "type": "integer",
            "minimum": 0
          },
          "lockFirstIp": {
            "type": "boolean"
          },
          "restreamAllowed": {
            "type": "boolean"
          },
          "detectSharedUse": {
            "type": "boolean"
          },
          "maxIpChangesPerHour": {
            "type": "integer",
            "minimum": 2
          },
          "maxDeviceChangesPerDay": {
            "type": "integer",
            "minimum": 2
          },
          "maxSessionStartsPerHour": {
            "type": "integer",
            "minimum": 5
          }
        }
      },
      "OutputProfile": {
        "type": "object",
        "properties": {
          "linearEnabled": {
            "type": "boolean"
          },
          "eventsEnabled": {
            "type": "boolean"
          },
          "combinedEnabled": {
            "type": "boolean"
          },
          "xmltvEnabled": {
            "type": "boolean"
          },
          "xtreamEnabled": {
            "type": "boolean"
          },
          "hlsEnabled": {
            "type": "boolean"
          },
          "mpegtsEnabled": {
            "type": "boolean",
            "description": "Continuous MPEG-TS is used only for TS-compatible runtimes; fMP4 streams safely retain HLS."
          },
          "onDemandEnabled": {
            "type": "boolean",
            "description": "Allows this package to cold-start streams configured for on-demand playback. Already-running streams remain available when disabled."
          },
          "preferredFormat": {
            "enum": [
              "auto",
              "hls",
              "mpegts"
            ]
          },
          "serviceTitlePrefix": {
            "type": "boolean"
          }
        }
      },
      "PackageWrite": {
        "type": "object",
        "required": [
          "name",
          "durationValue",
          "durationUnit"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "publicDescription": {
            "type": "string"
          },
          "durationValue": {
            "type": "integer",
            "minimum": 1
          },
          "durationUnit": {
            "enum": [
              "hours",
              "days",
              "months",
              "years"
            ]
          },
          "price": {
            "type": "number",
            "minimum": 0
          },
          "creditCost": {
            "type": "number",
            "minimum": 0
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3
          },
          "maxConnections": {
            "type": "integer",
            "minimum": 0
          },
          "trafficLimitGb": {
            "type": "number",
            "minimum": 0,
            "description": "0 means unlimited. Reset on a new package period or renewal."
          },
          "isTrial": {
            "type": "boolean"
          },
          "gracePeriodHours": {
            "type": "integer",
            "minimum": 0
          },
          "collectionIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "resellerGroupIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Reseller groups allowed to sell this package."
          },
          "outputProfile": {
            "$ref": "#/components/schemas/OutputProfile"
          },
          "securityPolicy": {
            "$ref": "#/components/schemas/SecurityPolicy"
          }
        }
      },
      "SubscriberCreate": {
        "type": "object",
        "required": [
          "username",
          "packageId"
        ],
        "properties": {
          "username": {
            "type": "string"
          },
          "packageId": {
            "type": "string",
            "format": "uuid"
          },
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "phone": {
            "type": "string"
          },
          "ownerResellerId": {
            "type": "string",
            "format": "uuid"
          },
          "customerPortalUsername": {
            "type": "string"
          },
          "customerPortalPassword": {
            "type": "string",
            "minLength": 10
          }
        }
      },
      "SubscriberUpdate": {
        "type": "object",
        "properties": {
          "status": {
            "enum": [
              "active",
              "grace",
              "suspended",
              "expired",
              "banned",
              "archived"
            ]
          },
          "maxConnections": {
            "type": "integer",
            "minimum": 0
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "ownerResellerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "notes": {
            "type": "string"
          },
          "packageId": {
            "type": "string",
            "format": "uuid",
            "description": "Optional package change committed atomically with the line fields."
          },
          "packageReplace": {
            "type": "boolean",
            "description": "When true, start the package period now instead of extending a future expiry."
          }
        }
      },
      "PlaybackBlockRule": {
        "type": "object",
        "required": [
          "ruleType",
          "ruleValue"
        ],
        "properties": {
          "ruleType": {
            "enum": [
              "ip",
              "cidr",
              "asn",
              "isp",
              "user_agent"
            ]
          },
          "ruleValue": {
            "type": "string"
          },
          "status": {
            "enum": [
              "active",
              "disabled"
            ]
          },
          "reason": {
            "type": "string"
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "DeviceAdapterCreate": {
        "type": "object",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "enum": [
              "mag",
              "enigma2"
            ]
          },
          "label": {
            "type": "string"
          },
          "macAddress": {
            "type": "string"
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "required": [
          "id",
          "type",
          "createdAt",
          "subject"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "subject": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "data": {
            "type": "object"
          },
          "metadata": {
            "type": "object"
          }
        }
      },
      "StaffApiClientCreate": {
        "type": "object",
        "required": [
          "name",
          "scopes"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 160
          },
          "scopes": {
            "type": "array",
            "minItems": 1,
            "items": {
              "enum": [
                "access.read",
                "subscribers.manage",
                "packages.manage",
                "resellers.manage",
                "security.manage",
                "commerce.manage",
                "api_clients.manage",
                "*"
              ]
            }
          },
          "allowedCidrs": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rateLimitPerMinute": {
            "type": "integer",
            "minimum": 10,
            "maximum": 10000,
            "default": 600
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "ownerUserId": {
            "type": "integer",
            "description": "Defaults to the creating staff account."
          }
        }
      },
      "ResellerApiClientCreate": {
        "type": "object",
        "required": [
          "name",
          "scopes"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 160
          },
          "scopes": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string"
            },
            "description": "Permission names enabled on the owning reseller. Broader scopes are rejected."
          },
          "allowedCidrs": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rateLimitPerMinute": {
            "type": "integer",
            "minimum": 10,
            "maximum": 10000,
            "default": 600
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CustomerWrite": {
        "type": "object",
        "required": [
          "displayName"
        ],
        "properties": {
          "displayName": {
            "type": "string"
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "enum": [
              "active",
              "suspended",
              "archived"
            ]
          },
          "portalEnabled": {
            "type": "boolean"
          },
          "portalUsername": {
            "type": [
              "string",
              "null"
            ]
          },
          "portalPassword": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 10
          }
        }
      },
      "EpgSourceWrite": {
        "type": "object",
        "required": [
          "name",
          "url"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "description": "Public HTTPS XMLTV endpoint. Redirects, credentials in URLs, and private/reserved destinations are rejected."
          },
          "priority": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000,
            "default": 100
          },
          "refreshMinutes": {
            "type": "integer",
            "minimum": 15,
            "maximum": 10080,
            "default": 360
          },
          "status": {
            "enum": [
              "active",
              "disabled"
            ]
          }
        }
      }
    }
  },
  "paths": {
    "/api/access/openapi.json": {
      "get": {
        "tags": [
          "Staff"
        ],
        "summary": "Download this OpenAPI contract",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1 document"
          }
        }
      }
    },
    "/api/access/api-clients": {
      "get": {
        "tags": [
          "Staff"
        ],
        "summary": "List staff integration clients",
        "description": "Requires staff.manage and, for API-key callers, api_clients.manage.",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Clients without secret hashes"
          }
        }
      },
      "post": {
        "tags": [
          "Staff"
        ],
        "summary": "Create a scoped staff integration key",
        "description": "The complete key is returned exactly once.",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StaffApiClientCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created; save apiKey immediately"
          }
        }
      }
    },
    "/api/access/api-clients/{id}": {
      "delete": {
        "tags": [
          "Staff"
        ],
        "summary": "Revoke a staff integration key immediately",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked"
          }
        }
      }
    },
    "/api/access/overview": {
      "get": {
        "tags": [
          "Staff"
        ],
        "summary": "Access and sales overview",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Overview"
          }
        }
      }
    },
    "/api/access/entitlements/reconcile": {
      "post": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Recompile package entitlements and reconcile legacy viewer compatibility state",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Reconciliation result"
          }
        }
      }
    },
    "/api/access/resources": {
      "get": {
        "tags": [
          "Packages"
        ],
        "summary": "List selectable content resources",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Resources"
          }
        }
      }
    },
    "/api/access/resources/vod": {
      "get": {
        "tags": [
          "Packages"
        ],
        "summary": "Search selectable VOD titles, categories and tags",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ids",
            "in": "query",
            "description": "Comma-separated selected title UUIDs, up to 200.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 25 search results, selected titles, categories and tags."
          }
        }
      }
    },
    "/api/access/epg-sources": {
      "get": {
        "tags": [
          "EPG"
        ],
        "summary": "List managed XMLTV sources and refresh status",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Sources"
          }
        }
      },
      "post": {
        "tags": [
          "EPG"
        ],
        "summary": "Create a managed XMLTV source",
        "description": "The source is stored first and can then be refreshed explicitly or by the scheduler.",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EpgSourceWrite"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/access/epg-sources/{id}": {
      "put": {
        "tags": [
          "EPG"
        ],
        "summary": "Update a managed XMLTV source",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EpgSourceWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      },
      "delete": {
        "tags": [
          "EPG"
        ],
        "summary": "Disable a managed XMLTV source without deleting cached guide data",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Disabled"
          }
        }
      }
    },
    "/api/access/epg-sources/{id}/refresh": {
      "post": {
        "tags": [
          "EPG"
        ],
        "summary": "Fetch, validate, and atomically replace cached XMLTV data",
        "description": "Prior valid guide data remains available when a refresh fails.",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Refreshed"
          }
        }
      }
    },
    "/api/access/epg-matches": {
      "get": {
        "tags": [
          "EPG"
        ],
        "summary": "List authorized saved streams and conservative guide match status",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated rows; search, limit and offset supported"
          }
        }
      }
    },
    "/api/access/epg-matches/{id}/candidates": {
      "get": {
        "tags": [
          "EPG"
        ],
        "summary": "Search/rank guide candidates; suggestions never assign a match",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 50 candidates with guide source and availability"
          }
        }
      }
    },
    "/api/access/epg-matches/{id}": {
      "put": {
        "tags": [
          "EPG"
        ],
        "summary": "Pin a guide, restore automatic matching or disable EPG",
        "description": "Requires packages.manage and stream access. Body mode: auto/manual/disabled; manual also requires sourceId and channelId. Changes are audited; playback is unchanged.",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Persistent mapping and resolved status"
          }
        }
      }
    },
    "/api/access/epg-channels/{sourceId}/{channelId}/preview": {
      "get": {
        "tags": [
          "EPG"
        ],
        "summary": "Preview up to six current/upcoming guide programmes",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "sourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Active-source programme preview with UTC timestamps"
          }
        }
      }
    },
    "/api/access/collections": {
      "get": {
        "tags": [
          "Packages"
        ],
        "summary": "List content collections",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Collections"
          }
        }
      },
      "post": {
        "tags": [
          "Packages"
        ],
        "summary": "Create a dynamic or static content collection",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/access/collections/preview": {
      "post": {
        "tags": [
          "Packages"
        ],
        "summary": "Preview unsaved collection rules without publishing changes",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Effective draft content and inclusion/exclusion reasons"
          }
        }
      }
    },
    "/api/access/collections/{id}": {
      "put": {
        "tags": [
          "Packages"
        ],
        "summary": "Update collection metadata and ordered inclusion/exclusion rules",
        "description": "Membership is live for every subscriber whose assigned package version links this collection. A successful save queues affected subscriber entitlements for refresh, including removals and exclusions, without changing package terms, expiry or credentials. Publication rule snapshots are retained as history only. Refresh player playlists after propagation.",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Saved; affected subscriber access refresh queued"
          }
        }
      }
    },
    "/api/access/collections/{id}/preview": {
      "get": {
        "tags": [
          "Packages"
        ],
        "summary": "Preview effective collection membership and reasons",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Preview"
          }
        }
      }
    },
    "/api/access/packages": {
      "get": {
        "tags": [
          "Packages"
        ],
        "summary": "List current package versions",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Packages"
          }
        }
      },
      "post": {
        "tags": [
          "Packages"
        ],
        "summary": "Create and publish a package version",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PackageWrite"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/access/packages/preview": {
      "post": {
        "tags": [
          "Packages"
        ],
        "summary": "Preview a package draft before publishing",
        "description": "Resolves effective streams, dynamic event-slot selectors, exclusions, content health, policy prerequisites, changed fields, and subscribers that retain their existing immutable version.",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PackageWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Draft preview"
          }
        }
      }
    },
    "/api/access/packages/{id}": {
      "put": {
        "tags": [
          "Packages"
        ],
        "summary": "Publish a new immutable package version",
        "description": "Versions freeze commercial/output/security terms and the linked collection IDs. Membership inside those linked collections stays live. Publishing does not move existing subscribers to different terms or collection links; use renewal or an explicit apply-to-existing action.",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PackageWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Published"
          }
        }
      }
    },
    "/api/access/packages/{id}/versions": {
      "get": {
        "tags": [
          "Packages"
        ],
        "summary": "List immutable package version history and snapshots",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Version history"
          }
        }
      }
    },
    "/api/access/packages/{id}/health": {
      "get": {
        "tags": [
          "Packages"
        ],
        "summary": "Preview content and policy deployment health",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Health preview"
          }
        }
      }
    },
    "/api/access/packages/{id}/apply-current-version": {
      "post": {
        "tags": [
          "Packages"
        ],
        "summary": "Dry-run or apply the current version to existing subscriptions",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dry-run or committed result"
          }
        }
      }
    },
    "/api/access/subscribers": {
      "get": {
        "tags": [
          "Subscribers"
        ],
        "summary": "List access lines",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated access lines"
          }
        }
      },
      "post": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Create customer, line, and subscription atomically",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriberCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/access/subscribers-selection": {
      "get": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Resolve a filtered subscriber selection for safe cross-page bulk operations",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Selected line IDs and total"
          }
        }
      }
    },
    "/api/access/subscribers/bulk": {
      "post": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Dry-run or apply bulk status, package, expiry, or ownership changes",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Dry-run summary or committed result"
          }
        }
      }
    },
    "/api/access/customers": {
      "get": {
        "tags": [
          "Customers"
        ],
        "summary": "List customer records independently from playback lines",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated customers"
          }
        }
      },
      "post": {
        "tags": [
          "Customers"
        ],
        "summary": "Create a customer that may own multiple access lines",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerWrite"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/access/customers/{id}": {
      "get": {
        "tags": [
          "Customers"
        ],
        "summary": "Get a customer and owned lines",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Customer detail"
          }
        }
      },
      "patch": {
        "tags": [
          "Customers"
        ],
        "summary": "Update, suspend, or archive a customer record",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      }
    },
    "/api/access/subscribers/{id}": {
      "patch": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Atomically update line fields and optionally change its package",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriberUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated; expiry grace is preserved and affected sessions are closed"
          }
        }
      }
    },
    "/api/access/subscribers/{id}/package": {
      "post": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Assign or renew a versioned package",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription updated"
          }
        }
      }
    },
    "/api/access/subscribers/{id}/traffic/reset": {
      "post": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Reset the current traffic accounting period",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Usage reset"
          }
        }
      }
    },
    "/api/access/subscribers/{id}/output": {
      "get": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Get permanent M3U, XMLTV, and Xtream-compatible credentials",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Output URLs and credentials"
          }
        }
      }
    },
    "/api/access/subscribers/{id}/output/rotate": {
      "post": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Rotate output credentials and revoke prior access",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rotated"
          }
        }
      }
    },
    "/api/access/subscribers/{id}/custom-access": {
      "get": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Get per-line entitlement overrides layered over the package",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Overrides"
          }
        }
      },
      "put": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Replace per-line allow/deny overrides without mutating the package version",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      }
    },
    "/api/access/subscribers/{id}/devices": {
      "get": {
        "tags": [
          "Security"
        ],
        "summary": "List registered player devices and security state",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Devices"
          }
        }
      }
    },
    "/api/access/subscribers/{id}/devices/{deviceId}": {
      "patch": {
        "tags": [
          "Security"
        ],
        "summary": "Trust or revoke a player device; revocation closes matching sessions",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "deviceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      }
    },
    "/api/access/subscribers/{id}/session-history": {
      "get": {
        "tags": [
          "Sessions"
        ],
        "summary": "List delivery, traffic, startup, stall, and recovery history for a line",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Session history and QoE"
          }
        }
      }
    },
    "/api/access/subscribers/{id}/adapters": {
      "get": {
        "tags": [
          "Adapters"
        ],
        "summary": "List MAG/Stalker and Enigma2 adapters",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Adapters"
          }
        }
      },
      "post": {
        "tags": [
          "Adapters"
        ],
        "summary": "Create an independently revocable adapter credential",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceAdapterCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created; secret is returned once"
          }
        }
      }
    },
    "/api/access/subscribers/{id}/adapters/{adapterId}/rotate": {
      "post": {
        "tags": [
          "Adapters"
        ],
        "summary": "Rotate an Enigma2 adapter secret",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "adapterId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rotated; secret is returned once"
          }
        }
      }
    },
    "/api/access/subscribers/{id}/adapters/{adapterId}": {
      "patch": {
        "tags": [
          "Adapters"
        ],
        "summary": "Activate or revoke an isolated device adapter",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "adapterId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      }
    },
    "/api/access/sessions": {
      "get": {
        "tags": [
          "Sessions"
        ],
        "summary": "Page observed streams within active playback leases",
        "description": "One row per stream observed within 45 seconds per lease, or one idle/last-reported row. Session traffic is shared across stream rows. Termination revokes the whole lease.",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 250,
              "default": 25
            }
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 10000000
            }
          },
          {
            "in": "query",
            "name": "search",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "in": "query",
            "name": "owner",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "in": "query",
            "name": "service",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "in": "query",
            "name": "server",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "in": "query",
            "name": "lineId",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "in": "query",
            "name": "type",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "event",
                "vod",
                "recording",
                "awaiting"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated sessions, total matching stream rows, limit, offset and activityWindowMs"
          }
        }
      }
    },
    "/api/access/sessions/{id}": {
      "delete": {
        "tags": [
          "Sessions"
        ],
        "summary": "Kill a live playback session and propagate revocation to workers",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Session revoked"
          }
        }
      }
    },
    "/api/access/playback-blocklist": {
      "get": {
        "tags": [
          "Security"
        ],
        "summary": "List IP, CIDR, ASN, ISP, and user-agent rules",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Rules"
          }
        }
      },
      "post": {
        "tags": [
          "Security"
        ],
        "summary": "Create a global playback block rule",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlaybackBlockRule"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/access/playback-blocklist/{id}": {
      "patch": {
        "tags": [
          "Security"
        ],
        "summary": "Update a global playback block rule",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      },
      "delete": {
        "tags": [
          "Security"
        ],
        "summary": "Disable a global playback block rule",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Disabled"
          }
        }
      }
    },
    "/api/access/security-alerts": {
      "get": {
        "tags": [
          "Security"
        ],
        "summary": "List shared-account anomaly alerts",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Alerts"
          }
        }
      }
    },
    "/api/access/security-alerts/{id}": {
      "patch": {
        "tags": [
          "Security"
        ],
        "summary": "Acknowledge or resolve a security alert",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      }
    },
    "/api/access/security-alerts/analyze": {
      "post": {
        "tags": [
          "Security"
        ],
        "summary": "Run shared-account anomaly analysis immediately",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Analysis result"
          }
        }
      }
    },
    "/api/access/resellers": {
      "get": {
        "tags": [
          "Resellers"
        ],
        "summary": "List reseller hierarchy, limits, balance, and line counts",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Resellers"
          }
        }
      },
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Create an isolated reseller account with assigned packages and permissions",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/access/reseller-groups": {
      "get": {
        "tags": [
          "Resellers"
        ],
        "summary": "List reusable reseller permission groups and their package availability",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Groups"
          }
        }
      },
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Create a reseller group with explicit reseller and package membership",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/access/reseller-groups/{id}": {
      "put": {
        "tags": [
          "Resellers"
        ],
        "summary": "Update reseller group membership and package availability",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      }
    },
    "/api/access/resellers/{id}": {
      "patch": {
        "tags": [
          "Resellers"
        ],
        "summary": "Update reseller status, hierarchy, packages, limits, and permissions",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      }
    },
    "/api/access/resellers/{id}/branding": {
      "get": {
        "tags": [
          "Resellers"
        ],
        "summary": "Get white-label branding and domain status",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Branding"
          }
        }
      },
      "put": {
        "tags": [
          "Resellers"
        ],
        "summary": "Configure branding and begin custom-domain verification",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Branding and optional one-time DNS verification value"
          }
        }
      }
    },
    "/api/access/resellers/{id}/branding/verify-domain": {
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Verify the reseller custom-domain DNS challenge",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verified"
          }
        }
      }
    },
    "/api/access/resellers/{id}/api-clients": {
      "get": {
        "tags": [
          "Resellers"
        ],
        "summary": "List independently revocable reseller integration clients",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Clients"
          }
        }
      },
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Create a reseller integration key; the complete key is returned once",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResellerApiClientCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/access/resellers/{id}/api-clients/{clientId}": {
      "delete": {
        "tags": [
          "Resellers"
        ],
        "summary": "Revoke a reseller integration key immediately",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked"
          }
        }
      }
    },
    "/api/access/resellers/{id}/credits": {
      "post": {
        "tags": [
          "Commerce"
        ],
        "summary": "Append an idempotent manual credit adjustment to a reseller wallet",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Unique client-generated key. Reusing it for a different mutation is rejected.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 160
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ledger entry"
          }
        }
      }
    },
    "/api/access/ledger": {
      "get": {
        "tags": [
          "Commerce"
        ],
        "summary": "List the immutable reseller credit ledger",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Ledger entries"
          }
        }
      }
    },
    "/api/access/ledger/{id}/reverse": {
      "post": {
        "tags": [
          "Commerce"
        ],
        "summary": "Append an idempotent compensating refund or reversal without mutating the original ledger entry",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Unique client-generated key. Reusing it for a different mutation is rejected.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 160
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Compensating ledger entry"
          }
        }
      }
    },
    "/api/access/commerce": {
      "get": {
        "tags": [
          "Commerce"
        ],
        "summary": "List payment events and provisioning outcomes",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Commerce status"
          }
        }
      }
    },
    "/api/access/commerce/webhook-secret/rotate": {
      "post": {
        "tags": [
          "Commerce"
        ],
        "summary": "Rotate the inbound payment-webhook signing secret",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "New secret returned once"
          }
        }
      }
    },
    "/api/access/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List outbound webhook endpoints and deliveries",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoints and deliveries"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a signed outbound webhook endpoint",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created; signing secret returned once"
          }
        }
      }
    },
    "/api/access/webhooks/{id}": {
      "put": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Update an outbound webhook endpoint",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Disable an outbound webhook endpoint",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Disabled"
          }
        }
      }
    },
    "/api/access/webhooks/deliveries/{id}/retry": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Retry one failed webhook delivery",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Retry result"
          }
        }
      }
    },
    "/api/access/audit": {
      "get": {
        "tags": [
          "Staff"
        ],
        "summary": "List immutable commercial and security mutation audit events",
        "security": [
          {
            "staffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Audit events"
          }
        }
      }
    },
    "/api/reseller/login": {
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Start reseller authentication (may return a 2FA challenge)",
        "responses": {
          "200": {
            "description": "JWT or second-factor challenge"
          }
        }
      }
    },
    "/api/reseller/login/2fa": {
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Complete reseller two-factor authentication",
        "responses": {
          "200": {
            "description": "Reseller JWT"
          }
        }
      }
    },
    "/api/reseller/2fa/status": {
      "get": {
        "tags": [
          "Resellers"
        ],
        "summary": "Read reseller two-factor status",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "2FA status"
          }
        }
      }
    },
    "/api/reseller/2fa/setup": {
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Create a pending reseller TOTP enrollment",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Pending secret and provisioning URI"
          }
        }
      }
    },
    "/api/reseller/2fa/enable": {
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Confirm and enable reseller TOTP",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Enabled with one-time recovery codes"
          }
        }
      }
    },
    "/api/reseller/2fa/disable": {
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Disable reseller TOTP after credential verification",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Disabled"
          }
        }
      }
    },
    "/api/reseller/me": {
      "get": {
        "tags": [
          "Resellers"
        ],
        "summary": "Get reseller permissions, wallet, sales, renewal, and active-line overview",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Profile and overview"
          }
        }
      }
    },
    "/api/reseller/packages": {
      "get": {
        "tags": [
          "Resellers"
        ],
        "summary": "List packages assigned to this reseller",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Packages"
          }
        }
      }
    },
    "/api/reseller/api-clients": {
      "get": {
        "tags": [
          "Resellers"
        ],
        "summary": "List this reseller account’s integration clients",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Clients"
          }
        }
      },
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Create a scoped integration key; the complete key is returned once",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResellerApiClientCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/reseller/api-clients/{id}": {
      "delete": {
        "tags": [
          "Resellers"
        ],
        "summary": "Revoke this reseller’s integration key",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked"
          }
        }
      }
    },
    "/api/reseller/customers": {
      "get": {
        "tags": [
          "Customers"
        ],
        "summary": "List owned customer records across the permitted hierarchy",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Customers"
          }
        }
      },
      "post": {
        "tags": [
          "Customers"
        ],
        "summary": "Create a customer record independently of an access line",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerWrite"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/reseller/customers/{id}": {
      "patch": {
        "tags": [
          "Customers"
        ],
        "summary": "Update a customer owned by this reseller hierarchy",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      }
    },
    "/api/reseller/subscribers": {
      "get": {
        "tags": [
          "Resellers"
        ],
        "summary": "List reseller-owned access lines",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Subscribers"
          }
        }
      },
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Purchase an assigned package using the append-only credit ledger",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Unique client-generated key. Reusing it for a different mutation is rejected.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 160
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriberCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created atomically"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}/renew": {
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Renew a reseller-owned line atomically",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Unique client-generated key. Reusing it for a different mutation is rejected.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 160
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Renewed"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}/change-package": {
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Replace a line subscription and charge the assigned package atomically",
        "description": "The operation resets the subscription period and traffic allowance and revokes active playback sessions.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Unique client-generated key. Reusing it for a different mutation is rejected.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 160
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Package changed"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}": {
      "patch": {
        "tags": [
          "Resellers"
        ],
        "summary": "Suspend or reactivate a reseller-owned line",
        "description": "Expired lines cannot be reactivated until renewed.",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}/output": {
      "get": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Get permanent output URLs for an owned line",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Outputs"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}/output/rotate": {
      "post": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Rotate an owned line’s playback credentials",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rotated"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}/session-history": {
      "get": {
        "tags": [
          "Sessions"
        ],
        "summary": "List recent delivery and QoE history for an owned line",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Session history"
          }
        }
      }
    },
    "/api/reseller/sessions": {
      "get": {
        "tags": [
          "Sessions"
        ],
        "summary": "List active sessions across the permitted reseller hierarchy",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Active sessions"
          }
        }
      }
    },
    "/api/reseller/sessions/{id}": {
      "delete": {
        "tags": [
          "Sessions"
        ],
        "summary": "Disconnect an active session owned by this reseller hierarchy",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Terminated"
          }
        }
      }
    },
    "/api/reseller/ledger": {
      "get": {
        "tags": [
          "Commerce"
        ],
        "summary": "List this reseller’s append-only wallet ledger",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Ledger entries"
          }
        }
      }
    },
    "/api/reseller/subresellers": {
      "get": {
        "tags": [
          "Resellers"
        ],
        "summary": "List direct sub-resellers",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Sub-resellers"
          }
        }
      },
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Create a sub-reseller and atomically transfer initial credits",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created"
          }
        }
      }
    },
    "/api/reseller/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List reseller-owned endpoints and deliveries",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoints and deliveries"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a reseller-owned signed webhook endpoint",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created; signing secret returned once"
          }
        }
      }
    },
    "/api/reseller/webhooks/{id}": {
      "put": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Update a reseller-owned webhook endpoint",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Disable a reseller-owned webhook endpoint",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Disabled"
          }
        }
      }
    },
    "/api/reseller/webhooks/deliveries/{id}/retry": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Retry one reseller-owned failed delivery",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Retry result"
          }
        }
      }
    },
    "/api/reseller/branding": {
      "get": {
        "tags": [
          "Resellers"
        ],
        "summary": "Get reseller branding",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Branding"
          }
        }
      },
      "put": {
        "tags": [
          "Resellers"
        ],
        "summary": "Configure reseller branding",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      }
    },
    "/api/reseller/branding/verify-domain": {
      "post": {
        "tags": [
          "Resellers"
        ],
        "summary": "Verify the configured white-label domain using its DNS challenge",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Verification status"
          }
        }
      }
    },
    "/api/reseller/security-alerts": {
      "get": {
        "tags": [
          "Security"
        ],
        "summary": "List security alerts for reseller-owned lines",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Alerts"
          }
        }
      }
    },
    "/api/reseller/security-alerts/{id}": {
      "patch": {
        "tags": [
          "Security"
        ],
        "summary": "Acknowledge or resolve an owned-line security alert",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}/custom-access": {
      "get": {
        "tags": [
          "Subscribers"
        ],
        "summary": "Inspect opaque custom-access rules for an owned line without provider credentials",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Custom access rule identifiers"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}/devices": {
      "get": {
        "tags": [
          "Security"
        ],
        "summary": "List registered devices for an owned line",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Devices"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}/devices/{deviceId}": {
      "patch": {
        "tags": [
          "Security"
        ],
        "summary": "Approve, block, or revoke an owned-line device",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "deviceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated device"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}/adapters": {
      "get": {
        "tags": [
          "Adapters"
        ],
        "summary": "List MAG and Enigma adapters for an owned line",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Adapters"
          }
        }
      },
      "post": {
        "tags": [
          "Adapters"
        ],
        "summary": "Create a MAG or Enigma adapter for an owned line",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created; Enigma secret is returned once"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}/adapters/{adapterId}": {
      "patch": {
        "tags": [
          "Adapters"
        ],
        "summary": "Enable, revoke, or archive an owned-line adapter",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "adapterId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated adapter"
          }
        }
      }
    },
    "/api/reseller/subscribers/{id}/adapters/{adapterId}/rotate": {
      "post": {
        "tags": [
          "Adapters"
        ],
        "summary": "Rotate an owned Enigma adapter secret",
        "security": [
          {
            "resellerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "adapterId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rotated; new secret is returned once"
          }
        }
      }
    },
    "/api/customer/login": {
      "post": {
        "tags": [
          "Customers"
        ],
        "summary": "Authenticate an opt-in customer portal account",
        "responses": {
          "200": {
            "description": "Customer JWT"
          }
        }
      }
    },
    "/api/customer/me": {
      "get": {
        "tags": [
          "Customers"
        ],
        "summary": "List customer subscriptions, traffic, and expiry without infrastructure details",
        "security": [
          {
            "customerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Customer and lines"
          }
        }
      }
    },
    "/api/customer/lines/{id}/output": {
      "get": {
        "tags": [
          "Customers"
        ],
        "summary": "Get the customer-owned line output URLs",
        "security": [
          {
            "customerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Outputs"
          }
        }
      }
    },
    "/api/customer/lines/{id}/output/rotate": {
      "post": {
        "tags": [
          "Customers"
        ],
        "summary": "Rotate customer-owned playback credentials",
        "security": [
          {
            "customerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rotated"
          }
        }
      }
    },
    "/api/customer/lines/{id}/activity": {
      "get": {
        "tags": [
          "Sessions"
        ],
        "summary": "List active and historical sessions for a customer-owned line",
        "security": [
          {
            "customerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Playback activity"
          }
        }
      }
    },
    "/api/customer/lines/{lineId}/sessions/{sessionId}": {
      "delete": {
        "tags": [
          "Sessions"
        ],
        "summary": "Disconnect one customer-owned playback session",
        "security": [
          {
            "customerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "lineId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Terminated"
          }
        }
      }
    },
    "/stalker_portal/server/load.php": {
      "get": {
        "tags": [
          "Adapters"
        ],
        "summary": "Stalker-compatible MAG portal RPC endpoint",
        "description": "Handshake authenticates the registered MAC; subsequent calls use the returned bearer token.",
        "responses": {
          "200": {
            "description": "Portal response"
          },
          "401": {
            "description": "Unregistered or revoked adapter"
          }
        }
      }
    },
    "/enigma2/{adapterId}/bouquet.tv": {
      "get": {
        "tags": [
          "Adapters"
        ],
        "summary": "Download a permanent Enigma2 bouquet",
        "parameters": [
          {
            "name": "adapterId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "key",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Enigma2 userbouquet"
          }
        }
      }
    },
    "/enigma2/{adapterId}/live/{streamId}": {
      "get": {
        "tags": [
          "Adapters"
        ],
        "summary": "Authorize an Enigma2 channel tune and redirect to a fresh protected stream URL",
        "parameters": [
          {
            "name": "adapterId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "streamId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "key",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Fresh protected playback URL"
          },
          "401": {
            "description": "Invalid or revoked adapter"
          },
          "404": {
            "description": "Stream is not entitled"
          }
        }
      }
    },
    "/api/access-commerce/{provider}/webhook": {
      "post": {
        "tags": [
          "Commerce"
        ],
        "summary": "Provision an exactly paid package",
        "description": "x-xott-signature is sha256=<HMAC-SHA256 of the exact request bytes>. Only final payment.succeeded events with exact product, amount, currency, and a unique provider transaction ID are accepted.",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-xott-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Idempotent result"
          },
          "400": {
            "description": "Payment rejected"
          },
          "401": {
            "description": "Invalid signature"
          }
        }
      }
    }
  },
  "x-xott-outbound-webhooks": {
    "signature": "sha256=HMAC-SHA256(secret, x-xott-timestamp + \".\" + exact_request_body)",
    "headers": [
      "x-xott-event",
      "x-xott-delivery",
      "x-xott-timestamp",
      "x-xott-signature"
    ],
    "retryPolicy": "Up to 8 attempts with bounded exponential backoff; HTTP redirects and non-public destinations are rejected."
  },
  "x-xott-playback-accounting": {
    "workerCounters": "Monotonic per node, playback session, stream, and process epoch; controller application is idempotent.",
    "quotaEnforcement": "Checked at authorization/playlist refresh. A small bounded overshoot may occur between media delivery and the next refresh."
  },
  "x-xott-staff-api-scopes": {
    "access.read": "Overview, audit, and generic access-plane reads.",
    "subscribers.manage": "Customers, access lines, devices, sessions, outputs, and subscriber bulk operations.",
    "packages.manage": "Content collections, package versions, resource previews, and entitlement reconciliation.",
    "resellers.manage": "Resellers, branding, reseller API clients, wallets, and credit ledger.",
    "security.manage": "Playback blocklists and anomaly alerts.",
    "commerce.manage": "Payment status, inbound secret rotation, and outbound webhook management.",
    "api_clients.manage": "Staff integration client creation and revocation; the owning role must also retain staff.manage."
  }
}
