openapi: 3.1.0
info:
  title: Zelf APIs
  version: 1.3.0
  description: |-
    The Zelf API provides programmatic access to Zelf's creator and content intelligence. Use it to search the content and creators tracked for your brand, run AI analyses over that content, vet individual creators, and subscribe to webhooks that deliver results as they are produced.

    Your API key reaches your own brand and every brand linked to it, sub-brands included; these are your linked brands, `brands.list` returns exactly that set, and any `brand_id` parameter accepts any brand in it.

    The content and AI analysis endpoints also accept a `brand_id` from `brands.catalog`, the catalog of brands Zelf curates for competitive benchmarking. Those brands are readable by every key; the brand write endpoints accept only your linked brands.

    This reference is organized by resource. A resource is a type of object in Zelf - such as a piece of content, a creator, or an analysis - and each resource is represented as a JSON object. For each one, the reference lists its fields and the methods it supports (`LIST`, `GET`, `POST`, and so on).

    ## Glossary

    - **Brand** - the workspace an API key represents. A brand owns the content, creators, and analyses you access through the API.
    - **Workspace brand** - the brand an API key is issued for. It is the brand every endpoint acts on when `brand_id` is omitted, and the brand new sub-brands are created under.
    - **Sub-brand** - a brand nested under a parent brand, used to track a competitor (`COMPETITIVE`) or a content theme (`THEME`) separately.
    - **Content** - a single social post (a video, image, or text post) from any platform Zelf covers.
    - **Creator** - a social account, identified by its platform and username.
    - **Tracked / influencer account** - creator accounts a brand follows: `tracked` identifies accounts the brand monitors, and `influencer` identifies potential partnership candidates.
    - **AI analysis** - an AI run over the content selected by `content_filters` (or its comments) for a `topic`. An analysis is divided into one or more runs.
    - **Creator vetting** - an AI run that retrieves a creator's profile and scores its safety and brand fit.
    - **Run** - a single unit of work within an analysis or vetting, each with its own `id` and `status` (`IN_PROGRESS`, `COMPLETED`, `EMPTY_RESULT`, `FAILED`).

    ## Authentication

    Every request must be authenticated with an API key issued for your brand. Supply the key in the `x-api-key` request header. A request without a valid key returns `INVALID_API_KEY`.

    All responses are returned as `application/json`; the `Accept` header is not required.

    ## Client SDKs

    Official clients are published for six languages. Each exposes the same resources and method names as this reference, so `content.list` is `client.content.list()` in every one of them.

    | Language | Install | Import |
    | --- | --- | --- |
    | Python | `pip install zelf-sdk` | `from zelf import ZelfAPIClient` |
    | TypeScript | `npm install zelf-sdk` | `import { ZelfAPIClient } from 'zelf-sdk'` |
    | Go | `go get github.com/hellozelf/zelf-sdk/go` | `import zelf "github.com/hellozelf/zelf-sdk/go"` |
    | Rust | `cargo add zelf-sdk` | `use zelf::Client` |
    | Ruby | `gem install zelf-sdk` | `require 'zelf'` |
    | C# | `dotnet add package Zelf.Sdk` | `using Zelf` |

    A client reads the key from `ZELF_API_KEY` and the base URL from `ZELF_BASE_URL` when neither is passed. It walks pagination, retries safe requests, verifies webhook signatures, and raises a typed error carrying the `log_id`.

    Source and per-language documentation: [github.com/hellozelf/zelf-sdk](https://github.com/hellozelf/zelf-sdk)

    ## Response format

    Every successful response is a JSON object that uses the same envelope:

    ```json
    {
      "log_id": "<identifier for this request, useful in support tickets>",
      "timestamp": "<UTC ISO-8601>",
      "results": [ ... ],
      "item_count": <number of items on this page>,
      "page": <current page>,
      "next": <next page number, or null>,
      "previous": <previous page number, or null>
    }
    ```

    The `results` field is always an array. List methods return the matching items, and methods that address a single resource return that resource as the only element of the array, with `item_count: 1`.

    The remaining fields describe the current page. When more results are available, `next` holds the number of the following page; pass it as the `page` query parameter to retrieve it. `previous` works the same way for the preceding page. Each is `null` when there is no page in that direction.

    ## Errors

    When a request cannot be completed, the API returns the appropriate HTTP status code and a JSON body containing an `error` object:

    ```json
    {
      "log_id": "...",
      "timestamp": "...",
      "error": {
        "code": "BRAND_ID_REQUIRED",
        "message": "...",
        "details": { ... }
      }
    }
    ```

    The `error.code` field is a constant your application can test to handle each condition; `message` is a description of the problem, and `details` provides condition-specific context. The following codes are returned:

    | Code | HTTP | Meaning |
    | --- | --- | --- |
    | `INVALID_API_KEY` | 401 | The `x-api-key` header is missing, malformed, or doesn't match a known key. |
    | `EXPIRED_API_KEY` | 401 | The key was valid but its expiration date has passed. Issue a new key. |
    | `REVOKED_API_KEY` | 401 | The key was explicitly revoked. Issue a new key. |
    | `INVALID_ID` | 400 | A path or query id (`brand_id`, `creator_id`, content id) is not a valid identifier. |
    | `NOT_FOUND` | 404 | The resource does not exist or is outside this api key's scope. |
    | `VALIDATION_ERROR` | 400 | A query parameter has the wrong type, an unknown sort field, or otherwise failed validation. `details` lists the offending fields. |
    | `BRAND_ID_REQUIRED` | 400 | A brand-scoped filter or sort was used without supplying `brand_id`. `details.blocked_by` names the parameters that triggered it. |
    | `PERMISSION_DENIED` | 403 | The api key cannot access the requested brand or resource. |
    | `RATE_LIMIT_EXCEEDED` | 429 | A quota limit was exceeded. `details.scopes` names the group(s) that were hit and `details.retry_after_seconds` is how long to wait before retrying. |
    | `NOT_ACCEPTABLE` | 406 | The requested response media type cannot be served (the API only emits `application/json`). |
    | `SERVICE_UNAVAILABLE` | 503 | The request took too long and was stopped. Narrow the date window or pass `brand_id`, then retry. |
    | `INTERNAL_ERROR` | 500 | Something went wrong on our side. Include `log_id` when filing a support ticket. |

    ## Sorting

    List methods that support sorting accept an `order_by` parameter. A field name on its own sorts results in descending order (largest or most recent first); prefixing it with a hyphen (`-`) sorts in ascending order. For example, `order_by=view_count` returns the most-viewed items first, and `order_by=-view_count` returns the least-viewed first. To sort by several fields, supply a comma-separated list, such as `order_by=view_count,-posted_at`. Each method documents the fields it accepts; an unsupported field returns `VALIDATION_ERROR`.

    ## Rate limits

    Requests are subject to quotas applied per API key and per brand, grouped as shown below. When a quota is exceeded, the API returns `RATE_LIMIT_EXCEEDED`; the response's `details.retry_after_seconds` indicates how long to wait before retrying, and `details.scopes` identifies the quota groups that were exceeded. The exact limit for each group is available in the dashboard.

    **Requests made with an API key**

    | Group | Scope (`details.scopes`) |
    | --- | --- |
    | General | `global` |
    | AI Analysis | `ai_analysis` |
    | Data Records | `data_records` |

    **Requests an AI client makes**

    | Group | Scope (`details.scopes`) |
    | --- | --- |
    | General | `mcp` |
    | AI Analysis | `mcp.ai_analysis` |
    | Data Records | `mcp.data_records` |

    ## Testing

    A sandbox answers every endpoint from fixed data, so you can build and test a client without a key and without touching production. The key below is public.

    ```bash
    export ZELF_BASE_URL=https://sandbox-api.hellozelf.com
    export ZELF_API_KEY=sk_zelf_sandbox_demo
    ```

    It returns the shapes, status codes, error codes and pagination that production returns, and `page` and `page_size` work. Every other query parameter is accepted and ignored, so a list returns the same rows whatever you filter on, nothing is validated, and writes persist nothing. That is enough to exercise request building, serialization, paging, authentication and retries.

    Anything that depends on a real filter, a real id or a real result has to be checked against production with your own key.

    ## Webhooks

    Zelf can send your data to you on a schedule. You register an endpoint once and we post to it, so nothing has to call the API to keep a warehouse, a database or a dashboard up to date.

    **Scheduled export.** Choose how often you want the data: every 4, 6, 12 or 24 hours, weekly, or every 30 days. At each run we build the file and post a notification to your endpoint with a signed link to download it. The file is NDJSON, one JSON object per line, which loads into Snowflake, BigQuery, Postgres or a data frame without conversion.

    **Row limit.** One delivery carries up to 100,000 rows. You can set a lower limit per subscription. If more rows match than the limit allows, the file stops at the limit and the next run picks up what is current then. There is no byte limit, and the notification carries the exact size and a checksum so you can confirm the download is complete.

    **What you filter.** A subscription carries the same filters as the read endpoints, so a delivery is scoped to one brand, one platform, a date window, or whatever else you would have passed to `content.list`.

    **Where it goes.** Deliveries go to an HTTPS endpoint you control. We do not post to email, nor write into a database or warehouse for you; your endpoint receives the link and loads the file wherever it belongs.

    Use the read endpoints for interactive lookups and for the question you are answering right now. Use a webhook for anything regular and for anything large: the same pull over `content.list` is thousands of requests against your rate limit.

    Two categories are supported:

    - **Snapshots** (`content.snapshot`, `creator.snapshot`) are delivered on a schedule and carry a signed, time-limited link to an NDJSON file containing the matching content or creators.
    - **Events** (`creator_vetting.completed`, `ai_analysis.completed`) are delivered when a run reaches a terminal state and carry a lightweight notification; retrieve the full result from the corresponding results operation (`ai_analysis.results` or `creator_vetting.results`).

    Every delivery shares a common envelope (`id`, `type`, `subscription_id`, `delivered_at`, `livemode`, `resource`, `delivery_mode`, `data`). Respond with any `2xx` status code to acknowledge receipt; a non-2xx response or a timeout is retried with exponential backoff. The **Webhooks** section documents each payload and the structure of the NDJSON files.

    ### Snapshot limits

    A snapshot carries at most 100,000 items. A subscription may set a lower `max_items`; leaving it empty or `0` applies that ceiling. Items count against the brand's data-record item quota, so a snapshot is cut to the remaining allowance, and a delivery is skipped and marked quota exhausted when none is left. When more items match than the limit allows, the file stops at the limit and the next scheduled snapshot carries whatever is current then. A test delivery always sends a sample of 10 items.

    ### The file

    There is no byte limit; size follows the item limit. The file is NDJSON, one JSON object per line, served as `application/x-ndjson` and not compressed. The notification carries the exact byte size and a SHA-256 checksum, so you can verify a download is complete before you read it.

    The signed download link is valid for 24 hours from delivery, and `expires_at` (Unix seconds) carries the exact moment. The file is kept for 7 days and then deleted with its delivery record. A link cannot be reissued, so download within the window.

    ### Schedule and retries

    Snapshots run every 4, 6, 12, 24, 168 (weekly) or 720 hours (30 days), defaulting to 12 hours. The first one arrives within minutes of creating the subscription.

    Any response other than `2xx` counts as a failure, including `3xx`, because redirects are not followed, as do timeouts and connection errors. The timeouts are 10 seconds to connect and 30 seconds to read, so answer `2xx` immediately and download the file afterwards rather than while we wait.

    A delivery is attempted 5 times in total. Retries start about 60 seconds apart and double with jitter, up to 6 hours between tries. Each retry rebuilds the snapshot from current data and carries a new signed link under the same delivery id. After the last failure the delivery is marked exhausted and the next scheduled snapshot runs as usual. Items are charged to the quota only when a delivery succeeds, so a retry is never billed twice.

    ## Models

    The **Models** section describes the JSON representations the API returns and delivers through webhooks. Every list method wraps its items in the response envelope described above.
paths:
  /api/v1/ai_analysis/:
    post:
      operationId: ai_analysis.create
      description: |-
        `ai_analysis.create`

        Starts an AI analysis over the content matched by `content_filters` (or its comments) for a given `topic`. Runs complete asynchronously; this method returns immediately with the created analysis and its initial runs.

        The analysis runs over the highest-ranked matching content by `content_filters.order_by`, up to 500 posts or the limit configured for your workspace. When `order_by` is omitted, content is ranked by `earned_media_value_cents` descending.

        The created analysis is returned as the single item in `results`. Take its `id` and poll `ai_analysis.list` until a run reports `COMPLETED`. An analysis can take an hour or more, so poll at a moderate interval; prefer the `ai_analysis.completed` webhook over frequent polling, which can trigger `RATE_LIMIT_EXCEEDED`. Once complete, retrieve the payload from `ai_analysis.results`, passing `analysis_id=<id>`.
      summary: Create AI Analysis
      tags:
      - AI Analysis
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AiAnalysisCreateRequest'
            examples:
              Example:
                value:
                  name: Zelf
                  topic: Zelf
                  analysis_scope:
                  - CONTENT
                  content_filters:
                    view_count_min: 42
                    view_count_max: 42
                    comment_count_min: 42
                    comment_count_max: 42
                    like_count_min: 42
                    like_count_max: 42
                    share_count_min: 42
                    share_count_max: 42
                    total_engagement_min: 42
                    total_engagement_max: 42
                    earned_media_value_cents_min: 42
                    earned_media_value_cents_max: 42
                    media_duration_min: 4.2
                    media_duration_max: 4.2
                    posted_after: '2025-01-01T00:00:00Z'
                    posted_before: '2025-01-01T00:00:00Z'
                    platform:
                    - instagram
                    language:
                    - en
                    content_form: VIDEO
                    search: zelfai
                    creator_id: cr_pfqPv0FLsBRimHfi
                    brand_id: brd_Lw6ZZmNYhW0P9k0x
                    is_owned: true
                    is_tagged: true
                    is_influencer: true
                    external_id: '7376543210987654321'
                    creator_username: zelfai
                    mention_type:
                    - content
                    mention_type_not:
                    - comment
                    creator_follower_count_min: 42
                    creator_follower_count_max: 42
                    engagement_rate_min: 4.2
                    engagement_rate_max: 4.2
                    net_promoter_score_min: 42
                    net_promoter_score_max: 42
                    posted_within_days: 42
                    posted_within_hours: 42
                    order_by:
                    - view_count
                    - -posted_at
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/AiAnalysisCreateRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/AiAnalysisCreateRequest'
        required: true
      security:
      - ApiKey: []
      responses:
        '201':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/AiAnalysis'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: bod_huNssMQX4FTvALEY
                      analysis_type: AI_ANALYSIS
                      name: Zelf
                      topic: Zelf
                      created_at: '2026-05-12T14:30:00Z'
                      modified_at: '2026-05-12T15:00:00Z'
                      analyses:
                      - id: ban_xJZFbcWNkF9M3brf
                        scope: CONTENT
                        status: COMPLETED
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    get:
      operationId: ai_analysis.list
      description: |-
        `ai_analysis.list`

        Returns the AI analyses that have been created, ordered newest first, each with its runs and their current `status`. It lists the analyses available and tracks run progress without retrieving the full result payload.

        An analysis `id` maps to `analysis_id` on `ai_analysis.results` and `ai_analysis.contents`, and each run's `id` maps to `id` on `ai_analysis.results`. The run `result` download is not included in this response; retrieve it from `ai_analysis.results` once a run reports `COMPLETED` (statuses: `IN_PROGRESS`, `COMPLETED`, `EMPTY_RESULT`, `FAILED`). Supply `id` to return a single analysis.
      summary: List AI Analyses
      parameters:
      - in: query
        name: id
        schema:
          type: string
        description: Optional analysis id. When supplied, only that analysis is returned.
          Returns `INVALID_ID` if malformed.
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      tags:
      - AI Analysis
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedAiAnalysisList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: bod_huNssMQX4FTvALEY
                      analysis_type: AI_ANALYSIS
                      name: Zelf
                      topic: Zelf
                      created_at: '2026-05-12T14:30:00Z'
                      modified_at: '2026-05-12T15:00:00Z'
                      analyses:
                      - id: ban_xJZFbcWNkF9M3brf
                        scope: CONTENT
                        status: COMPLETED
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/ai_analysis/results/:
    get:
      operationId: ai_analysis.results
      description: |-
        `ai_analysis.results`

        Returns each run's `result`, a signed, time-limited download of the analysis payload. Call this method after a run reaches `COMPLETED`; for earlier statuses the row is returned with a `null` `result`.

        Supply `analysis_id` (the analysis `id` from `ai_analysis.list`) to return every run of that analysis, or `id` (a single run's `id`) to return one run. Exactly one is required. A malformed id returns `INVALID_ID`; omitting both returns `VALIDATION_ERROR`.
      summary: Get AI Analysis Results
      parameters:
      - in: query
        name: analysis_id
        schema:
          type: string
        description: Analysis id (from `ai_analysis.list`). Returns every run of that
          analysis. Exactly one of `analysis_id` and `id` is required, and a request
          carrying both is rejected. Returns `INVALID_ID` if malformed.
      - in: query
        name: id
        schema:
          type: string
        description: Single run id. Returns only that run. Exactly one of `analysis_id`
          and `id` is required, and a request carrying both is rejected. Returns `INVALID_ID`
          if malformed.
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      tags:
      - AI Analysis
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedAiAnalysisResultList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: ban_xJZFbcWNkF9M3brf
                      analysis_id: bod_huNssMQX4FTvALEY
                      scope: CONTENT
                      status: COMPLETED
                      result:
                        download_url: https://storage.googleapis.com/zelf-webhook-snapshots/ai_analysis/run_8f2c.json?X-Goog-Signature=...
                        expires_at: 1718000000
                        format: json
                      created_at: '2026-05-12T14:30:00Z'
                      modified_at: '2026-05-12T15:00:00Z'
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/ai_analysis/contents/:
    get:
      operationId: ai_analysis.contents
      description: |-
        `ai_analysis.contents`

        Returns the content an AI analysis ran over, in the standard content shape. Use it to inspect the exact posts included in an analysis.

        Requires `analysis_id` (an analysis `id` from `ai_analysis.list`). Brand-scoped fields (`is_owned`, `is_tagged`, `net_promoter_score`, ...) are `null` in this response.
      summary: List AI Analysis Content
      parameters:
      - in: query
        name: analysis_id
        schema:
          type: string
        description: Analysis id, as returned by `ai_analysis.list`.
        required: true
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      tags:
      - AI Analysis
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedContentList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: con_DZaWwnKo5HThZ9bu
                      platform_post_id: '7376543210987654321'
                      platform: instagram
                      content_form: VIDEO
                      thumbnail_url: https://cdn.hellozelf.com/thumbnails/zelfai-launch.png
                      posted_at: '2026-05-12T16:00:00Z'
                      view_count: 184230
                      like_count: 12840
                      comment_count: 932
                      share_count: 415
                      total_engagement: 14187
                      language: en
                      media_duration: 42.0
                      engagement_rate: 0.077
                      earned_media_value_cents: 248000
                      creator:
                        id: cr_pfqPv0FLsBRimHfi
                        username: zelfai
                        name: Zelf
                        platform: instagram
                        platform_handle: zelfai
                        platform_url: https://www.instagram.com/zelfai
                        profile_picture_url: https://cdn.hellozelf.com/avatars/zelfai.png
                        profile_bio: Creator and content intelligence for modern brands.
                        follower_count: 53120
                        active_content_count: 217
                      brand_id: brd_Lw6ZZmNYhW0P9k0x
                      net_promoter_score: 1
                      is_owned: true
                      is_tagged: true
                      is_influencer: true
                      mention_type: content
                      caption: Behind the scenes at Zelf.
                      description: How Zelf turns social signals into creator insights.
                      transcript: We walk through how Zelf ranks creators by brand
                        fit.
                      platform_url: https://www.instagram.com/zelfai
                      last_keyword: zelf creators
                      ai_summary: A product walkthrough of Zelf creator analytics.
                      hashtags:
                      - zelfai
                      - creatoreconomy
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/ai_analysis/contents/statistics/:
    get:
      operationId: ai_analysis.content_statistics
      description: |-
        `ai_analysis.content_statistics`

        Returns aggregate totals (post count, views, engagement, earned media value, and unique creators) over the content an AI analysis ran over.

        Requires `analysis_id` (an analysis `id` from `ai_analysis.list`) and returns the same totals shape as `content.statistics`.
      summary: Get AI Analysis Content Statistics
      parameters:
      - in: query
        name: analysis_id
        schema:
          type: string
        description: Analysis id, as returned by `ai_analysis.list`.
        required: true
      tags:
      - AI Analysis
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/ContentStatistics'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - post_count: 1280
                      total_views: 4500000
                      total_likes: 320000
                      total_comments: 18000
                      total_shares: 9500
                      total_engagement: 347500
                      total_earned_media_value_cents: 12500000
                      total_media_duration: 53400.0
                      engagement_rate: 0.077
                      unique_creator_count: 412
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/analytics/brand_comparison/:
    get:
      operationId: analytics.brand_comparison
      description: |-
        `analytics.brand_comparison`

        Returns one row of totals per brand: the top-level brand named by `brand_id` first, then each of its competitor sub-brands by title. Theme sub-brands are not competitors and are left out. The statistics block has the same shape as `content.statistics`, so the same parsing works for both. It counts the same window, and it leaves out mentions that appear only in a comment, which `content.statistics` includes.

        `brand_id` must name a top-level brand. A sub-brand is refused with the id of the parent to use instead.
      summary: Compare Brand Performance
      parameters:
      - in: query
        name: brand_id
        schema:
          type: string
        description: The top-level brand to analyse, as returned by `brands.list`.
          A sub-brand is refused with the id of the parent to use instead.
        required: true
      - in: query
        name: platform
        schema:
          type: array
          items:
            type: string
            enum:
            - instagram
            - tiktok
            - youtube
          uniqueItems: true
        description: Platforms to include, comma-separated. Defaults to all of them.
        explode: false
        style: form
      - in: query
        name: posted_after
        schema:
          type: string
          format: date-time
        description: 'Start of the window (UTC, ISO 8601). Supplying more than one
          start is accepted and only one applies: this one wins over `posted_within_hours`,
          which wins over `posted_within_days`.'
      - in: query
        name: posted_before
        schema:
          type: string
          format: date-time
        description: End of the window (UTC, ISO 8601). Defaults to the current time,
          and a relative start is counted back from it.
      - in: query
        name: posted_within_days
        schema:
          type: integer
          minimum: 1
          maximum: 365
        description: Start of the window, counted back from `posted_before` or the
          current time, in days (1-365). Defaults to 7 when no date parameter is supplied,
          and loses to `posted_after` and `posted_within_hours`.
      - in: query
        name: posted_within_hours
        schema:
          type: integer
          minimum: 1
          maximum: 8760
        description: Start of the window, counted back from `posted_before` or the
          current time, in hours (1-8760). `posted_after` wins over it; it wins over
          `posted_within_days`.
      tags:
      - Analytics
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/BrandComparison'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - brand:
                        id: brd_Lw6ZZmNYhW0P9k0x
                        title: Zelf
                        sub_brand_type: null
                        is_primary: true
                      statistics:
                        post_count: 1280
                        total_views: 5000000
                        total_likes: 320000
                        total_comments: 20000
                        total_shares: 10000
                        total_engagement: 350000
                        total_earned_media_value_cents: 12500000
                        engagement_rate: 0.07
                        unique_creator_count: 412
                        total_positive_nps: 245000
                        total_neutral_nps: 84000
                        total_negative_nps: 21000
                      followers:
                        total: 775000
                        accounts:
                        - platform: instagram
                          username: zelfai
                          followers: 420000
                        - platform: tiktok
                          username: zelfai
                          followers: 260000
                        - platform: youtube
                          username: zelfai
                          followers: 95000
                    - brand:
                        id: brd_TOmS4tGtnsEhS9Fy
                        title: Northwind Apparel
                        sub_brand_type: COMPETITIVE
                        is_primary: false
                      statistics:
                        post_count: 940
                        total_views: 3500000
                        total_likes: 180000
                        total_comments: 11000
                        total_shares: 5000
                        total_engagement: 196000
                        total_earned_media_value_cents: 7100000
                        engagement_rate: 0.056
                        unique_creator_count: 265
                        total_positive_nps: 122500
                        total_neutral_nps: 58800
                        total_negative_nps: 14700
                      followers:
                        total: 458000
                        accounts:
                        - platform: instagram
                          username: northwindapparel
                          followers: 310000
                        - platform: tiktok
                          username: northwindapparel
                          followers: 148000
                    - brand:
                        id: brd_rMojoPskS9hZZVa0
                        title: Harborline
                        sub_brand_type: COMPETITIVE
                        is_primary: false
                      statistics:
                        post_count: 510
                        total_views: 2000000
                        total_likes: 72000
                        total_comments: 5400
                        total_shares: 2600
                        total_engagement: 80000
                        total_earned_media_value_cents: 3400000
                        engagement_rate: 0.04
                        unique_creator_count: 148
                        total_positive_nps: 46400
                        total_neutral_nps: 26400
                        total_negative_nps: 7200
                      followers:
                        total: 137000
                        accounts:
                        - platform: instagram
                          username: harborline
                          followers: 96000
                        - platform: youtube
                          username: harborline
                          followers: 41000
                    item_count: 3
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/analytics/follower_timeseries/:
    get:
      operationId: analytics.follower_timeseries
      description: |-
        `analytics.follower_timeseries`

        Returns follower history for the top-level brand named by `brand_id` first, then each of its competitor sub-brands by title, bucketed by `period`. A brand appears only for the periods in which one of its owned handles was measured.

        Follower counts carry forward: a handle with no reading in a period keeps the last one recorded before it.
      summary: Get Brand Follower Time Series
      parameters:
      - in: query
        name: brand_id
        schema:
          type: string
        description: The top-level brand to analyse, as returned by `brands.list`.
          A sub-brand is refused with the id of the parent to use instead.
        required: true
      - in: query
        name: period
        schema:
          type: string
          enum:
          - day
          - week
          - month
          - year
        description: Size of each bucket in the series. Defaults to `day`.
      - in: query
        name: platform
        schema:
          type: array
          items:
            type: string
            enum:
            - instagram
            - tiktok
            - youtube
          uniqueItems: true
        description: Platforms to include, comma-separated. Defaults to all of them.
        explode: false
        style: form
      - in: query
        name: posted_after
        schema:
          type: string
          format: date-time
        description: 'Start of the window (UTC, ISO 8601). Supplying more than one
          start is accepted and only one applies: this one wins over `posted_within_hours`,
          which wins over `posted_within_days`.'
      - in: query
        name: posted_before
        schema:
          type: string
          format: date-time
        description: End of the window (UTC, ISO 8601). Defaults to the current time,
          and a relative start is counted back from it.
      - in: query
        name: posted_within_days
        schema:
          type: integer
          minimum: 1
          maximum: 365
        description: Start of the window, counted back from `posted_before` or the
          current time, in days (1-365). Defaults to 7 when no date parameter is supplied,
          and loses to `posted_after` and `posted_within_hours`.
      - in: query
        name: posted_within_hours
        schema:
          type: integer
          minimum: 1
          maximum: 8760
        description: Start of the window, counted back from `posted_before` or the
          current time, in hours (1-8760). `posted_after` wins over it; it wins over
          `posted_within_days`.
      tags:
      - Analytics
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/FollowerTimeseries'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - brand:
                        id: brd_Lw6ZZmNYhW0P9k0x
                        title: Zelf
                        sub_brand_type: null
                        is_primary: true
                      periods:
                      - start_date: '2026-09-28'
                        end_date: '2026-10-04'
                        followers: 742000
                        accounts:
                        - platform: instagram
                          username: zelfai
                          followers: 402000
                        - platform: tiktok
                          username: zelfai
                          followers: 248000
                        - platform: youtube
                          username: zelfai
                          followers: 92000
                      - start_date: '2026-10-05'
                        end_date: '2026-10-11'
                        followers: 758500
                        accounts:
                        - platform: instagram
                          username: zelfai
                          followers: 411000
                        - platform: tiktok
                          username: zelfai
                          followers: 254000
                        - platform: youtube
                          username: zelfai
                          followers: 93500
                      - start_date: '2026-10-12'
                        end_date: '2026-10-18'
                        followers: 775000
                        accounts:
                        - platform: instagram
                          username: zelfai
                          followers: 420000
                        - platform: tiktok
                          username: zelfai
                          followers: 260000
                        - platform: youtube
                          username: zelfai
                          followers: 95000
                    - brand:
                        id: brd_TOmS4tGtnsEhS9Fy
                        title: Northwind Apparel
                        sub_brand_type: COMPETITIVE
                        is_primary: false
                      periods:
                      - start_date: '2026-09-28'
                        end_date: '2026-10-04'
                        followers: 443000
                        accounts:
                        - platform: instagram
                          username: northwindapparel
                          followers: 301000
                        - platform: tiktok
                          username: northwindapparel
                          followers: 142000
                      - start_date: '2026-10-05'
                        end_date: '2026-10-11'
                        followers: 450700
                        accounts:
                        - platform: instagram
                          username: northwindapparel
                          followers: 305500
                        - platform: tiktok
                          username: northwindapparel
                          followers: 145200
                      - start_date: '2026-10-12'
                        end_date: '2026-10-18'
                        followers: 458000
                        accounts:
                        - platform: instagram
                          username: northwindapparel
                          followers: 310000
                        - platform: tiktok
                          username: northwindapparel
                          followers: 148000
                    item_count: 2
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/engage/comment_statistics/:
    get:
      operationId: engage.comment_statistics
      description: |-
        `engage.comment_statistics`

        Returns the comments the brand posted in the window, grouped by the kind of content they were posted on, together with the likes and replies those comments received. These are comments the brand wrote, not comments left on its content; `comments.list` returns the latter.

        The five categories add up to `total_comments_posted`.
      summary: Get Comment Statistics
      parameters:
      - in: query
        name: brand_id
        schema:
          type: string
        description: The top-level brand to analyse, as returned by `brands.list`.
          A sub-brand is refused with the id of the parent to use instead.
        required: true
      - in: query
        name: platform
        schema:
          type: array
          items:
            type: string
            enum:
            - instagram
            - tiktok
            - youtube
          uniqueItems: true
        description: Platforms to include, comma-separated. Defaults to all of them.
        explode: false
        style: form
      - in: query
        name: posted_after
        schema:
          type: string
          format: date-time
        description: 'Start of the window (UTC, ISO 8601). Supplying more than one
          start is accepted and only one applies: this one wins over `posted_within_hours`,
          which wins over `posted_within_days`.'
      - in: query
        name: posted_before
        schema:
          type: string
          format: date-time
        description: End of the window (UTC, ISO 8601). Defaults to the current time,
          and a relative start is counted back from it.
      - in: query
        name: posted_within_days
        schema:
          type: integer
          minimum: 1
          maximum: 365
        description: Start of the window, counted back from `posted_before` or the
          current time, in days (1-365). Defaults to 7 when no date parameter is supplied,
          and loses to `posted_after` and `posted_within_hours`.
      - in: query
        name: posted_within_hours
        schema:
          type: integer
          minimum: 1
          maximum: 8760
        description: Start of the window, counted back from `posted_before` or the
          current time, in hours (1-8760). `posted_after` wins over it; it wins over
          `posted_within_days`.
      tags:
      - Engage
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/CommentStatistics'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - total_comments_posted: 3600
                      categories:
                      - category: brand
                        comments: 1420
                        likes: 3860
                        replies: 512
                      - category: owned
                        comments: 980
                        likes: 2140
                        replies: 361
                      - category: competitors
                        comments: 640
                        likes: 1275
                        replies: 208
                      - category: themes
                        comments: 415
                        likes: 702
                        replies: 97
                      - category: other
                        comments: 145
                        likes: 233
                        replies: 34
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/brands/:
    post:
      operationId: sub_brands.create
      description: |-
        `sub_brands.create`

        Creates a sub-brand under the workspace brand. A sub-brand tracks a competitor (`COMPETITIVE`) or a content theme (`THEME`) separately.

        A brand may have at most 64 sub-brands; requests beyond this limit are rejected. The new brand's `id` is the value to supply as `brand_id` elsewhere.
      summary: Create Sub-brand
      tags:
      - Brands
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BrandCreateSubBrandRequest'
            examples:
              Example:
                value:
                  search_terms:
                  - zelf
                  - zelfai
                  inclusion_terms:
                  - creator
                  - influencer
                  exclusion_terms:
                  - giveaway
                  compound_inclusion_terms:
                  - marketing
                  influencer_terms:
                  - sponsored
                  - ad
                  - gifted
                  allowed_languages:
                  - en
                  boolean_query: python
                  title: Zelf
                  sub_brand_type: COMPETITIVE
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/BrandCreateSubBrandRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/BrandCreateSubBrandRequest'
        required: true
      security:
      - ApiKey: []
      responses:
        '201':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/Brand'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: brd_Lw6ZZmNYhW0P9k0x
                      title: Zelf
                      is_primary: true
                      sub_brand_type: null
                      parent_brand_id: null
                      keywords:
                        search_terms:
                        - zelf
                        - zelfai
                        inclusion_terms:
                        - creator
                        - influencer
                        exclusion_terms:
                        - giveaway
                        compound_inclusion_terms:
                        - marketing
                        influencer_terms:
                        - sponsored
                        - ad
                        - gifted
                        allowed_languages:
                        - en
                        boolean_query: python
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    get:
      operationId: brands.list
      description: |-
        `brands.list`

        Returns every brand this key can reach: the workspace brand, every brand linked to it, and the sub-brands of each. Start here when building a new integration, to find out which brand ids are available.

        Each row's `id` is the value to supply as `brand_id` wherever a brand is named. `is_primary` identifies the workspace brand, and `parent_brand_id` links a sub-brand to its parent.
      summary: List Brands
      parameters:
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      tags:
      - Brands
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedBrandList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: brd_Lw6ZZmNYhW0P9k0x
                      title: Zelf
                      is_primary: true
                      sub_brand_type: null
                      parent_brand_id: null
                      keywords:
                        search_terms:
                        - zelf
                        - zelfai
                        inclusion_terms:
                        - creator
                        - influencer
                        exclusion_terms:
                        - giveaway
                        compound_inclusion_terms:
                        - marketing
                        influencer_terms:
                        - sponsored
                        - ad
                        - gifted
                        allowed_languages:
                        - en
                        boolean_query: python
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    patch:
      operationId: brands.update
      description: |-
        `brands.update`

        Partially updates a brand's monitoring keywords. Only the supplied fields are modified.

        Targets the brand the key acts on unless `brand_id` is provided in the request body.

        Any brand listed by `brands.list` is a valid target. That list holds every brand linked to the key, each linked brand's sub-brands, and the key's own brand. A single key therefore reaches whole brand families, often dozens of brands, and this endpoint writes to all of them. Returns the updated brand.
      summary: Update Brand
      tags:
      - Brands
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchedBrandUpdateRequest'
            examples:
              Example:
                value:
                  search_terms:
                  - zelf
                  - zelfai
                  inclusion_terms:
                  - creator
                  - influencer
                  exclusion_terms:
                  - giveaway
                  compound_inclusion_terms:
                  - marketing
                  influencer_terms:
                  - sponsored
                  - ad
                  - gifted
                  allowed_languages:
                  - en
                  boolean_query: python
                  brand_id: brd_Lw6ZZmNYhW0P9k0x
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/PatchedBrandUpdateRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PatchedBrandUpdateRequest'
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/Brand'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: brd_Lw6ZZmNYhW0P9k0x
                      title: Zelf
                      is_primary: true
                      sub_brand_type: null
                      parent_brand_id: null
                      keywords:
                        search_terms:
                        - zelf
                        - zelfai
                        inclusion_terms:
                        - creator
                        - influencer
                        exclusion_terms:
                        - giveaway
                        compound_inclusion_terms:
                        - marketing
                        influencer_terms:
                        - sponsored
                        - ad
                        - gifted
                        allowed_languages:
                        - en
                        boolean_query: python
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/brands/accounts/:
    post:
      operationId: brand_accounts.add
      description: |-
        `brand_accounts.add`

        Adds profiles to a brand's `tracked`, `influencer` or `owned` list. Use `owned` for the brand's own profiles.

        Each `profile_url` is matched and deduplicated by `(platform, username)`, so re-adding an existing account has no effect. URLs that cannot be parsed fail validation on `profile_urls`.

        Supply `brand_id` in the request body to target another brand; otherwise the brand the key acts on is used. Any brand listed by `brands.list` is a valid target, which includes linked top-level brands and their sub-brands.
      summary: Add Brand Accounts
      tags:
      - Brands
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BrandAccountsWriteRequest'
            examples:
              Example:
                value:
                  brand_id: brd_Lw6ZZmNYhW0P9k0x
                  account_type: tracked
                  profile_urls:
                  - https://www.instagram.com/zelfai
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/BrandAccountsWriteRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/BrandAccountsWriteRequest'
        required: true
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/BrandAccountGroups'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - tracked:
                      - username: zelfai
                        platform: instagram
                        external_url: https://www.instagram.com/zelfai
                      influencer:
                      - username: zelfai
                        platform: instagram
                        external_url: https://www.instagram.com/zelfai
                      owned:
                      - username: zelfai
                        platform: instagram
                        external_url: https://www.instagram.com/zelfai
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    get:
      operationId: brand_accounts.list
      description: |-
        `brand_accounts.list`

        Returns a brand's tracked and influencer creator accounts, plus the `owned` accounts the brand operates itself.

        Defaults to the brand the key acts on. Supply `brand_id` to target another brand listed by `brands.list`, which includes linked top-level brands and their sub-brands. Restrict the response to a single list with the `account_type` parameter (`tracked`, `influencer` or `owned`).
      summary: List Brand Accounts
      parameters:
      - in: query
        name: account_type
        schema:
          type: string
          enum:
          - influencer
          - owned
          - tracked
        description: Restricts the response to a single list (`tracked`, `influencer`
          or `owned`).
      - in: query
        name: brand_id
        schema:
          type: string
        description: Identifies the brand to operate on. Defaults to the brand the
          key acts on when `brand_id` is omitted. Every `id` returned by `brands.list`
          is accepted. That set includes linked top-level brands and their sub-brands,
          not only sub-brands. Returns `INVALID_ID` if the value is malformed, and
          `403` for a brand outside the key's reach.
      tags:
      - Brands
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/BrandAccountGroups'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - tracked:
                      - username: zelfai
                        platform: instagram
                        external_url: https://www.instagram.com/zelfai
                      influencer:
                      - username: zelfai
                        platform: instagram
                        external_url: https://www.instagram.com/zelfai
                      owned:
                      - username: zelfai
                        platform: instagram
                        external_url: https://www.instagram.com/zelfai
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    delete:
      operationId: brand_accounts.remove
      description: |-
        `brand_accounts.remove`

        Removes profiles from a brand's `tracked`, `influencer` or `owned` list. This operation is destructive; call it only when the intent is to stop following these accounts.

        Profiles are matched by `(platform, username)` parsed from each `profile_url`. Returns the updated list.

        Supply `brand_id` in the request body to target another brand; otherwise the brand the key acts on is used. Any brand listed by `brands.list` is a valid target, which includes linked top-level brands and their sub-brands.
      summary: Remove Brand Accounts
      tags:
      - Brands
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BrandAccountsWriteRequest'
            examples:
              Example:
                value:
                  brand_id: brd_Lw6ZZmNYhW0P9k0x
                  account_type: tracked
                  profile_urls:
                  - https://www.instagram.com/zelfai
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/BrandAccountsWriteRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/BrandAccountsWriteRequest'
        required: true
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/BrandAccountGroups'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - tracked:
                      - username: zelfai
                        platform: instagram
                        external_url: https://www.instagram.com/zelfai
                      influencer:
                      - username: zelfai
                        platform: instagram
                        external_url: https://www.instagram.com/zelfai
                      owned:
                      - username: zelfai
                        platform: instagram
                        external_url: https://www.instagram.com/zelfai
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/brands/catalog/:
    get:
      operationId: brands.catalog
      description: |-
        `brands.catalog`

        Returns the brands Zelf curates for competitive benchmarking. Every api key can read content for these brands, whether or not they are linked to yours, so this is where to look for a competitor that is not one of your own sub-brands.

        Each row's `id` is the value to supply as `brand_id` on the content and AI analysis endpoints; the brand write endpoints do not accept it. Narrow the list with `search` (title substring), `industry_id`, or `category_id`; the id values for the last two come from `brands.catalog_categories`.
      summary: Search Brand Catalog
      parameters:
      - name: category_id
        in: query
        required: false
        schema:
          type: string
        description: Category id from `brands.catalog_categories`. Case is ignored.
      - name: industry_id
        in: query
        required: false
        schema:
          type: string
        description: Industry id from `brands.catalog_categories`. Case is ignored.
      - name: order_by
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - title
            - -title
          uniqueItems: true
        explode: false
        style: form
        description: '**Sort direction is inverted:** `order_by=field` sorts descending
          and `order_by=-field` sorts ascending. Comma-separate to sort by multiple
          fields.'
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: search
        in: query
        required: false
        schema:
          type: string
        description: Brand title substring (case-insensitive).
      tags:
      - Brands
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedBrandCatalogList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: brd_Lw6ZZmNYhW0P9k0x
                      title: Zelf
                      brand_logo: https://cdn.hellozelf.com/logos/zelfai.png
                      industry_id: C05
                      industry_name: Apparel and Fashion
                      category_id: C05S04
                      category_name: Casual/Everyday Wear
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/brands/catalog/categories/:
    get:
      operationId: brands.catalog_categories
      description: |-
        `brands.catalog_categories`

        Returns every industry and the categories under it, as one flat list. An industry has `level: industry` and a null `parent_id`; a category has `level: category` and the `parent_id` of its industry.

        Each `id` is the value to supply as `industry_id` or `category_id` to `brands.catalog`.
      summary: List Brand Catalog Categories
      tags:
      - Brands
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/BrandCatalogItem'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: C05S04
                      name: Casual/Everyday Wear
                      level: category
                      parent_id: C05
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/comment/:
    get:
      operationId: comments.list
      description: |-
        `comments.list`

        Returns a paginated list of comments, ordered by newest first. At least one filter is required to scope the query.

        Filter by `content_id` (a content `id`) to retrieve the comments on a piece of content, by `id` to retrieve a single comment, or by `parent_comment_id` to retrieve the replies to a comment. A request that supplies none of these returns `INVALID_ID`.
      summary: List Comments
      parameters:
      - name: content_id
        in: query
        required: false
        schema:
          type: string
        description: Id of the content to list comments for.
      - name: id
        in: query
        required: false
        schema:
          type: string
        description: Id of a single comment to fetch.
      - name: is_parent_comment
        in: query
        required: false
        schema:
          type: boolean
        description: True to only top-level comments, false to only replies.
      - name: order_by
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - like_count
            - -like_count
          uniqueItems: true
        description: |-
          Sort by `like_count`. A bare key sorts descending.

          **Sort direction is inverted:** `order_by=field` sorts descending and `order_by=-field` sorts ascending. Comma-separate to sort by multiple fields.
        explode: false
        style: form
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      - name: parent_comment_id
        in: query
        required: false
        schema:
          type: string
        description: Id of the parent comment to list replies for.
      tags:
      - Comment
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedCommentList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: com_tfBrrZrU2Kp5VTsW
                      text: Love what Zelf is building!
                      platform: instagram
                      like_count: 312
                      posted_at: '2026-05-12T16:00:00Z'
                      content_id: con_DZaWwnKo5HThZ9bu
                      parent_comment_id: null
                      is_parent_comment: true
                      author:
                        id: cr_bxUfTmtAcXdlHwKO
                        username: zelfai
                        name: Zelf
                        profile_picture_url: https://cdn.hellozelf.com/avatars/zelfai.png
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/content/:
    get:
      operationId: content.list
      description: |-
        `content.list`

        Returns a paginated list of content. Every request must scope what it covers: supply at least one of `creator_id`, `creator_username`, `search`, `brand_id`, or `external_id`, otherwise the request is rejected with `VALIDATION_ERROR`. By default results are ordered by `posted_at` descending (most recently posted first); override with `order_by`.

        Every response echoes the effective defaults in `applied_filters` (the resolved date window, `page`, `page_size`, and `brand_id`), so an empty `results` set can be explained.

        ### Dates

        Every request covers a time window. Four parameters define it.

        | Parameter | Type | Defines |
        |---|---|---|
        | `posted_after` | timestamp | Start of the window |
        | `posted_within_hours` | integer, 1 to 8760 | Start of the window, counted back from the current time |
        | `posted_within_days` | integer, 1 to 365 | Start of the window, counted back from the current time |
        | `posted_before` | timestamp | End of the window |

        A request with no date parameter covers the last 7 days. `applied_filters` reports this as `posted_within_days: 7`.

        Three parameters define the start of the window, so a request may carry more than one. Such a request is valid. The API applies the first parameter present in this order and ignores the rest:

        1. `posted_after`
        2. `posted_within_hours`
        3. `posted_within_days`

        `applied_filters` contains only the parameter that was applied, so it identifies the window that produced a response.

        `posted_before` defines the end of the window and combines with any of the three. On its own it covers the 366 days before it. With `posted_within_hours` or `posted_within_days` the window is counted back from `posted_before` instead of from the current time, so `posted_before=2026-09-01T00:00:00Z` with `posted_within_hours=24` covers `2026-08-31T00:00:00Z` to `2026-09-01T00:00:00Z`.

        Two limits apply to every window. `posted_after` and `posted_before` may be at most 366 days apart, and a longer period is read as consecutive requests. Content posted before 2025-01-01 is not available.

        A value outside the ranges in the table returns `VALIDATION_ERROR`. Such values are rejected, not clamped: `posted_within_days=400` is an error rather than 365.

        Results are paginated: `page` defaults to `1` and `page_size` to `30`, which is also the maximum (larger values are reduced to `30`). `item_count` is the number of rows in the returned page, not the size of the whole match set, and `next` is non-null while further pages remain. For the total number of matching posts, call `content.statistics` with the same query and read `post_count`.

        When `brand_id` is omitted, the brand-specific fields (`brand_id`, `is_owned`, `is_tagged`, `is_influencer`, `mention_type`, `net_promoter_score`) are returned as `null`, and the brand-only filters and sorts return `BRAND_ID_REQUIRED`. Supply `brand_id` (one or more values, comma-separated) to populate those fields and enable the brand-only filters. Each row's `id` is the value to use for `content.details` and for the `comments.list` `content_id`.

        **Examples**

        - Full-text search over the last 7 days (brand fields null): `GET /api/v1/content/?search=running+shoes`
        - Brand content over an explicit window: `GET /api/v1/content/?brand_id=brd_123&posted_after=2026-01-01T00:00:00Z&posted_before=2026-06-30T23:59:59Z`
        - Most-viewed first: add `&order_by=view_count`. Sort direction is inverted, so a bare key sorts descending and a `-` prefix ascending.
      summary: List Content
      parameters:
      - in: query
        name: brand_id
        schema:
          type: string
        description: One or more brand ids, comma-separated, as returned by `brands.list`.
          Scopes the query to those brands, populates the brand-specific fields, and
          enables the brand-only filters and sorts. Any id you cannot access returns
          `403`, and a malformed value returns `INVALID_ID`.
      - name: comment_count
        in: query
        required: false
        schema:
          type: integer
        description: Number of comments on the post.
      - name: comment_count_max
        in: query
        required: false
        schema:
          type: integer
        description: Number of comments on the post. (Inclusive Upper Bound)
      - name: comment_count_min
        in: query
        required: false
        schema:
          type: integer
        description: Number of comments on the post. (Inclusive Lower Bound)
      - name: content_form
        in: query
        required: false
        schema:
          type: string
          enum:
          - VIDEO
          - PHOTO
        description: Content medium. Case-sensitive; values are stored and returned
          in uppercase.
      - name: creator_follower_count
        in: query
        required: false
        schema:
          type: integer
        description: Follower count of the post's creator.
      - name: creator_follower_count_max
        in: query
        required: false
        schema:
          type: integer
        description: Follower count of the post's creator. (Inclusive Upper Bound)
      - name: creator_follower_count_min
        in: query
        required: false
        schema:
          type: integer
        description: Follower count of the post's creator. (Inclusive Lower Bound)
      - name: creator_id
        in: query
        required: false
        schema:
          type: string
        description: Limit results to a single creator by id (from `/api/v1/creator/`).
      - name: creator_username
        in: query
        required: false
        schema:
          type: string
        description: Creator username substring. Case-insensitive.
      - name: earned_media_value_cents
        in: query
        required: false
        schema:
          type: integer
        description: Estimated earned media value, in cents.
      - name: earned_media_value_cents_max
        in: query
        required: false
        schema:
          type: integer
        description: Estimated earned media value, in cents. (Inclusive Upper Bound)
      - name: earned_media_value_cents_min
        in: query
        required: false
        schema:
          type: integer
        description: Estimated earned media value, in cents. (Inclusive Lower Bound)
      - name: engagement_rate
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Engagement as a fraction of views (for example, 0.05 = 5%).
      - name: engagement_rate_max
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Engagement as a fraction of views (for example, 0.05 = 5%). (Inclusive
          Upper Bound)
      - name: engagement_rate_min
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Engagement as a fraction of views (for example, 0.05 = 5%). (Inclusive
          Lower Bound)
      - name: external_id
        in: query
        required: false
        schema:
          type: string
        description: Platform-side post id. Matched exactly.
      - name: is_influencer
        in: query
        required: false
        schema:
          type: boolean
        description: Limit results to the brand's influencer content, or exclude it
          with `false`. Requires brand_id.
      - name: is_owned
        in: query
        required: false
        schema:
          type: boolean
        description: Limit results to content owned by the brand. Requires brand_id.
      - name: is_tagged
        in: query
        required: false
        schema:
          type: boolean
        description: Limit results to content where the brand is tagged. Requires
          brand_id.
      - name: language
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
        description: Language codes to include, comma-separated (for example, en,
          es).
        explode: false
        style: form
      - name: like_count
        in: query
        required: false
        schema:
          type: integer
        description: Number of likes on the post.
      - name: like_count_max
        in: query
        required: false
        schema:
          type: integer
        description: Number of likes on the post. (Inclusive Upper Bound)
      - name: like_count_min
        in: query
        required: false
        schema:
          type: integer
        description: Number of likes on the post. (Inclusive Lower Bound)
      - name: media_duration
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Length of the media in seconds.
      - name: media_duration_max
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Length of the media in seconds. (Inclusive Upper Bound)
      - name: media_duration_min
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Length of the media in seconds. (Inclusive Lower Bound)
      - name: mention_type
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - content
            - comment
          uniqueItems: true
        description: Limit results to these mention kinds, comma-separated. Requires
          brand_id.
        explode: false
        style: form
      - name: mention_type_not
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - content
            - comment
          uniqueItems: true
        description: Exclude these mention kinds, comma-separated. Requires brand_id.
        explode: false
        style: form
      - name: net_promoter_score
        in: query
        required: false
        schema:
          type: integer
        description: Brand net promoter score for the content. Requires brand_id.
      - name: net_promoter_score_max
        in: query
        required: false
        schema:
          type: integer
        description: Brand net promoter score for the content. Requires brand_id.
          (Inclusive Upper Bound)
      - name: net_promoter_score_min
        in: query
        required: false
        schema:
          type: integer
        description: Brand net promoter score for the content. Requires brand_id.
          (Inclusive Lower Bound)
      - name: order_by
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - posted_at
            - -posted_at
            - view_count
            - -view_count
            - like_count
            - -like_count
            - comment_count
            - -comment_count
            - share_count
            - -share_count
            - engagement_rate
            - -engagement_rate
            - total_engagement
            - -total_engagement
            - earned_media_value_cents
            - -earned_media_value_cents
            - net_promoter_score
            - -net_promoter_score
            - relevance_score
            - -relevance_score
          uniqueItems: true
        description: |-
          Sort the results. Any of: posted_at, view_count, like_count, comment_count, share_count, engagement_rate, total_engagement, earned_media_value_cents, net_promoter_score, relevance_score. A bare key sorts descending; prefix with `-` to sort ascending.

          **Sort direction is inverted:** `order_by=field` sorts descending and `order_by=-field` sorts ascending. Comma-separate to sort by multiple fields.
        explode: false
        style: form
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      - name: platform
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - instagram
            - tiktok
            - youtube
          uniqueItems: true
        description: Platforms to include, comma-separated.
        explode: false
        style: form
      - name: posted
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: When the post was published (UTC, ISO 8601).
      - name: posted_after
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: When the post was published (UTC, ISO 8601). (Inclusive Lower
          Bound)
      - name: posted_before
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: When the post was published (UTC, ISO 8601). (Inclusive Upper
          Bound)
      - name: posted_within_days
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 365
        description: Start of the window, counted back from the current time, in days
          (1-365). Defaults to 7 when no date parameter is supplied. `posted_after`
          and `posted_within_hours` take precedence over it.
      - name: posted_within_hours
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 8760
        description: Start of the window, counted back from the current time, in hours
          (1-8760). Takes precedence over `posted_within_days`. Counted back from
          `posted_before` when that is also supplied.
      - name: search
        in: query
        required: false
        schema:
          type: string
        description: |-
          Full-text search across the caption, transcript, summary, and hashtags.

          **Syntax**
          - Multiple words are combined with AND: `nike running` matches content containing both `nike` and `running`.
          - `OR` (case-insensitive) matches either side: `nike OR adidas`.
          - `NOT` (case-insensitive) excludes a term: `running NOT marathon` matches `running` but not `marathon`.
          - Wrap a phrase in double quotes to match the words in order: `"summer collection"`.
          - Negate a phrase with `NOT`: `NOT "out of stock"`.
          - Group with parentheses to combine operators: `(nike OR adidas) running`.
          - `ONEAR/n` (case-insensitive) matches terms within `n` words of each other, in order: `apple ONEAR/2 watch` matches `apple watch` and `apple smart watch`, but not `watch ... apple`. It binds more tightly than `AND` and `OR`, so `apple ONEAR/2 watch AND cheap` is interpreted as `(apple near watch) AND cheap`.

          **Examples**
          - `nike running` - contains both terms.
          - `nike OR adidas` - contains either brand.
          - `"summer sale" NOT clearance` - the exact phrase, without `clearance`.
          - `(running OR cycling) NOT "beginner guide"` - either sport, excluding the phrase.
          - `apple ONEAR/2 watch` - `apple` within two words before `watch`.
      - name: share_count
        in: query
        required: false
        schema:
          type: integer
        description: Number of shares of the post.
      - name: share_count_max
        in: query
        required: false
        schema:
          type: integer
        description: Number of shares of the post. (Inclusive Upper Bound)
      - name: share_count_min
        in: query
        required: false
        schema:
          type: integer
        description: Number of shares of the post. (Inclusive Lower Bound)
      - name: total_engagement
        in: query
        required: false
        schema:
          type: integer
        description: Total engagement (likes plus comments plus shares).
      - name: total_engagement_max
        in: query
        required: false
        schema:
          type: integer
        description: Total engagement (likes plus comments plus shares). (Inclusive
          Upper Bound)
      - name: total_engagement_min
        in: query
        required: false
        schema:
          type: integer
        description: Total engagement (likes plus comments plus shares). (Inclusive
          Lower Bound)
      - name: view_count
        in: query
        required: false
        schema:
          type: integer
        description: Number of views on the post.
      - name: view_count_max
        in: query
        required: false
        schema:
          type: integer
        description: Number of views on the post. (Inclusive Upper Bound)
      - name: view_count_min
        in: query
        required: false
        schema:
          type: integer
        description: Number of views on the post. (Inclusive Lower Bound)
      tags:
      - Content
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  applied_filters:
                    type: object
                    additionalProperties: true
                    description: The effective filters the request ran with.
                  results:
                    $ref: '#/components/schemas/PaginatedContentList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - applied_filters
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    applied_filters:
                      search: zelfai
                      order_by:
                      - timestamp
                      - id
                      posted_within_days: 7
                      brand_id: null
                      page: 1
                      page_size: 30
                    results:
                    - id: con_DZaWwnKo5HThZ9bu
                      platform_post_id: '7376543210987654321'
                      platform: instagram
                      content_form: VIDEO
                      thumbnail_url: https://cdn.hellozelf.com/thumbnails/zelfai-launch.png
                      posted_at: '2026-05-12T16:00:00Z'
                      view_count: 184230
                      like_count: 12840
                      comment_count: 932
                      share_count: 415
                      total_engagement: 14187
                      language: en
                      media_duration: 42.0
                      engagement_rate: 0.077
                      earned_media_value_cents: 248000
                      creator:
                        id: cr_pfqPv0FLsBRimHfi
                        username: zelfai
                        name: Zelf
                        platform: instagram
                        platform_handle: zelfai
                        platform_url: https://www.instagram.com/zelfai
                        profile_picture_url: https://cdn.hellozelf.com/avatars/zelfai.png
                        profile_bio: Creator and content intelligence for modern brands.
                        follower_count: 53120
                        active_content_count: 217
                      brand_id: brd_Lw6ZZmNYhW0P9k0x
                      net_promoter_score: 1
                      is_owned: true
                      is_tagged: true
                      is_influencer: true
                      mention_type: content
                      caption: Behind the scenes at Zelf.
                      description: How Zelf turns social signals into creator insights.
                      transcript: We walk through how Zelf ranks creators by brand
                        fit.
                      platform_url: https://www.instagram.com/zelfai
                      last_keyword: zelf creators
                      ai_summary: A product walkthrough of Zelf creator analytics.
                      hashtags:
                      - zelfai
                      - creatoreconomy
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/content/statistics/:
    get:
      operationId: content.statistics
      description: |-
        `content.statistics`

        Returns a single aggregate of reach, engagement, value, and creator counts over the **same content set** that `content.list` would return for an identical query. The `post_count` is the number of posts in that whole set, across every page the list would return, and each other total is the sum over those same posts. `content.list` never counts the full set: its `item_count` is only the number of rows in the page it returned, so `post_count` here is the only total available.

        This accepts every filter `content.list` accepts and applies the **same defaults** and requirements: every request must scope what it covers with at least one of `creator_id`, `creator_username`, `search`, `brand_id`, or `external_id` (else `VALIDATION_ERROR`); brand-only filters and sorts still require `brand_id`. `order_by` is validated the same as in `content.list` (brand-only sorts still require `brand_id`) but has no effect on the aggregated totals. Returns a single totals object (`item_count: 1`), not paginated, and echoes the resolved date window and `brand_id` in `applied_filters`.

        ### Dates

        `content.statistics` takes the same four date parameters as `content.list` and applies them identically. `posted_after`, `posted_within_hours` (1 to 8760) and `posted_within_days` (1 to 365) define the start of the window; `posted_before` defines the end.

        A request with no date parameter covers the last 7 days. A request carrying more than one start parameter is valid, and the first present in the order `posted_after`, `posted_within_hours`, `posted_within_days` is applied; `applied_filters` reports that one. `posted_after` and `posted_before` may be at most 366 days apart.

        Both endpoints apply these parameters identically, so one set of dates covers the same posts on each. `post_count` is therefore the size of the set `content.list` pages through.

        Supply `brand_id` (one or more values, comma-separated) to scope the totals the same way the list does. Each piece of content is counted once even when several `brand_id` values match it.

        **Examples**

        - Totals for a search over the last 7 days: `GET /api/v1/content/statistics/?search=running+shoes`
        - Brand totals over an explicit window: `GET /api/v1/content/statistics/?brand_id=brd_123&posted_after=2026-01-01T00:00:00Z&posted_before=2026-06-30T23:59:59Z`
        - Totals for a single platform: add `&platform=tiktok`.
      summary: Get Content Statistics
      parameters:
      - in: query
        name: brand_id
        schema:
          type: string
        description: One or more brand ids, comma-separated, as returned by `brands.list`.
          Scopes the query to those brands, populates the brand-specific fields, and
          enables the brand-only filters and sorts. Any id you cannot access returns
          `403`, and a malformed value returns `INVALID_ID`.
      - name: comment_count
        in: query
        required: false
        schema:
          type: integer
        description: Number of comments on the post.
      - name: comment_count_max
        in: query
        required: false
        schema:
          type: integer
        description: Number of comments on the post. (Inclusive Upper Bound)
      - name: comment_count_min
        in: query
        required: false
        schema:
          type: integer
        description: Number of comments on the post. (Inclusive Lower Bound)
      - name: content_form
        in: query
        required: false
        schema:
          type: string
          enum:
          - VIDEO
          - PHOTO
        description: Content medium. Case-sensitive; values are stored and returned
          in uppercase.
      - name: creator_follower_count
        in: query
        required: false
        schema:
          type: integer
        description: Follower count of the post's creator.
      - name: creator_follower_count_max
        in: query
        required: false
        schema:
          type: integer
        description: Follower count of the post's creator. (Inclusive Upper Bound)
      - name: creator_follower_count_min
        in: query
        required: false
        schema:
          type: integer
        description: Follower count of the post's creator. (Inclusive Lower Bound)
      - name: creator_id
        in: query
        required: false
        schema:
          type: string
        description: Limit results to a single creator by id (from `/api/v1/creator/`).
      - name: creator_username
        in: query
        required: false
        schema:
          type: string
        description: Creator username substring. Case-insensitive.
      - name: earned_media_value_cents
        in: query
        required: false
        schema:
          type: integer
        description: Estimated earned media value, in cents.
      - name: earned_media_value_cents_max
        in: query
        required: false
        schema:
          type: integer
        description: Estimated earned media value, in cents. (Inclusive Upper Bound)
      - name: earned_media_value_cents_min
        in: query
        required: false
        schema:
          type: integer
        description: Estimated earned media value, in cents. (Inclusive Lower Bound)
      - name: engagement_rate
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Engagement as a fraction of views (for example, 0.05 = 5%).
      - name: engagement_rate_max
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Engagement as a fraction of views (for example, 0.05 = 5%). (Inclusive
          Upper Bound)
      - name: engagement_rate_min
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Engagement as a fraction of views (for example, 0.05 = 5%). (Inclusive
          Lower Bound)
      - name: external_id
        in: query
        required: false
        schema:
          type: string
        description: Platform-side post id. Matched exactly.
      - name: is_influencer
        in: query
        required: false
        schema:
          type: boolean
        description: Limit results to the brand's influencer content, or exclude it
          with `false`. Requires brand_id.
      - name: is_owned
        in: query
        required: false
        schema:
          type: boolean
        description: Limit results to content owned by the brand. Requires brand_id.
      - name: is_tagged
        in: query
        required: false
        schema:
          type: boolean
        description: Limit results to content where the brand is tagged. Requires
          brand_id.
      - name: language
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
        description: Language codes to include, comma-separated (for example, en,
          es).
        explode: false
        style: form
      - name: like_count
        in: query
        required: false
        schema:
          type: integer
        description: Number of likes on the post.
      - name: like_count_max
        in: query
        required: false
        schema:
          type: integer
        description: Number of likes on the post. (Inclusive Upper Bound)
      - name: like_count_min
        in: query
        required: false
        schema:
          type: integer
        description: Number of likes on the post. (Inclusive Lower Bound)
      - name: media_duration
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Length of the media in seconds.
      - name: media_duration_max
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Length of the media in seconds. (Inclusive Upper Bound)
      - name: media_duration_min
        in: query
        required: false
        schema:
          type: number
          format: float
        description: Length of the media in seconds. (Inclusive Lower Bound)
      - name: mention_type
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - content
            - comment
          uniqueItems: true
        description: Limit results to these mention kinds, comma-separated. Requires
          brand_id.
        explode: false
        style: form
      - name: mention_type_not
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - content
            - comment
          uniqueItems: true
        description: Exclude these mention kinds, comma-separated. Requires brand_id.
        explode: false
        style: form
      - name: net_promoter_score
        in: query
        required: false
        schema:
          type: integer
        description: Brand net promoter score for the content. Requires brand_id.
      - name: net_promoter_score_max
        in: query
        required: false
        schema:
          type: integer
        description: Brand net promoter score for the content. Requires brand_id.
          (Inclusive Upper Bound)
      - name: net_promoter_score_min
        in: query
        required: false
        schema:
          type: integer
        description: Brand net promoter score for the content. Requires brand_id.
          (Inclusive Lower Bound)
      - name: order_by
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - posted_at
            - -posted_at
            - view_count
            - -view_count
            - like_count
            - -like_count
            - comment_count
            - -comment_count
            - share_count
            - -share_count
            - engagement_rate
            - -engagement_rate
            - total_engagement
            - -total_engagement
            - earned_media_value_cents
            - -earned_media_value_cents
            - net_promoter_score
            - -net_promoter_score
            - relevance_score
            - -relevance_score
          uniqueItems: true
        description: |-
          Sort the results. Any of: posted_at, view_count, like_count, comment_count, share_count, engagement_rate, total_engagement, earned_media_value_cents, net_promoter_score, relevance_score. A bare key sorts descending; prefix with `-` to sort ascending.

          **Sort direction is inverted:** `order_by=field` sorts descending and `order_by=-field` sorts ascending. Comma-separate to sort by multiple fields.
        explode: false
        style: form
      - name: platform
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - instagram
            - tiktok
            - youtube
          uniqueItems: true
        description: Platforms to include, comma-separated.
        explode: false
        style: form
      - name: posted
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: When the post was published (UTC, ISO 8601).
      - name: posted_after
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: When the post was published (UTC, ISO 8601). (Inclusive Lower
          Bound)
      - name: posted_before
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: When the post was published (UTC, ISO 8601). (Inclusive Upper
          Bound)
      - name: posted_within_days
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 365
        description: Start of the window, counted back from the current time, in days
          (1-365). Defaults to 7 when no date parameter is supplied. `posted_after`
          and `posted_within_hours` take precedence over it.
      - name: posted_within_hours
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 8760
        description: Start of the window, counted back from the current time, in hours
          (1-8760). Takes precedence over `posted_within_days`. Counted back from
          `posted_before` when that is also supplied.
      - name: search
        in: query
        required: false
        schema:
          type: string
        description: |-
          Full-text search across the caption, transcript, summary, and hashtags.

          **Syntax**
          - Multiple words are combined with AND: `nike running` matches content containing both `nike` and `running`.
          - `OR` (case-insensitive) matches either side: `nike OR adidas`.
          - `NOT` (case-insensitive) excludes a term: `running NOT marathon` matches `running` but not `marathon`.
          - Wrap a phrase in double quotes to match the words in order: `"summer collection"`.
          - Negate a phrase with `NOT`: `NOT "out of stock"`.
          - Group with parentheses to combine operators: `(nike OR adidas) running`.
          - `ONEAR/n` (case-insensitive) matches terms within `n` words of each other, in order: `apple ONEAR/2 watch` matches `apple watch` and `apple smart watch`, but not `watch ... apple`. It binds more tightly than `AND` and `OR`, so `apple ONEAR/2 watch AND cheap` is interpreted as `(apple near watch) AND cheap`.

          **Examples**
          - `nike running` - contains both terms.
          - `nike OR adidas` - contains either brand.
          - `"summer sale" NOT clearance` - the exact phrase, without `clearance`.
          - `(running OR cycling) NOT "beginner guide"` - either sport, excluding the phrase.
          - `apple ONEAR/2 watch` - `apple` within two words before `watch`.
      - name: share_count
        in: query
        required: false
        schema:
          type: integer
        description: Number of shares of the post.
      - name: share_count_max
        in: query
        required: false
        schema:
          type: integer
        description: Number of shares of the post. (Inclusive Upper Bound)
      - name: share_count_min
        in: query
        required: false
        schema:
          type: integer
        description: Number of shares of the post. (Inclusive Lower Bound)
      - name: total_engagement
        in: query
        required: false
        schema:
          type: integer
        description: Total engagement (likes plus comments plus shares).
      - name: total_engagement_max
        in: query
        required: false
        schema:
          type: integer
        description: Total engagement (likes plus comments plus shares). (Inclusive
          Upper Bound)
      - name: total_engagement_min
        in: query
        required: false
        schema:
          type: integer
        description: Total engagement (likes plus comments plus shares). (Inclusive
          Lower Bound)
      - name: view_count
        in: query
        required: false
        schema:
          type: integer
        description: Number of views on the post.
      - name: view_count_max
        in: query
        required: false
        schema:
          type: integer
        description: Number of views on the post. (Inclusive Upper Bound)
      - name: view_count_min
        in: query
        required: false
        schema:
          type: integer
        description: Number of views on the post. (Inclusive Lower Bound)
      tags:
      - Content
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  applied_filters:
                    type: object
                    additionalProperties: true
                    description: The effective filters the request ran with.
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/ContentStatistics'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - applied_filters
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    applied_filters:
                      search: zelfai
                      order_by:
                      - timestamp
                      - id
                      posted_within_days: 7
                      brand_id: null
                    results:
                    - post_count: 1280
                      total_views: 4500000
                      total_likes: 320000
                      total_comments: 18000
                      total_shares: 9500
                      total_engagement: 347500
                      total_earned_media_value_cents: 12500000
                      total_media_duration: 53400.0
                      engagement_rate: 0.077
                      unique_creator_count: 412
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/content/discovery/:
    post:
      operationId: content.discover
      description: |-
        `content.discover`

        Starts an asynchronous job that indexes fresh content matching a search term across the requested platforms. Use it to ingest content Zelf does not yet cover.

        The response describes the job, not the content itself. Once the job completes, query `content.list` to retrieve the indexed content. An identical term requested again within the deduplication window reuses the in-flight job and returns `dispatched: false` instead of starting a new one.
      summary: Create Content Discovery
      tags:
      - Content
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContentDiscoveryRequestRequest'
            examples:
              Example:
                value:
                  search_term: zelf creators
                  platforms:
                  - instagram
                  - tiktok
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/ContentDiscoveryRequestRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ContentDiscoveryRequestRequest'
        required: true
      security:
      - ApiKey: []
      responses:
        '201':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/ContentDiscovery'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: sas_rVCYR6uQsQNCDRnY
                      search_term: zelf creators
                      created_at: '2026-05-12T14:30:00Z'
                      updated_at: '2026-05-12T15:00:00Z'
                      platforms:
                      - instagram
                      - tiktok
                      status: COMPLETED
                      dispatched: true
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/content/transcribe/:
    post:
      operationId: content.transcribe
      description: |-
        `content.transcribe`

        Transcribes the speech in the content's audio or video into text. Returns `202` with `status: PENDING` when a run starts. Call again to follow it: `PENDING` while it runs, `COMPLETED` once the content has a transcription, `FAILED` if all 3 tries failed.
      summary: Transcribe Content
      tags:
      - Content
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContentActionRequestRequest'
            examples:
              Example:
                value:
                  content_id: con_DZaWwnKo5HThZ9bu
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/ContentActionRequestRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ContentActionRequestRequest'
        required: true
      security:
      - ApiKey: []
      responses:
        '202':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/ContentActionResponse'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Queued:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - content_id: con_DZaWwnKo5HThZ9bu
                      status: PENDING
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: A new run started.
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/ContentActionResponse'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                AlreadyDone:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - content_id: con_DZaWwnKo5HThZ9bu
                      status: COMPLETED
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
                  summary: Already done
                StillRunning:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - content_id: con_DZaWwnKo5HThZ9bu
                      status: PENDING
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
                  summary: Still running
                Failed:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - content_id: con_DZaWwnKo5HThZ9bu
                      status: FAILED
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: '`COMPLETED`: the result is ready. `PENDING`: the run is still
            going. `FAILED`: all 3 tries failed.'
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/content/ai_summarize/:
    post:
      operationId: content.ai_summarize
      description: |-
        `content.ai_summarize`

        Writes a short AI summary of what the content is about. Returns `202` with `status: PENDING` when a run starts. Call again to follow it: `PENDING` while it runs, `COMPLETED` once the content has a summary, `FAILED` if all 3 tries failed.
      summary: AI Summarize Content
      tags:
      - Content
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContentActionRequestRequest'
            examples:
              Example:
                value:
                  content_id: con_DZaWwnKo5HThZ9bu
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/ContentActionRequestRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ContentActionRequestRequest'
        required: true
      security:
      - ApiKey: []
      responses:
        '202':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/ContentActionResponse'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Queued:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - content_id: con_DZaWwnKo5HThZ9bu
                      status: PENDING
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: A new run started.
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/ContentActionResponse'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                AlreadyDone:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - content_id: con_DZaWwnKo5HThZ9bu
                      status: COMPLETED
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
                  summary: Already done
                StillRunning:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - content_id: con_DZaWwnKo5HThZ9bu
                      status: PENDING
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
                  summary: Still running
                Failed:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - content_id: con_DZaWwnKo5HThZ9bu
                      status: FAILED
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: '`COMPLETED`: the result is ready. `PENDING`: the run is still
            going. `FAILED`: all 3 tries failed.'
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/content/{id}/:
    get:
      operationId: content.details
      description: |-
        `content.details`

        Returns a single piece of content identified by `id`.

        Supply `brand_id` to additionally require the content to exist under that brand and to populate the brand-specific fields. If the content does not exist under that brand, the response is an empty result set rather than a 404. When `brand_id` is omitted, the brand-specific fields are returned as `null`.
      summary: Get Content
      parameters:
      - in: query
        name: brand_id
        schema:
          type: string
        description: Optional brand id. When supplied, the content must exist under
          that brand, otherwise the response is an empty result set. Populates the
          brand-specific response fields.
      - in: path
        name: id
        schema:
          type: string
        required: true
      tags:
      - Content
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/Content'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: con_DZaWwnKo5HThZ9bu
                      platform_post_id: '7376543210987654321'
                      platform: instagram
                      content_form: VIDEO
                      thumbnail_url: https://cdn.hellozelf.com/thumbnails/zelfai-launch.png
                      posted_at: '2026-05-12T16:00:00Z'
                      view_count: 184230
                      like_count: 12840
                      comment_count: 932
                      share_count: 415
                      total_engagement: 14187
                      language: en
                      media_duration: 42.0
                      engagement_rate: 0.077
                      earned_media_value_cents: 248000
                      creator:
                        id: cr_pfqPv0FLsBRimHfi
                        username: zelfai
                        name: Zelf
                        platform: instagram
                        platform_handle: zelfai
                        platform_url: https://www.instagram.com/zelfai
                        profile_picture_url: https://cdn.hellozelf.com/avatars/zelfai.png
                        profile_bio: Creator and content intelligence for modern brands.
                        follower_count: 53120
                        active_content_count: 217
                      brand_id: brd_Lw6ZZmNYhW0P9k0x
                      net_promoter_score: 1
                      is_owned: true
                      is_tagged: true
                      is_influencer: true
                      mention_type: content
                      caption: Behind the scenes at Zelf.
                      description: How Zelf turns social signals into creator insights.
                      transcript: We walk through how Zelf ranks creators by brand
                        fit.
                      platform_url: https://www.instagram.com/zelfai
                      last_keyword: zelf creators
                      ai_summary: A product walkthrough of Zelf creator analytics.
                      hashtags:
                      - zelfai
                      - creatoreconomy
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/creator/:
    get:
      operationId: creators.list
      description: |-
        `creators.list`

        Returns creators across the platforms Zelf supports. At least one of `id`, `username`, or `email` must be supplied to scope the search.

        Results can be ordered with `order_by` (`follower_count`, `id`). The `id` of each creator corresponds to the `creator_id` filter on `creator_vetting.list`. Requests that supply none of the required filters return `INVALID_ID`.
      summary: List Creators
      parameters:
      - name: email
        in: query
        required: false
        schema:
          type: string
          format: email
        description: Creator email (exact match, case-insensitive).
      - name: id
        in: query
        required: false
        schema:
          type: string
        description: Creator id (from a previous creator response).
      - name: order_by
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - follower_count
            - -follower_count
            - id
            - -id
          uniqueItems: true
        explode: false
        style: form
        description: '**Sort direction is inverted:** `order_by=field` sorts descending
          and `order_by=-field` sorts ascending. Comma-separate to sort by multiple
          fields.'
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      - name: platform
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - instagram
            - tiktok
            - youtube
          uniqueItems: true
        description: Platforms to include (comma-separated).
        explode: false
        style: form
      - name: username
        in: query
        required: false
        schema:
          type: string
        description: Creator username substring (case-insensitive).
      tags:
      - Creator
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedCreatorList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: cr_pfqPv0FLsBRimHfi
                      username: zelfai
                      name: Zelf
                      email: example@zelflive.com
                      platform: instagram
                      platform_handle: zelfai
                      follower_count: 53120
                      active_content_count: 217
                      profile_bio: Creator and content intelligence for modern brands.
                      platform_url: https://www.instagram.com/zelfai
                      profile_picture_url: https://cdn.hellozelf.com/avatars/zelfai.png
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/creator/vetting/:
    post:
      operationId: creator_vetting.create
      description: |-
        `creator_vetting.create`

        Vets a creator identified by `profile_url`, retrieving the profile and scoring its safety and brand fit. Runs complete asynchronously, so this method returns immediately with the created vetting.

        The vetting analyzes up to 150 of the creator's posts per connection type, or the limit configured for your workspace.

        The created vetting is returned as the single item in `results`. Take its `id` and poll `creator_vetting.list` until a run reports `COMPLETED`. A vetting can take an hour or more to complete, so prefer the `creator_vetting.completed` webhook over frequent polling, which can trigger `RATE_LIMIT_EXCEEDED`. Once a run is complete, retrieve the payload from `creator_vetting.results`, passing `vetting_id=<id>`.
      summary: Create Creator Vetting
      tags:
      - Creator
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatorVettingRequest'
            examples:
              Example:
                value:
                  resync_profile: true
                  profile_url: https://www.instagram.com/zelfai
                  content_scope:
                    owned: true
                    repost: false
                    liked: false
                  safety_checks:
                  - type: ADULT_CONTENT
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/CreatorVettingRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/CreatorVettingRequest'
        required: true
      security:
      - ApiKey: []
      responses:
        '201':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/CreatorVetting'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: bod_krO77hRVsmbF1ihj
                      created_at: '2026-05-12T14:30:00Z'
                      updated_at: '2026-05-12T15:00:00Z'
                      creator_id: cr_pfqPv0FLsBRimHfi
                      status: COMPLETED
                      creator:
                        id: cr_pfqPv0FLsBRimHfi
                        username: zelfai
                        name: Zelf
                        email: example@zelflive.com
                        platform: instagram
                        platform_handle: zelfai
                        follower_count: 53120
                        active_content_count: 217
                        profile_bio: Creator and content intelligence for modern brands.
                        platform_url: https://www.instagram.com/zelfai
                        profile_picture_url: https://cdn.hellozelf.com/avatars/zelfai.png
                      type: CREATOR_ANALYSIS
                      sync_job_id: csj_qtQeeG2PjW6ua1rX
                      analysis:
                      - id: ban_xJZFbcWNkF9M3brf
                        scope: CONTENT
                        status: COMPLETED
                      safety_checks:
                      - type: ADULT_CONTENT
                      content_scope:
                        owned: true
                        repost: false
                        liked: false
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    get:
      operationId: creator_vetting.list
      description: |-
        `creator_vetting.list`

        Returns creator vettings ordered newest first, each with its analysis runs and their current `status`. Use it to discover vettings and monitor run progress without retrieving the full result payload.

        A vetting `id` corresponds to the `vetting_id` parameter on `creator_vetting.results` and `creator_vetting.contents`, and each run's `id` corresponds to the `id` parameter on `creator_vetting.results`. The run `result` download is not included in this response; retrieve it from `creator_vetting.results` once a run reports `COMPLETED`. Possible statuses are `IN_PROGRESS`, `COMPLETED`, `EMPTY_RESULT`, and `FAILED`. Supply `creator_id` to filter to a single creator, or `id` to return a single vetting.
      summary: List Creator Vettings
      parameters:
      - in: query
        name: creator_id
        schema:
          type: string
        description: Identifier of a creator, obtained from `creators.list`. When
          supplied, only vetting analyses tied to that creator are returned. Returns
          `INVALID_ID` if the value is not a valid creator identifier.
      - in: query
        name: id
        schema:
          type: string
        description: Identifier of a vetting. When supplied, only that vetting is
          returned. Returns `INVALID_ID` if the value is not valid.
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      tags:
      - Creator
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedCreatorVettingList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: bod_krO77hRVsmbF1ihj
                      created_at: '2026-05-12T14:30:00Z'
                      updated_at: '2026-05-12T15:00:00Z'
                      creator_id: cr_pfqPv0FLsBRimHfi
                      status: COMPLETED
                      creator:
                        id: cr_pfqPv0FLsBRimHfi
                        username: zelfai
                        name: Zelf
                        email: example@zelflive.com
                        platform: instagram
                        platform_handle: zelfai
                        follower_count: 53120
                        active_content_count: 217
                        profile_bio: Creator and content intelligence for modern brands.
                        platform_url: https://www.instagram.com/zelfai
                        profile_picture_url: https://cdn.hellozelf.com/avatars/zelfai.png
                      type: CREATOR_ANALYSIS
                      sync_job_id: csj_qtQeeG2PjW6ua1rX
                      analysis:
                      - id: ban_xJZFbcWNkF9M3brf
                        scope: CONTENT
                        status: COMPLETED
                      safety_checks:
                      - type: ADULT_CONTENT
                      content_scope:
                        owned: true
                        repost: false
                        liked: false
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/creator/vetting/results/:
    get:
      operationId: creator_vetting.results
      description: |-
        `creator_vetting.results`

        Returns the `result` for each run: a signed, time-limited download of the vetting analysis payload. Call this method after a run reaches `COMPLETED`; for earlier statuses the row is returned with a `null` `result`.

        Supply `vetting_id` (the vetting `id` from `creator_vetting.list`) to return every run of that vetting, or `id` (a single run's `id`) to return one run. One of the two is required. A malformed identifier returns `INVALID_ID`; omitting both returns `VALIDATION_ERROR`.

        **Disclosures.** When the vetting requested `DISCLOSURE`, the payload's `insight_json.disclosures` holds one entry per analyzed video: its `content_id` and a `result`. The `result` lists `evidence` items, each with the `source` it was found in (`frame` for on-screen text, `audio` for the speech transcription, `title`, `caption` or `metadata`), a `kind` (`sponsorship`, `affiliate`, `gifted` or `relationship` for a commercial relationship; `terms` or `risk` for offer conditions and warnings), the quoted `text` and, where known, `time_seconds`. It also gives each source a `channels` status: `present`, `absent` or `unknown` (not checked). Disclosures are not counted as violations.

        Limits: only videos up to 10 minutes are checked; on-screen text is read from sampled frames, so text shown only briefly can be missed; videos Zelf analyzed before the request are not re-analyzed for disclosures. An empty `evidence` list does not confirm that a video has no disclosure. Detection reports what was stated; it does not assess legal compliance.
      summary: Get Creator Vetting Results
      parameters:
      - in: query
        name: id
        schema:
          type: string
        description: Identifier of a single analysis. Returns only that analysis.
          Required unless `vetting_id` is supplied. Returns `INVALID_ID` if malformed.
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      - in: query
        name: vetting_id
        schema:
          type: string
        description: Identifier of the vetting, obtained from `creator_vetting.list`.
          Returns every analysis of that vetting. Required unless `id` is supplied.
          Returns `INVALID_ID` if malformed.
      tags:
      - Creator
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedCreatorVettingResultList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: ban_xJZFbcWNkF9M3brf
                      scope: CONTENT
                      created_at: '2026-05-12T14:30:00Z'
                      modified_at: '2026-05-12T15:00:00Z'
                      vetting_id: bod_krO77hRVsmbF1ihj
                      status: COMPLETED
                      result:
                        download_url: https://storage.googleapis.com/zelf-webhook-snapshots/ai_analysis/run_8f2c.json?X-Goog-Signature=...
                        expires_at: 1718000000
                        format: json
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/creator/vetting/contents/:
    get:
      operationId: creator_vetting.contents
      description: |-
        `creator_vetting.contents`

        Returns the content tied to a creator vetting, in the standard content shape. Use it to inspect the specific posts behind a vetting.

        Requires `vetting_id` (a vetting `id` from `creator_vetting.list`). Brand-scoped fields (`is_owned`, `is_tagged`, `net_promoter_score`, ...) are `null` in this response.
      summary: List Creator Vetting Content
      parameters:
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      - in: query
        name: vetting_id
        schema:
          type: string
        description: Identifier of the vetting, obtained from `creator_vetting.list`.
        required: true
      tags:
      - Creator
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedContentList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: con_DZaWwnKo5HThZ9bu
                      platform_post_id: '7376543210987654321'
                      platform: instagram
                      content_form: VIDEO
                      thumbnail_url: https://cdn.hellozelf.com/thumbnails/zelfai-launch.png
                      posted_at: '2026-05-12T16:00:00Z'
                      view_count: 184230
                      like_count: 12840
                      comment_count: 932
                      share_count: 415
                      total_engagement: 14187
                      language: en
                      media_duration: 42.0
                      engagement_rate: 0.077
                      earned_media_value_cents: 248000
                      creator:
                        id: cr_pfqPv0FLsBRimHfi
                        username: zelfai
                        name: Zelf
                        platform: instagram
                        platform_handle: zelfai
                        platform_url: https://www.instagram.com/zelfai
                        profile_picture_url: https://cdn.hellozelf.com/avatars/zelfai.png
                        profile_bio: Creator and content intelligence for modern brands.
                        follower_count: 53120
                        active_content_count: 217
                      brand_id: brd_Lw6ZZmNYhW0P9k0x
                      net_promoter_score: 1
                      is_owned: true
                      is_tagged: true
                      is_influencer: true
                      mention_type: content
                      caption: Behind the scenes at Zelf.
                      description: How Zelf turns social signals into creator insights.
                      transcript: We walk through how Zelf ranks creators by brand
                        fit.
                      platform_url: https://www.instagram.com/zelfai
                      last_keyword: zelf creators
                      ai_summary: A product walkthrough of Zelf creator analytics.
                      hashtags:
                      - zelfai
                      - creatoreconomy
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/creator/vetting/contents/statistics/:
    get:
      operationId: creator_vetting.content_statistics
      description: |-
        `creator_vetting.content_statistics`

        Returns aggregate totals (post count, views, engagement, earned media value, and unique creators) over the content tied to a creator vetting.

        Requires `vetting_id` (a vetting `id` from `creator_vetting.list`) and returns the same totals shape as `content.statistics`.
      summary: Get Creator Vetting Content Statistics
      parameters:
      - in: query
        name: vetting_id
        schema:
          type: string
        description: Identifier of the vetting, obtained from `creator_vetting.list`.
        required: true
      tags:
      - Creator
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/ContentStatistics'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - post_count: 1280
                      total_views: 4500000
                      total_likes: 320000
                      total_comments: 18000
                      total_shares: 9500
                      total_engagement: 347500
                      total_earned_media_value_cents: 12500000
                      total_media_duration: 53400.0
                      engagement_rate: 0.077
                      unique_creator_count: 412
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/creator_sync/:
    post:
      operationId: creator_sync.create
      description: |-
        `creator_sync.create`

        Add a creator's profile and posts to Zelf so their videos can be analyzed. Send the creator's `profile_url`. Zelf reads and stores the profile and the creator's own posts, which then appear in `creators.list` and `content.list`.

        Syncing is asynchronous. This method returns immediately with the sync at `status: SYNCING`. Poll `creator_sync.get` until `status` is `SUCCESS` or `FAILED`.

        Only the creator's own posts are synced. Reposts and posts they are tagged in are not. Synced content is not attached to your brand, so it does not appear in brand-scoped totals.
      summary: Create Creator Sync
      tags:
      - Creator Sync
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatorSyncCreateRequest'
            examples:
              Example:
                value:
                  profile_url: https://www.instagram.com/zelfai
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/CreatorSyncCreateRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/CreatorSyncCreateRequest'
        required: true
      security:
      - ApiKey: []
      responses:
        '201':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/CreatorSync'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: csj_qtQeeG2PjW6ua1rX
                      created_at: '2026-05-12T14:30:00Z'
                      updated_at: '2026-05-12T15:00:00Z'
                      creator_id: cr_pfqPv0FLsBRimHfi
                      profile_url: https://www.instagram.com/zelfai
                      status: SYNCING
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    get:
      operationId: creator_sync.list
      description: |-
        `creator_sync.list`

        Returns creator sync jobs ordered newest first, each with its current `status`. Use it to monitor progress after `creator_sync.create`.

        Possible statuses are `SCHEDULED`, `SYNCING`, `SUCCESS` and `FAILED`. A job's `creator_id` is `null` until the creator has been resolved, and then corresponds to the `id` filter on `creators.list`. Syncs started by `creator_vetting` are listed here too; a vetting reports its own sync as `sync_job_id`.
      summary: List Creator Syncs
      parameters:
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      tags:
      - Creator Sync
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedCreatorSyncList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: csj_qtQeeG2PjW6ua1rX
                      created_at: '2026-05-12T14:30:00Z'
                      updated_at: '2026-05-12T15:00:00Z'
                      creator_id: cr_pfqPv0FLsBRimHfi
                      profile_url: https://www.instagram.com/zelfai
                      status: SYNCING
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/creator_sync/{id}/:
    get:
      operationId: creator_sync.get
      description: |-
        `creator_sync.get`

        Returns a single creator sync job identified by `id`, the value returned by `creator_sync.create` or reported by a vetting as `sync_job_id`. Poll it until `status` reports `SUCCESS` or `FAILED`.
      summary: Get Creator Sync
      parameters:
      - in: path
        name: id
        schema:
          type: string
        required: true
      tags:
      - Creator Sync
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/CreatorSync'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: csj_qtQeeG2PjW6ua1rX
                      created_at: '2026-05-12T14:30:00Z'
                      updated_at: '2026-05-12T15:00:00Z'
                      creator_id: cr_pfqPv0FLsBRimHfi
                      profile_url: https://www.instagram.com/zelfai
                      status: SYNCING
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/detection/:
    post:
      operationId: detection.create
      description: |-
        `detection.create`

        Detects a list of brands in one video. Zelf Detection reads the whole video and finds each scene where a brand is identifiable: its logo or name is visible (on a product, on packaging or in on-screen text) or the brand is named out loud. Every appearance (placement) comes back with its timestamp, what was detected and how confident the detection is. Send the brands in `context.hints.brands`.

        Identify the video with `content_id` (a video already in Zelf) or `content_url` (a link to any supported video). Sending both, or neither, returns `VALIDATION_ERROR`. A `content_url` that is not in Zelf yet is added first, which makes the detection take longer.

        **Limits**

        - One video per request, up to 90 minutes. YouTube, TikTok and Instagram videos. Photo posts are not supported.
        - A product with no visible logo or name, and no spoken mention, is not detected.
        - `content_url`: the full video link, not a short link (`vm.tiktok.com`, `vt.tiktok.com`, `tiktok.com/t/`). Supported forms: `youtube.com/watch?v=<id>`, `youtu.be/<id>`, `youtube.com/shorts/<id>`, `tiktok.com/@<handle>/video/<id>`, `instagram.com/reel/<id>`, `/p/<id>`, `/tv/<id>`.
        - `context.hints.brands`: 1 to 200 brands. No two names that differ only by punctuation or digits.
        - `context.hints.brands[].name`: up to 60 characters.
        - `context.hints.brands[].identifiers`: 2 to 10, each 2 to 200 characters, including the brand name.
        - `context.hints.brands[].negatives`: optional, up to 12, each up to 200 characters.

        A request that breaks a rule returns `VALIDATION_ERROR` for the first rule it breaks. `error.details.field` names the field, for example `context.hints.brands[3].identifiers`, and `error.details.code` is one of `INVALID_REQUEST`, `NO_BRANDS`, `TOO_MANY_BRANDS`, `INVALID_BRAND`, `IDENTIFIER_COUNT`, `IDENTIFIER_TOO_SHORT`, `UNSUPPORTED_URL`, `SHORT_LINK`, `PHOTO_POST` or `VIDEO_TOO_LONG`.

        Detection is asynchronous. This method returns immediately with the detection at `status: SCHEDULED`. Poll `detection.get` until `status` is `SUCCESS` or `FAILED`; both are final.
      summary: Detect brands in a video
      tags:
      - Detection
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DetectionCreateRequest'
            examples:
              ByContentUrl:
                value:
                  content_url: https://www.youtube.com/watch?v=PpAPhxsuCJw
                  context:
                    hints:
                      brands:
                      - name: Nike
                        identifiers:
                        - Nike
                        - Jordan
                        - Air Force 1
                        negatives:
                        - Jordan (the country)
                      - name: Adidas
                        identifiers:
                        - Adidas
                        - Samba
                        negatives: []
                summary: By content url
              ByContentId:
                value:
                  content_id: con_DZaWwnKo5HThZ9bu
                  context:
                    hints:
                      brands:
                      - name: Nike
                        identifiers:
                        - Nike
                        - Jordan
                        - Air Force 1
                        negatives:
                        - Jordan (the country)
                      - name: Adidas
                        identifiers:
                        - Adidas
                        - Samba
                        negatives: []
                summary: By content id
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/DetectionCreateRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/DetectionCreateRequest'
        required: true
      security:
      - ApiKey: []
      responses:
        '201':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/Detection'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Scheduled:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: aer_TqNpNLkEfxbokqbD
                      status: SCHEDULED
                      context:
                        external_url: https://www.youtube.com/watch?v=PpAPhxsuCJw
                        hints:
                          brands:
                          - name: Nike
                            identifiers:
                            - Nike
                            - Jordan
                            - Air Force 1
                            negatives:
                            - Jordan (the country)
                          - name: Adidas
                            identifiers:
                            - Adidas
                            - Samba
                            negatives: []
                      result: null
                      created_at: '2026-05-12T14:30:00Z'
                      updated_at: '2026-05-12T14:30:00Z'
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    get:
      operationId: detection.list
      description: |-
        `detection.list`

        Lists detections, newest first. Each detection has its current `status` and, once finished, its placements in `result`. Possible statuses are `SCHEDULED`, `IN_PROGRESS`, `SUCCESS` and `FAILED`. `SUCCESS` and `FAILED` are final.
      summary: List detections and their status
      parameters:
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      tags:
      - Detection
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedDetectionList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: aer_TqNpNLkEfxbokqbD
                      status: SUCCESS
                      context:
                        external_url: https://www.youtube.com/watch?v=PpAPhxsuCJw
                        hints:
                          brands:
                          - name: Nike
                            identifiers:
                            - Nike
                            - Jordan
                            - Air Force 1
                            negatives:
                            - Jordan (the country)
                          - name: Adidas
                            identifiers:
                            - Adidas
                            - Samba
                            negatives: []
                      result:
                        video:
                          video_url: https://www.youtube.com/watch?v=PpAPhxsuCJw
                          platform: youtube
                          duration_s: 65.1
                        summary:
                          brands_requested: 2
                          brands_found: 1
                          placements: 2
                          confirmed: 1
                          review: 1
                        brands:
                        - brand_name: Nike
                          found: true
                          summary:
                            placements: 2
                            confirmed: 1
                            review: 1
                          placements:
                          - timestamp: 00:12-00:15
                            video_url_at_timestamp: https://www.youtube.com/watch?v=PpAPhxsuCJw&t=7s
                            modality: Visual
                            what_detected: Nike swoosh on the left shoe
                            reasoning: The swoosh is fully visible on the left shoe.
                            confidence: confirmed
                          - timestamp: 00:34-00:37
                            video_url_at_timestamp: https://www.youtube.com/watch?v=PpAPhxsuCJw&t=29s
                            modality: Audio
                            what_detected: Spoken word 'Nike'
                            reasoning: The presenter says 'Nike' while introducing
                              the hoop.
                            confidence: review
                        - brand_name: Adidas
                          found: false
                          summary:
                            placements: 0
                            confirmed: 0
                            review: 0
                          placements: []
                      created_at: '2026-05-12T14:30:00Z'
                      updated_at: '2026-05-12T15:00:00Z'
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/detection/{id}/:
    get:
      operationId: detection.get
      description: |-
        `detection.get`

        Retrieve one detection by `id`, the value returned by `detection.create`. Poll it until `status` is `SUCCESS` or `FAILED`; both are final. `result` is `null` until then. On `FAILED`, `result` carries the counts and `brands: []`.

        **Reading `result`**

        - `brands`: one entry per requested brand, in request order. `found: false` means the brand was searched for and does not appear in the video.
        - `timestamp`: `mm:ss-mm:ss`, or `mm:ss` for a single moment. Past an hour the minutes keep counting (`69:23-69:25`).
        - `modality`: `Visual` (logo or product on screen), `Text` (brand name written on screen) or `Audio` (brand name spoken).
        - `confidence`: `confirmed` means the placement passed a final check against the video's frames; `review` means it was detected but did not pass that final frame check. `Audio` placements are always `review`, because a spoken mention has no frames to check. On `Visual` and `Text` placements, `review` means the final frame check did not confirm the placement.
        - `video_url_at_timestamp`: YouTube only, opens the video 5 seconds before the placement. `null` on TikTok and Instagram.
      summary: Retrieve a detection and its placements
      parameters:
      - in: path
        name: id
        schema:
          type: string
        required: true
      tags:
      - Detection
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/Detection'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - id: aer_TqNpNLkEfxbokqbD
                      status: SUCCESS
                      context:
                        external_url: https://www.youtube.com/watch?v=PpAPhxsuCJw
                        hints:
                          brands:
                          - name: Nike
                            identifiers:
                            - Nike
                            - Jordan
                            - Air Force 1
                            negatives:
                            - Jordan (the country)
                          - name: Adidas
                            identifiers:
                            - Adidas
                            - Samba
                            negatives: []
                      result:
                        video:
                          video_url: https://www.youtube.com/watch?v=PpAPhxsuCJw
                          platform: youtube
                          duration_s: 65.1
                        summary:
                          brands_requested: 2
                          brands_found: 1
                          placements: 2
                          confirmed: 1
                          review: 1
                        brands:
                        - brand_name: Nike
                          found: true
                          summary:
                            placements: 2
                            confirmed: 1
                            review: 1
                          placements:
                          - timestamp: 00:12-00:15
                            video_url_at_timestamp: https://www.youtube.com/watch?v=PpAPhxsuCJw&t=7s
                            modality: Visual
                            what_detected: Nike swoosh on the left shoe
                            reasoning: The swoosh is fully visible on the left shoe.
                            confidence: confirmed
                          - timestamp: 00:34-00:37
                            video_url_at_timestamp: https://www.youtube.com/watch?v=PpAPhxsuCJw&t=29s
                            modality: Audio
                            what_detected: Spoken word 'Nike'
                            reasoning: The presenter says 'Nike' while introducing
                              the hoop.
                            confidence: review
                        - brand_name: Adidas
                          found: false
                          summary:
                            placements: 0
                            confirmed: 0
                            review: 0
                          placements: []
                      created_at: '2026-05-12T14:30:00Z'
                      updated_at: '2026-05-12T15:00:00Z'
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/usage/:
    get:
      operationId: usage.retrieve
      description: |-
        Live consumption for your brand, per group and per window.

        Both sections come back however you call: `api` groups count requests made with an API key, `mcp` groups count the ones an AI client makes, and the two never share a counter. `authenticated_as` says which of them the current credentials spend.

        A window appears only when a limit is configured for it, so a brand with no monthly limit has no monthly row. Ask an administrator to set one and it appears here with no further work.

        `reset_in` counts down to the end of the current window, which starts on its first request rather than on the clock. Reading this consumes no quota.
      summary: Get Usage
      tags:
      - Usage
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/Usage'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
              examples:
                Example:
                  value:
                    log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    timestamp: '2026-01-01T00:00:00Z'
                    results:
                    - brand_id: brd_Lw6ZZmNYhW0P9k0x
                      authenticated_as: api_key
                      scopes:
                      - scope: global
                        human_name: General
                        kind: request
                        section: api
                        windows:
                        - window: rps
                          limit: 100
                          used: 10
                          remaining: 90
                          reset_in: 1
                        - window: rph
                          limit: 5000
                          used: 500
                          remaining: 4500
                          reset_in: 1800
                        - window: rpd
                          limit: 10000
                          used: 1000
                          remaining: 9000
                          reset_in: 43200
                      - scope: ai_analysis
                        human_name: AI Analysis
                        kind: request
                        section: api
                        windows:
                        - window: rps
                          limit: 10
                          used: 1
                          remaining: 9
                          reset_in: 1
                        - window: rph
                          limit: 1000
                          used: 100
                          remaining: 900
                          reset_in: 1800
                        - window: rpd
                          limit: 5000
                          used: 500
                          remaining: 4500
                          reset_in: 43200
                      - scope: data_records
                        human_name: Data Records
                        kind: item
                        section: api
                        windows:
                        - window: items_per_month
                          limit: 10000
                          used: 1000
                          remaining: 9000
                          reset_in: 1296000
                      - scope: mcp
                        human_name: General
                        kind: request
                        section: mcp
                        windows:
                        - window: rps
                          limit: 20
                          used: 2
                          remaining: 18
                          reset_in: 1
                        - window: rph
                          limit: 10000
                          used: 1000
                          remaining: 9000
                          reset_in: 1800
                        - window: rpd
                          limit: 25000
                          used: 2500
                          remaining: 22500
                          reset_in: 43200
                      - scope: mcp.ai_analysis
                        human_name: AI Analysis
                        kind: request
                        section: mcp
                        windows:
                        - window: rps
                          limit: 5
                          used: 1
                          remaining: 4
                          reset_in: 1
                        - window: rph
                          limit: 2500
                          used: 250
                          remaining: 2250
                          reset_in: 1800
                        - window: rpd
                          limit: 10000
                          used: 1000
                          remaining: 9000
                          reset_in: 43200
                      - scope: mcp.data_records
                        human_name: Data Records
                        kind: item
                        section: mcp
                        windows:
                        - window: items_per_month
                          limit: 10000
                          used: 1000
                          remaining: 9000
                          reset_in: 1296000
                      history:
                        start_date: '2026-05-01'
                        end_date: '2026-10-21'
                        days:
                        - date: '2026-10-20'
                          source: api
                          general: 4820
                          ai_analysis: 48
                          data_records: 4772
                          data_records_items: 143160
                        - date: '2026-10-21'
                          source: mcp
                          general: 311
                          ai_analysis: 4
                          data_records: 307
                          data_records_items: 9210
                    item_count: 1
                    page: 1
                    next: null
                    previous: null
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/webhooks/subscriptions/:
    post:
      operationId: webhooks.create_subscription
      description: |-
        `webhooks.create_subscription`

        Creates a webhook subscription for your brand. The full signing `secret` is returned only once, in this response. A brand may hold at most one `event` subscription.
      summary: Create Webhook Subscription
      tags:
      - Webhook Management
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookSubscriptionCreateRequest'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/WebhookSubscriptionCreateRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/WebhookSubscriptionCreateRequest'
        required: true
      security:
      - ApiKey: []
      responses:
        '201':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookSubscriptionCreated'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '409':
          description: The request conflicts with the resource's current state.
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    get:
      operationId: webhooks.list_subscriptions
      description: |-
        `webhooks.list_subscriptions`

        Returns the webhook subscriptions owned by your brand, newest first.
      summary: List Webhook Subscriptions
      parameters:
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedWebhookSubscriptionList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/webhooks/subscriptions/{subscription_id}/:
    get:
      operationId: webhooks.get_subscription
      description: |-
        `webhooks.get_subscription`

        Returns a single subscription.
      summary: Get Webhook Subscription
      parameters:
      - in: path
        name: subscription_id
        schema:
          type: string
        required: true
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookSubscription'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    patch:
      operationId: webhooks.update_subscription
      description: |-
        `webhooks.update_subscription`

        Partially updates a subscription's `url`, `custom_headers`, `resource_filters`, `interval_hours`, `max_items`, or `enabled`. `kind`, `resource`, and `event_types` are immutable after create.
      summary: Update Webhook Subscription
      parameters:
      - in: path
        name: subscription_id
        schema:
          type: string
        required: true
      tags:
      - Webhook Management
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchedWebhookSubscriptionUpdateRequest'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/PatchedWebhookSubscriptionUpdateRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PatchedWebhookSubscriptionUpdateRequest'
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookSubscription'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    delete:
      operationId: webhooks.delete_subscription
      description: |-
        `webhooks.delete_subscription`

        Deletes a subscription. This is destructive: the subscription stops receiving deliveries.
      summary: Delete Webhook Subscription
      parameters:
      - in: path
        name: subscription_id
        schema:
          type: string
        required: true
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookDeleteAcknowledgement'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/webhooks/subscriptions/{subscription_id}/rotate_secret/:
    post:
      operationId: webhooks.rotate_subscription_secret
      description: |-
        `webhooks.rotate_subscription_secret`

        Mints a new signing secret for the subscription and returns the full `secret` once. Deliveries after this use the new secret.
      summary: Rotate Webhook Subscription Secret
      parameters:
      - in: path
        name: subscription_id
        schema:
          type: string
        required: true
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookSubscriptionCreated'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/webhooks/subscriptions/{subscription_id}/send_test/:
    post:
      operationId: webhooks.send_test
      description: |-
        `webhooks.send_test`

        Queues a test event to the subscription's URL so you can verify signing and parsing. Rate limited to 12 per hour per subscription. A disabled subscription is rejected.
      summary: Send Test Webhook
      parameters:
      - in: path
        name: subscription_id
        schema:
          type: string
        required: true
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '202':
          description: Test delivery queued.
        '409':
          description: The request conflicts with the resource's current state.
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/webhooks/secrets/:
    post:
      operationId: webhooks.create_secret
      description: |-
        `webhooks.create_secret`

        Creates a signing secret for the brand. The full `secret` is returned only once.
      summary: Create Webhook Secret
      tags:
      - Webhook Management
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookSecretCreateRequest'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/WebhookSecretCreateRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/WebhookSecretCreateRequest'
      security:
      - ApiKey: []
      responses:
        '201':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookSecretCreated'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    get:
      operationId: webhooks.list_secrets
      description: |-
        `webhooks.list_secrets`

        Lists the brand's active secrets.
      summary: List Webhook Secrets
      parameters:
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedWebhookSecretList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/webhooks/secrets/{secret_id}/:
    get:
      operationId: webhooks.get_secret
      description: |-
        `webhooks.get_secret`

        Returns a single secret.
      summary: Get Webhook Secret
      parameters:
      - in: path
        name: secret_id
        schema:
          type: string
        required: true
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookSecret'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
    delete:
      operationId: webhooks.delete_secret
      description: |-
        `webhooks.delete_secret`

        Deactivates a secret. The default secret, or one still linked to a subscription, cannot be deleted.
      summary: Delete Webhook Secret
      parameters:
      - in: path
        name: secret_id
        schema:
          type: string
        required: true
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookDeleteAcknowledgement'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '409':
          description: The request conflicts with the resource's current state.
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/webhooks/secrets/{secret_id}/set_default/:
    post:
      operationId: webhooks.set_default_secret
      description: |-
        `webhooks.set_default_secret`

        Marks a secret as the brand's default for its kind.
      summary: Set Default Webhook Secret
      parameters:
      - in: path
        name: secret_id
        schema:
          type: string
        required: true
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookSecret'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/webhooks/events/:
    get:
      operationId: webhooks.list_events
      description: |-
        `webhooks.list_events`

        Lists the event types an event subscription can listen for.
      summary: List Available Webhook Events
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookAvailableEvent'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/webhooks/deliveries/:
    get:
      operationId: webhooks.list_deliveries
      description: |-
        `webhooks.list_deliveries`

        Lists webhook delivery attempts for the brand, newest first. Filter by `subscription` (masked id), `state`, and a `created_after` / `created_before` window.
      summary: List Webhook Deliveries
      parameters:
      - name: created
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: Delivery creation time (ISO 8601).
      - name: created_after
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: Delivery creation time (ISO 8601). (Inclusive Lower Bound)
      - name: created_before
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: Delivery creation time (ISO 8601). (Inclusive Upper Bound)
      - name: page
        required: false
        in: query
        description: Page number (1-based). Omit for the first page.
        schema:
          type: integer
          minimum: 1
          default: 1
      - name: page_size
        required: false
        in: query
        description: Items per page (1-30). Defaults to 30; larger values are clamped
          down.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
      - name: sort_by
        in: query
        required: false
        schema:
          type: array
          items:
            type: string
            enum:
            - created
            - -created
          uniqueItems: true
        explode: false
        style: form
      - name: state
        in: query
        required: false
        schema:
          type: string
          enum:
          - PENDING
          - SUCCEEDED
          - FAILED
          - EXHAUSTED
          - QUOTA_EXHAUSTED
        description: Current delivery state.
      - name: state!
        in: query
        required: false
        schema:
          type: string
          enum:
          - PENDING
          - SUCCEEDED
          - FAILED
          - EXHAUSTED
          - QUOTA_EXHAUSTED
        description: exclude where state - Current delivery state.
      - in: query
        name: subscription
        schema:
          type: string
        description: Scope to a single subscription by its masked id.
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    $ref: '#/components/schemas/PaginatedWebhookDeliveryList'
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
  /api/v1/webhooks/deliveries/{delivery_id}/:
    get:
      operationId: webhooks.get_delivery
      description: |-
        `webhooks.get_delivery`

        Returns a single delivery attempt.
      summary: Get Webhook Delivery
      parameters:
      - in: path
        name: delivery_id
        schema:
          type: string
        required: true
      tags:
      - Webhook Management
      security:
      - ApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  log_id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookDelivery'
                    maxItems: 1
                  item_count:
                    type: integer
                  page:
                    type: integer
                  next:
                    type:
                    - integer
                    - 'null'
                  previous:
                    type:
                    - integer
                    - 'null'
                required:
                - log_id
                - timestamp
                - results
                - item_count
                - page
                - next
                - previous
          description: ''
        '400':
          description: Validation failed or a supplied id is malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: The `x-api-key` header is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '403':
          description: The api key cannot access the requested brand or resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error403'
        '404':
          description: The resource does not exist or is outside this key's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '406':
          description: The requested response media type cannot be served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error406'
        '429':
          description: A rate-limit quota was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error429'
        '500':
          description: Something went wrong on our side; include `log_id` when reporting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
        '503':
          description: The request took too long and was stopped; narrow the window
            or pass `brand_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error503'
components:
  schemas:
    AccountTypeEnum:
      enum:
      - tracked
      - influencer
      - owned
      type: string
      x-internal: true
    AiAnalysis:
      type: object
      description: Response body for the AI analysis root.
      properties:
        id:
          type: string
          readOnly: true
          description: ID of the analysis. Use this value on follow-up calls.
        analysis_type:
          type: string
          readOnly: true
          description: Always `AI_ANALYSIS` here.
        name:
          type: string
          description: The analysis label.
        topic:
          type:
          - string
          - 'null'
          description: The subject the analysis focuses on.
        created_at:
          type: string
          format: date-time
          description: When the analysis was created (UTC, ISO-8601).
        modified_at:
          type: string
          format: date-time
          description: When the analysis was last modified (UTC, ISO-8601).
        analyses:
          type: array
          items:
            $ref: '#/components/schemas/AiAnalysisRun'
          description: The runs that make up this analysis, one per scope, ordered
            newest first.
      required:
      - analyses
      - analysis_type
      - created_at
      - id
      - modified_at
      - name
      - topic
    AiAnalysisCreateRequest:
      type: object
      description: Request body for ``POST /api/v1/ai_analysis/``.
      properties:
        name:
          type: string
          minLength: 1
          description: A label for this analysis.
        topic:
          type: string
          minLength: 1
          description: 'The brand, product, person or event to analyze, as a short
            term. For example `Levi''s` or `Running shoes`. Keep it under five words.
            Do not write a question: every run reports sentiment, price, quality,
            competitors and products for the topic. To narrow the posts, use `content_filters`.'
        analysis_scope:
          type: array
          items:
            $ref: '#/components/schemas/AnalysisScopeEnum'
          default:
          - CONTENT
          description: 'What to analyze: any of `CONTENT`, `COMMENT`. Defaults to
            `["CONTENT"]`.'
        content_filters:
          allOf:
          - $ref: '#/components/schemas/ContentFilterInputRequest'
          description: Selects which content the analysis runs over. At least one
            of `search`, `creator_id`, `creator_username`, `external_id`, or `brand_id`
            is required to scope it. When both `posted_after` and `posted_before`
            are omitted, the analysis defaults to content from the last 7 days.
      required:
      - content_filters
      - name
      - topic
      x-internal: true
    AiAnalysisResult:
      type: object
      description: A single analysis run with its downloadable ``result``.
      properties:
        id:
          type: string
          readOnly: true
          description: ID of the analysis run.
        analysis_id:
          type: string
          readOnly: true
          description: ID of the analysis this run belongs to.
        scope:
          type: string
          description: 'What this run covers: `CONTENT` or `COMMENT`.'
        status:
          type: string
          readOnly: true
          description: 'Run status: one of `IN_PROGRESS`, `COMPLETED`, `EMPTY_RESULT`
            (finished with no matching content), or `FAILED`.'
        result:
          readOnly: true
          description: 'Signed, time-limited download of the run''s analysis payload:
            `download_url` (request it to retrieve the JSON), `expires_at` (Unix epoch
            seconds, approximately 24h TTL), and `format` (`json`). `null` until the
            run completes.'
          anyOf:
          - $ref: '#/components/schemas/ResultFile'
          - type: 'null'
        created_at:
          type: string
          format: date-time
          description: When the run was created (UTC, ISO-8601).
        modified_at:
          type: string
          format: date-time
          description: When the run was last modified (UTC, ISO-8601).
      required:
      - analysis_id
      - created_at
      - id
      - modified_at
      - scope
      - status
    AiAnalysisRun:
      type: object
      description: |-
        A single analysis run, without its result payload.

        The run ``result`` is not included; retrieve it from
        `ai_analysis.results` once ``status`` is `COMPLETED`.
      properties:
        id:
          type: string
          readOnly: true
          description: ID of the analysis run.
        scope:
          type: string
          description: 'What this run covers: `CONTENT` or `COMMENT`.'
        status:
          type: string
          readOnly: true
          description: 'Run status: one of `IN_PROGRESS`, `COMPLETED`, `EMPTY_RESULT`
            (finished with no matching content), or `FAILED`.'
      required:
      - id
      - scope
      - status
    AnalysisScopeEnum:
      enum:
      - CONTENT
      - COMMENT
      type: string
      x-internal: true
    AnalyticsBrand:
      type: object
      description: The brand a row of analytics belongs to.
      properties:
        id:
          type: string
          readOnly: true
          description: Unique identifier for the brand.
        title:
          type:
          - string
          - 'null'
          description: Display name of the brand.
        is_primary:
          type: boolean
          description: '`true` for the brand the request named, `false` for a sub-brand.'
        sub_brand_type:
          type:
          - string
          - 'null'
          readOnly: true
          description: '`COMPETITIVE` or `THEME` for a sub-brand; `null` for the top-level
            brand the request named.'
      required:
      - id
      - is_primary
      - sub_brand_type
      - title
    BlankEnum:
      enum:
      - ''
      x-internal: true
    Brand:
      type: object
      description: A brand you can reach.
      properties:
        id:
          type: string
          readOnly: true
          description: Unique identifier for the brand. Supply it wherever a `brand_id`
            accepts a brand id.
        title:
          type: string
          description: Display name of the brand.
        is_primary:
          type: boolean
          description: '`true` for the brand this access belongs to, and `false` for
            every other brand the request can access.'
        sub_brand_type:
          type:
          - string
          - 'null'
          readOnly: true
          description: '`COMPETITIVE` or `THEME` for a sub-brand; `null` for a top-level
            brand.'
        parent_brand_id:
          type:
          - string
          - 'null'
          readOnly: true
          description: Id of the parent brand for a sub-brand; `null` for a top-level
            brand.
        keywords:
          allOf:
          - $ref: '#/components/schemas/BrandKeywords'
          description: The terms this brand uses to monitor social platforms. All
            arrays are always present, though they may be empty, and are never null.
      required:
      - id
      - is_primary
      - keywords
      - parent_brand_id
      - sub_brand_type
      - title
    BrandAccount:
      type: object
      description: An account tracked, marked as an influencer, or owned by the brand.
      properties:
        username:
          type: string
          description: The account's handle.
        platform:
          type: string
          description: '`instagram`, `tiktok` or `youtube`.'
        external_url:
          type:
          - string
          - 'null'
          description: The account's profile URL, when available.
      required:
      - platform
      - username
    BrandAccountGroups:
      type: object
      description: |-
        A brand's accounts, grouped into one list per account type.

        Every key is optional because `account_type` narrows the response to the
        single list it names.
      properties:
        tracked:
          type: array
          items:
            $ref: '#/components/schemas/BrandAccount'
          description: Accounts the brand monitors.
        influencer:
          type: array
          items:
            $ref: '#/components/schemas/BrandAccount'
          description: Accounts the brand treats as partnership candidates.
        owned:
          type: array
          items:
            $ref: '#/components/schemas/BrandAccount'
          description: Profiles the brand operates itself.
    BrandAccountsWriteRequest:
      type: object
      description: Add or remove profiles in a brand's tracked/influencer/owned list.
      properties:
        brand_id:
          type: string
          minLength: 1
          description: Brand to modify, specified as an `id` from `brands.list`. Omit
            to modify the workspace brand.
        account_type:
          allOf:
          - $ref: '#/components/schemas/AccountTypeEnum'
          description: 'The list to modify: `tracked`, `influencer`, or `owned` for
            the brand''s own profiles.'
        profile_urls:
          type: array
          items:
            type: string
            format: uri
            minLength: 1
          description: Profile URLs on a supported platform (`instagram`, `tiktok`
            or `youtube`).
      required:
      - account_type
      - profile_urls
      x-internal: true
    BrandCatalog:
      type: object
      description: A brand in the curated catalog, readable by every api key.
      properties:
        id:
          type: string
          readOnly: true
          description: Unique identifier for the brand. Supply it as `brand_id` on
            the content and AI analysis endpoints to read this brand's content.
        title:
          type: string
          description: Display name of the brand.
        brand_logo:
          type:
          - string
          - 'null'
          description: URL of the brand's logo, when one is on file.
        industry_id:
          type:
          - string
          - 'null'
          description: Identifier of the brand's industry.
        industry_name:
          type:
          - string
          - 'null'
          description: Name of the brand's industry.
        category_id:
          type:
          - string
          - 'null'
          description: Identifier of the brand's category.
        category_name:
          type:
          - string
          - 'null'
          description: Name of the brand's category.
      required:
      - brand_logo
      - category_id
      - category_name
      - id
      - industry_id
      - industry_name
      - title
    BrandCatalogItem:
      type: object
      description: An industry or a category, used to filter the brand catalog.
      properties:
        id:
          type: string
          description: Identifier to pass as `industry_id` or `category_id` to `brands.catalog`.
        name:
          type: string
          description: Display name of the industry or category.
        level:
          type: string
          description: '`industry` or `category`.'
        parent_id:
          type:
          - string
          - 'null'
          description: Industry id a category belongs to; `null` for an industry.
      required:
      - id
      - level
      - name
      - parent_id
    BrandComparison:
      type: object
      description: One brand's performance in the comparison.
      properties:
        brand:
          allOf:
          - $ref: '#/components/schemas/AnalyticsBrand'
          description: The brand this row covers.
        statistics:
          allOf:
          - $ref: '#/components/schemas/BrandStatistics'
          description: Totals over the window.
        followers:
          allOf:
          - $ref: '#/components/schemas/BrandFollowers'
          description: Follower counts as they stand now, not over the window.
      required:
      - brand
      - followers
      - statistics
    BrandCreateSubBrandRequest:
      type: object
      description: Create a sub-brand under the workspace brand.
      properties:
        search_terms:
          type: array
          items:
            type: string
            minLength: 1
          description: Plain keywords the brand monitors across platforms, matched
            as-is (case-insensitive, no operators).
        inclusion_terms:
          type: array
          items:
            type: string
            minLength: 1
          description: Terms that must be present for content to match.
        exclusion_terms:
          type: array
          items:
            type: string
            minLength: 1
          description: Terms that disqualify content from matching.
        compound_inclusion_terms:
          type: array
          items:
            type: string
            minLength: 1
          description: Secondary inclusion terms layered on top of `inclusion_terms`.
        influencer_terms:
          type: array
          items:
            type: string
            minLength: 1
          description: Terms that mark matched content as influencer content.
        allowed_languages:
          type: array
          items:
            type: string
            minLength: 1
          description: ISO 639-1 codes to track, or the single value `all`. An empty
            array means all languages are tracked. A code outside ISO 639-1 is rejected,
            because a brand tracking it would match no content.
        boolean_query:
          type:
          - string
          - 'null'
          description: |-
            Full-text search across the caption, transcript, summary, and hashtags.

            **Syntax**
            - Multiple words are combined with AND: `nike running` matches content containing both `nike` and `running`.
            - `OR` (case-insensitive) matches either side: `nike OR adidas`.
            - `NOT` (case-insensitive) excludes a term: `running NOT marathon` matches `running` but not `marathon`.
            - Wrap a phrase in double quotes to match the words in order: `"summer collection"`.
            - Negate a phrase with `NOT`: `NOT "out of stock"`.
            - Group with parentheses to combine operators: `(nike OR adidas) running`.
            - `ONEAR/n` (case-insensitive) matches terms within `n` words of each other, in order: `apple ONEAR/2 watch` matches `apple watch` and `apple smart watch`, but not `watch ... apple`. It binds more tightly than `AND` and `OR`, so `apple ONEAR/2 watch AND cheap` is interpreted as `(apple near watch) AND cheap`.

            **Examples**
            - `nike running` - contains both terms.
            - `nike OR adidas` - contains either brand.
            - `"summer sale" NOT clearance` - the exact phrase, without `clearance`.
            - `(running OR cycling) NOT "beginner guide"` - either sport, excluding the phrase.
            - `apple ONEAR/2 watch` - `apple` within two words before `watch`.

            Content matching this query is connected to (associated with) this brand.
        title:
          type: string
          minLength: 1
          description: Display name of the new sub-brand.
        sub_brand_type:
          allOf:
          - $ref: '#/components/schemas/SubBrandTypeEnum'
          description: 'Type of sub-brand: `COMPETITIVE` or `THEME`.'
      required:
      - sub_brand_type
      - title
      x-internal: true
    BrandFollowers:
      type: object
      description: Followers across the handles a brand owns.
      properties:
        total:
          type: integer
          description: Followers across every owned handle.
        accounts:
          type: array
          items:
            $ref: '#/components/schemas/FollowerAccount'
          description: Per handle counts that add up to `total`.
      required:
      - accounts
      - total
    BrandKeywords:
      type: object
      description: The terms this brand uses to monitor social platforms.
      properties:
        search_terms:
          type: array
          items:
            type: string
          readOnly: true
          description: Plain keywords the brand monitors across platforms, matched
            as-is (case-insensitive, no operators).
        inclusion_terms:
          type: array
          items:
            type: string
          readOnly: true
          description: Terms that must be present for content to match.
        exclusion_terms:
          type: array
          items:
            type: string
          readOnly: true
          description: Terms that disqualify content from matching, even when other
            criteria are met.
        compound_inclusion_terms:
          type: array
          items:
            type: string
          readOnly: true
          description: Secondary inclusion terms layered on top of `inclusion_terms`.
            A match requires at least one term from `inclusion_terms` and at least
            one from `compound_inclusion_terms`.
        influencer_terms:
          type: array
          items:
            type: string
          readOnly: true
          description: Terms that mark matched content as influencer content (for
            example, `sponsored`, `ad`, or `gifted`).
        allowed_languages:
          type: array
          items:
            type: string
          readOnly: true
          description: ISO 639-1 codes for the languages the brand tracks. An empty
            array indicates that all languages are tracked.
        boolean_query:
          type:
          - string
          - 'null'
          readOnly: true
          description: |-
            Full-text search across the caption, transcript, summary, and hashtags.

            **Syntax**
            - Multiple words are combined with AND: `nike running` matches content containing both `nike` and `running`.
            - `OR` (case-insensitive) matches either side: `nike OR adidas`.
            - `NOT` (case-insensitive) excludes a term: `running NOT marathon` matches `running` but not `marathon`.
            - Wrap a phrase in double quotes to match the words in order: `"summer collection"`.
            - Negate a phrase with `NOT`: `NOT "out of stock"`.
            - Group with parentheses to combine operators: `(nike OR adidas) running`.
            - `ONEAR/n` (case-insensitive) matches terms within `n` words of each other, in order: `apple ONEAR/2 watch` matches `apple watch` and `apple smart watch`, but not `watch ... apple`. It binds more tightly than `AND` and `OR`, so `apple ONEAR/2 watch AND cheap` is interpreted as `(apple near watch) AND cheap`.

            **Examples**
            - `nike running` - contains both terms.
            - `nike OR adidas` - contains either brand.
            - `"summer sale" NOT clearance` - the exact phrase, without `clearance`.
            - `(running OR cycling) NOT "beginner guide"` - either sport, excluding the phrase.
            - `apple ONEAR/2 watch` - `apple` within two words before `watch`.

            Content matching this query is connected to (associated with) this brand.
      required:
      - allowed_languages
      - boolean_query
      - compound_inclusion_terms
      - exclusion_terms
      - inclusion_terms
      - influencer_terms
      - search_terms
    BrandStatistics:
      type: object
      description: Totals over the content matched to a brand in the window.
      properties:
        post_count:
          type: integer
          description: Number of posts matched to the brand.
        total_views:
          type: integer
          description: Total views across those posts.
        total_likes:
          type: integer
          description: Total likes across those posts.
        total_comments:
          type: integer
          description: Total comments across those posts.
        total_shares:
          type: integer
          description: Total shares across those posts.
        total_engagement:
          type: integer
          description: Likes, comments and shares across those posts.
        total_earned_media_value_cents:
          type: integer
          description: Estimated marketing value across those posts, in US cents.
        engagement_rate:
          type:
          - number
          - 'null'
          format: double
          description: Total engagement divided by total views. Null when the set
            has no recorded views.
        unique_creator_count:
          type: integer
          description: Number of distinct creators who posted.
        total_positive_nps:
          type: integer
          description: Engagement on positive posts.
        total_neutral_nps:
          type: integer
          description: Engagement on neutral posts.
        total_negative_nps:
          type: integer
          description: Engagement on negative posts.
      required:
      - engagement_rate
      - post_count
      - total_comments
      - total_earned_media_value_cents
      - total_engagement
      - total_likes
      - total_negative_nps
      - total_neutral_nps
      - total_positive_nps
      - total_shares
      - total_views
      - unique_creator_count
    Comment:
      type: object
      description: A comment left on a piece of content.
      properties:
        id:
          type: string
          readOnly: true
          description: Unique identifier for the comment.
        text:
          type: string
          description: The comment's text content.
        platform:
          type:
          - string
          - 'null'
          description: 'The platform the comment was posted on: `instagram`, `tiktok`
            or `youtube`.'
        like_count:
          type:
          - integer
          - 'null'
          description: Number of likes the comment has received.
        posted_at:
          type:
          - string
          - 'null'
          format: date-time
          description: Time the comment was posted on its platform (UTC).
        content_id:
          type:
          - string
          - 'null'
          readOnly: true
          description: Identifier of the content the comment was left on.
        parent_comment_id:
          type:
          - string
          - 'null'
          readOnly: true
          description: Identifier of the comment this is a reply to. Null for top-level
            comments.
        is_parent_comment:
          type: boolean
          readOnly: true
          description: Whether this is a top-level comment, rather than a reply to
            another comment.
        author:
          allOf:
          - $ref: '#/components/schemas/CommentAuthor'
          readOnly: true
          description: The creator who wrote the comment. Null when the author is
            unknown.
      required:
      - author
      - content_id
      - id
      - is_parent_comment
      - like_count
      - parent_comment_id
      - platform
      - posted_at
      - text
    CommentAuthor:
      type: object
      description: A short profile of the creator who wrote the comment.
      properties:
        id:
          type: string
          readOnly: true
          description: Unique identifier for the author.
        username:
          type:
          - string
          - 'null'
          description: The author's username or handle on the platform (without the
            leading `@`).
        name:
          type:
          - string
          - 'null'
          description: The author's display name on the platform.
        profile_picture_url:
          type:
          - string
          - 'null'
          readOnly: true
          description: URL of the author's profile picture.
      required:
      - id
      - name
      - profile_picture_url
      - username
    CommentCategory:
      type: object
      description: Comments the brand posted on one category of content.
      properties:
        category:
          type: string
          description: '`brand`, `owned`, `competitors`, `themes` or `other`. `other`
            covers content outside the brand''s own tree.'
        comments:
          type: integer
          description: Comments posted on this category.
        likes:
          type: integer
          description: Likes those comments received.
        replies:
          type: integer
          description: Replies those comments received.
      required:
      - category
      - comments
      - likes
      - replies
    CommentStatistics:
      type: object
      description: Comments the brand posted, split by the content they were posted
        on.
      properties:
        total_comments_posted:
          type: integer
          description: Comments the brand posted in the window, across every category.
        categories:
          type: array
          items:
            $ref: '#/components/schemas/CommentCategory'
          description: One entry per category. They add up to `total_comments_posted`.
      required:
      - categories
      - total_comments_posted
    Content:
      type: object
      description: A piece of content (a post, video, or image) and its metrics.
      properties:
        id:
          type: string
          readOnly: true
          description: Unique identifier for this piece of content. Use it to retrieve
            the content anywhere a content id is accepted.
        platform_post_id:
          type: string
          description: |-
            The post's identifier on its platform. This is the value at the end of the post's URL.

            Examples:
            - Instagram: `https://instagram.com/reel/abcd` -> `abcd`
            - TikTok: `https://www.tiktok.com/@username/video/12345678` -> `12345678`
        platform:
          type: string
          description: 'The platform the content was published on: `instagram`, `tiktok`
            or `youtube`.'
        content_form:
          type:
          - string
          - 'null'
          description: Whether the content is a video or an image.
        thumbnail_url:
          type:
          - string
          - 'null'
          description: URL of the content's thumbnail image.
        posted_at:
          type:
          - string
          - 'null'
          format: date-time
          description: Time the content was originally published on its platform (UTC).
        view_count:
          type:
          - integer
          - 'null'
          description: Total number of times the content has been viewed.
        like_count:
          type:
          - integer
          - 'null'
          description: Total number of likes the content has received.
        comment_count:
          type:
          - integer
          - 'null'
          description: Total number of comments on the content.
        share_count:
          type:
          - integer
          - 'null'
          description: Total number of times the content has been shared.
        total_engagement:
          type:
          - integer
          - 'null'
          description: Sum of the likes, comments, and shares the content has received.
        language:
          type:
          - string
          - 'null'
          description: ISO 639 code of the content's primary language.
        media_duration:
          type:
          - number
          - 'null'
          format: double
          description: Length of the media in seconds. Null when not known.
        engagement_rate:
          type:
          - number
          - 'null'
          format: double
          readOnly: true
          description: Engagement per view (likes plus comments plus shares, divided
            by views). Null when the content has no recorded views.
        earned_media_value_cents:
          type:
          - integer
          - 'null'
          readOnly: true
          description: Estimated marketing value of the content, in US cents.
        creator:
          allOf:
          - $ref: '#/components/schemas/ContentCreator'
          readOnly: true
          description: The creator who published the content.
        brand_id:
          type:
          - string
          - 'null'
          readOnly: true
          description: Identifier of the brand that the brand-specific signals on
            this row (`is_owned`, `is_tagged`, `is_influencer`, `mention_type`, `net_promoter_score`)
            are scoped to. Returned as `null` when the request omits `brand_id`.
        net_promoter_score:
          type:
          - integer
          - 'null'
          readOnly: true
          description: Net promoter score for the content from the perspective of
            the scoped brand. Returned as `null` when the request omits `brand_id`.
        is_owned:
          type:
          - boolean
          - 'null'
          readOnly: true
          description: Whether the scoped brand owns the content; that is, whether
            it was published by an account the brand controls. Returned as `null`
            when the request omits `brand_id`.
        is_tagged:
          type:
          - boolean
          - 'null'
          readOnly: true
          description: Whether the post tags the scoped brand on the platform. Returned
            as `null` when the request omits `brand_id`.
        is_influencer:
          type:
          - boolean
          - 'null'
          readOnly: true
          description: 'Whether the scoped brand counts the content as influencer
            content: it matches the brand''s influencer terms, comes from an account
            on its influencer list, was reported as branded content by the platform,
            or was marked by hand. Content the brand owns is never influencer content.
            Returned as `null` when the request omits `brand_id`.'
        mention_type:
          type:
          - string
          - 'null'
          readOnly: true
          description: How the scoped brand is mentioned in the content. One of `content`
            or `comment`, or null when the brand is not mentioned. Returned as `null`
            when the request omits `brand_id`.
        caption:
          type: string
          description: Short caption or title of the post (the primary user-visible
            text).
        description:
          type: string
          description: Long-form description of the post.
        transcript:
          type:
          - string
          - 'null'
          description: Transcribed speech from the content's audio, when available.
        platform_url:
          type: string
          description: |-
            Direct URL to the post on its platform.

            Examples:
            - `https://instagram.com/reel/abcd`
            - `https://www.tiktok.com/@username/video/12345678`
        last_keyword:
          type:
          - string
          - 'null'
          description: 'What the content was most recently collected under: a hashtag,
            a creator handle, a search term, or a brand identifier. Null when the
            content did not arrive through a collection task.'
        ai_summary:
          type:
          - string
          - 'null'
          description: Short AI-generated summary of the content.
        hashtags:
          type: array
          items:
            type: string
          description: Hashtags used in the post, normalized to lowercase and without
            the leading `#`.
      required:
      - ai_summary
      - brand_id
      - caption
      - comment_count
      - content_form
      - creator
      - description
      - earned_media_value_cents
      - engagement_rate
      - hashtags
      - id
      - is_influencer
      - is_owned
      - is_tagged
      - language
      - last_keyword
      - like_count
      - media_duration
      - mention_type
      - net_promoter_score
      - platform
      - platform_post_id
      - platform_url
      - posted_at
      - share_count
      - thumbnail_url
      - total_engagement
      - transcript
      - view_count
    ContentActionRequestRequest:
      type: object
      description: Request body for the content transcribe and AI summarize actions.
      properties:
        content_id:
          type: string
          minLength: 1
          description: Identifier of the content, as returned by `content.list`.
      required:
      - content_id
      x-internal: true
    ContentActionResponse:
      type: object
      description: State of a content transcribe or AI summarize request.
      properties:
        content_id:
          type: string
          description: Identifier of the content.
        status:
          type: string
          description: '`PENDING` when the work was queued, `COMPLETED` when the content
            already has the result.'
      required:
      - content_id
      - status
    ContentCreator:
      type: object
      description: The creator who published the content.
      properties:
        id:
          type: string
          readOnly: true
          description: Unique identifier for the creator.
        username:
          type:
          - string
          - 'null'
          description: The creator's username or handle on the platform (without an
            `@`).
        name:
          type:
          - string
          - 'null'
          description: The creator's display name on the platform.
        platform:
          type:
          - string
          - 'null'
          description: The platform the creator publishes on. One of `instagram`,
            `tiktok`, `youtube`.
        platform_handle:
          type:
          - string
          - 'null'
          description: The platform-side identifier of the creator's account.
        platform_url:
          type:
          - string
          - 'null'
          description: Direct URL to the creator's profile on the platform.
        profile_picture_url:
          type:
          - string
          - 'null'
          description: URL of the creator's profile picture.
        profile_bio:
          type:
          - string
          - 'null'
          description: The creator's profile bio as it appears on the platform.
        follower_count:
          type:
          - integer
          - 'null'
          description: The creator's follower count as of the most recent observation.
        active_content_count:
          type:
          - integer
          - 'null'
          description: Number of active content items attributed to the creator.
      required:
      - active_content_count
      - follower_count
      - id
      - name
      - platform
      - platform_handle
      - platform_url
      - profile_bio
      - profile_picture_url
      - username
    ContentDiscovery:
      type: object
      description: A content discovery job started for a search term.
      properties:
        id:
          type: string
          readOnly: true
          description: Identifier of the discovery job. Use it to track the job's
            status.
        search_term:
          type: string
          description: The search term the discovery job was started for.
        created_at:
          type: string
          format: date-time
          description: When the discovery job was created (UTC, ISO 8601).
        updated_at:
          type: string
          format: date-time
          description: Time of the most recent change to the discovery job (UTC, ISO
            8601).
        platforms:
          type:
          - array
          - 'null'
          items:
            type: string
          description: Platforms the indexing job was dispatched to.
        status:
          type: string
          readOnly: true
          description: Status of the discovery job. One of `PENDING`, `IN_PROGRESS`,
            `COMPLETED`, `FAILED`, `CANCELLED`.
        dispatched:
          type: boolean
          readOnly: true
          description: Whether a new indexing job was dispatched. Returns `false`
            when an identical search term was already requested within the recent
            deduplication window, in which case the in-flight job is reused.
      required:
      - created_at
      - dispatched
      - id
      - platforms
      - search_term
      - status
      - updated_at
    ContentDiscoveryRequestRequest:
      type: object
      description: Request body for ``POST /api/v1/content/discovery/``.
      properties:
        search_term:
          type: string
          minLength: 1
          description: The term to index content for (for example, `nike running`).
            Matching content is indexed asynchronously.
        platforms:
          type: array
          items:
            $ref: '#/components/schemas/PlatformEnum'
          default:
          - instagram
          - tiktok
          - youtube
          description: Platforms to index (`instagram`, `tiktok`, `youtube`). Defaults
            to all platforms when omitted.
      required:
      - search_term
      x-internal: true
    ContentFilterInputRequest:
      type: object
      description: |-
        Selects which content the analysis runs over.

        These parameters correspond one-to-one with the `content.list`
        filter parameters. At least one of `search`, `creator_id`,
        `creator_username`, `external_id`, or `brand_id` is required to scope the
        analysis. Supplied filters are
        combined with AND; min/max pairs are inclusive bounds, and dates are UTC
        ISO-8601 instants. When `posted_after`, `posted_before`, and
        `posted_within_days` are all omitted, the analysis defaults to the most
        recent window. The window between `posted_after` and `posted_before` must
        not exceed 366 days.

        The analysis runs over the highest-ranked matching content by `order_by`,
        up to 500 items or the limit configured for your workspace. When `order_by`
        is omitted, content is ranked by `earned_media_value_cents` descending.
      properties:
        view_count_min:
          type: integer
          description: Include only content with at least this many views.
        view_count_max:
          type: integer
          description: Include only content with at most this many views.
        comment_count_min:
          type: integer
          description: Include only content with at least this many comments.
        comment_count_max:
          type: integer
          description: Include only content with at most this many comments.
        like_count_min:
          type: integer
          description: Include only content with at least this many likes.
        like_count_max:
          type: integer
          description: Include only content with at most this many likes.
        share_count_min:
          type: integer
          description: Include only content with at least this many shares.
        share_count_max:
          type: integer
          description: Include only content with at most this many shares.
        total_engagement_min:
          type: integer
          description: Minimum total engagement (likes + comments + shares).
        total_engagement_max:
          type: integer
          description: Maximum total engagement (likes + comments + shares).
        earned_media_value_cents_min:
          type: integer
          description: Minimum estimated earned media value, in cents.
        earned_media_value_cents_max:
          type: integer
          description: Maximum estimated earned media value, in cents.
        media_duration_min:
          type: number
          format: double
          description: Include only content at least this many seconds long.
        media_duration_max:
          type: number
          format: double
          description: Include only content at most this many seconds long.
        posted_after:
          type: string
          format: date-time
          description: Include only content posted at or after this UTC ISO-8601 time.
        posted_before:
          type: string
          format: date-time
          description: Include only content posted at or before this UTC ISO-8601
            time.
        platform:
          type: array
          items:
            $ref: '#/components/schemas/PlatformEnum'
          description: 'Platforms to include. Any of: instagram, tiktok, youtube.'
        language:
          type: array
          items:
            type: string
            minLength: 1
          description: Language codes to include, for example `en` or `es`.
        content_form:
          allOf:
          - $ref: '#/components/schemas/ContentFormEnum'
          description: 'Content medium. One of: VIDEO, PHOTO.'
        search:
          type: string
          minLength: 1
          description: Full-text search across caption, transcript, and summary. Space-separated
            terms are AND-ed.
        creator_id:
          type: string
          minLength: 1
          description: Restrict to a single creator by id (from `creators.list`).
        brand_id:
          type: string
          minLength: 1
          description: Restrict to an accessible brand id (from `brands.list`). Defaults
            to your own brand when omitted.
        is_owned:
          type: boolean
          description: Include only content owned by the brand.
        is_tagged:
          type: boolean
          description: Include only content where the brand is tagged.
        is_influencer:
          type: boolean
          description: Include only the brand's influencer content, or exclude it
            with `false`.
        external_id:
          type: string
          minLength: 1
          description: Platform-side post id. Matched exactly.
        creator_username:
          type: string
          minLength: 1
          description: Creator username substring. Case-insensitive.
        mention_type:
          type: array
          items:
            $ref: '#/components/schemas/MentionTypeEnum'
          description: 'Restrict to these mention kinds. Any of: content, comment.'
        mention_type_not:
          type: array
          items:
            $ref: '#/components/schemas/MentionTypeEnum'
          description: 'Exclude these mention kinds. Any of: content, comment.'
        creator_follower_count_min:
          type: integer
          description: Include only content whose creator has at least this many followers.
        creator_follower_count_max:
          type: integer
          description: Include only content whose creator has at most this many followers.
        engagement_rate_min:
          type: number
          format: double
          description: Minimum engagement as a fraction of views (e.g. 0.05 = 5%).
        engagement_rate_max:
          type: number
          format: double
          description: Maximum engagement as a fraction of views (e.g. 0.05 = 5%).
        net_promoter_score_min:
          type: integer
          description: Minimum brand net promoter score for the content.
        net_promoter_score_max:
          type: integer
          description: Maximum brand net promoter score for the content.
        posted_within_days:
          type: integer
          maximum: 365
          minimum: 1
          description: Start of the window, counted back from the current time, in
            days (1-365). `posted_after` and `posted_within_hours` take precedence
            over it.
        posted_within_hours:
          type: integer
          maximum: 8760
          minimum: 1
          description: Start of the window, counted back from the current time, in
            hours (1-8760). Takes precedence over `posted_within_days`. Counted back
            from `posted_before` when that is also supplied.
        order_by:
          type: array
          items:
            type: string
            minLength: 1
          description: 'Sort the selected content. Any of: posted_at, view_count,
            like_count, comment_count, share_count, engagement_rate, total_engagement,
            earned_media_value_cents, net_promoter_score, relevance_score. A bare
            key sorts descending; prefix with `-` to sort ascending. Determines which
            content is retained when the set is capped. Defaults to `earned_media_value_cents`
            descending.'
      x-internal: true
    ContentFormEnum:
      enum:
      - VIDEO
      - PHOTO
      type: string
      x-internal: true
    ContentScopeRequest:
      type: object
      description: |-
        Specifies which of the creator's content relationships feed the analysis.

        Flags that are omitted default to owned content only.
      properties:
        owned:
          type: boolean
          default: true
          description: Whether to include content the creator originally posted.
        repost:
          type: boolean
          default: false
          description: Whether to include content the creator reposted.
        liked:
          type: boolean
          default: false
          description: Whether to include content the creator liked.
      x-internal: true
    ContentStatistics:
      type: object
      description: Aggregated totals for a filtered set of content.
      properties:
        post_count:
          type: integer
          description: Number of unique pieces of content in the filtered set.
        total_views:
          type: integer
          description: Total views across the filtered set.
        total_likes:
          type: integer
          description: Total likes across the filtered set.
        total_comments:
          type: integer
          description: Total comments across the filtered set.
        total_shares:
          type: integer
          description: Total shares across the filtered set.
        total_engagement:
          type: integer
          description: Total engagement (likes + comments + shares) across the filtered
            set.
        total_earned_media_value_cents:
          type: integer
          description: Total estimated marketing value across the filtered set, in
            US cents. Each piece of content contributes once even when multiple brand
            contexts match it through `brand_id`.
        total_media_duration:
          type: number
          format: double
          description: Total media length in seconds across the filtered set.
        engagement_rate:
          type:
          - number
          - 'null'
          format: double
          description: Total engagement divided by total views across the filtered
            set. Null when the filtered set has no recorded views.
        unique_creator_count:
          type: integer
          description: Number of distinct creators represented in the filtered set.
      required:
      - engagement_rate
      - post_count
      - total_comments
      - total_earned_media_value_cents
      - total_engagement
      - total_likes
      - total_media_duration
      - total_shares
      - total_views
      - unique_creator_count
    Creator:
      type: object
      description: A creator and their public profile fields.
      properties:
        id:
          type: string
          readOnly: true
          description: Unique identifier of the creator.
        username:
          type:
          - string
          - 'null'
          description: The creator's username or handle on the platform, without the
            `@`.
        name:
          type:
          - string
          - 'null'
          description: The creator's display name on the platform.
        email:
          type:
          - string
          - 'null'
          description: The creator's email address when known by the platform; empty
            otherwise.
        platform:
          type: string
          description: The platform the creator is on. One of `instagram`, `tiktok`,
            `youtube`.
        platform_handle:
          type: string
          description: The platform-side identifier of the account.
        follower_count:
          type: integer
          description: The creator's follower count as of the most recent observation.
        active_content_count:
          type:
          - integer
          - 'null'
          description: Number of active content items attributed to the creator.
        profile_bio:
          type:
          - string
          - 'null'
          description: The creator's profile bio as it appears on the platform.
        platform_url:
          type: string
          readOnly: true
          description: Direct URL to the creator's profile on the platform.
        profile_picture_url:
          type: string
          readOnly: true
          description: URL of the creator's profile picture.
      required:
      - active_content_count
      - email
      - follower_count
      - id
      - name
      - platform
      - platform_handle
      - platform_url
      - profile_bio
      - profile_picture_url
      - username
    CreatorSync:
      type: object
      description: One creator sync job.
      properties:
        id:
          type: string
          readOnly: true
          description: Identifier of the sync job. Use this value on follow-up calls.
            It is the same identifier `creator_vetting` reports as `sync_job_id`.
        created_at:
          type: string
          format: date-time
          description: Time the job was created (UTC, ISO-8601).
        updated_at:
          type: string
          format: date-time
          description: Time the job was most recently modified (UTC, ISO-8601).
        creator_id:
          type:
          - string
          - 'null'
          readOnly: true
          description: Identifier of the synced creator. Returns `null` until the
            creator has been resolved, which happens once the first posts have been
            read.
        profile_url:
          type:
          - string
          - 'null'
          description: The profile URL this job syncs. Returns `null` for a job whose
            source URL was not recorded.
        status:
          type: string
          readOnly: true
          description: Where the job has got to. One of `SCHEDULED`, `SYNCING`, `SUCCESS`,
            `FAILED`.
      required:
      - created_at
      - creator_id
      - id
      - status
      - updated_at
    CreatorSyncCreateRequest:
      type: object
      description: Request body for ``POST /api/v1/creator_sync/``.
      properties:
        profile_url:
          type: string
          format: uri
          minLength: 1
          description: Direct URL to the creator's profile on a supported platform
            (`tiktok`, `instagram`, `youtube`).
      required:
      - profile_url
      x-internal: true
    CreatorVetting:
      type: object
      description: A creator vetting, including its analysis runs and request settings.
      properties:
        id:
          type: string
          readOnly: true
          description: Identifier of the vetting. Use this value on follow-up calls.
        created_at:
          type: string
          format: date-time
          description: Time the vetting was created (UTC, ISO-8601).
        updated_at:
          type: string
          format: date-time
          description: Time the vetting was most recently modified (UTC, ISO-8601).
        creator_id:
          type:
          - string
          - 'null'
          readOnly: true
          description: Identifier of the creator the vetting is tied to. Returns ``null``
            until the creator has been resolved.
        status:
          type: string
          readOnly: true
          description: Status of the vetting. One of `IN_PROGRESS`, `COMPLETED`, `EMPTY_RESULT`
            (finished with no matching content), or `FAILED`.
        creator:
          oneOf:
          - $ref: '#/components/schemas/Creator'
          - type: 'null'
          readOnly: true
          description: The creator tied to this vetting. Returns null until resolved.
        type:
          type: string
          readOnly: true
          description: Always `CREATOR_ANALYSIS` here.
        sync_job_id:
          type:
          - string
          - 'null'
          readOnly: true
          description: Identifier of the scrape job triggered for this request. Populated
            only when `resync_profile=true` was supplied and the creator already existed;
            `null` otherwise.
        analysis:
          type: array
          items:
            $ref: '#/components/schemas/CreatorVettingRun'
          description: The analysis runs tied to this vetting, ordered newest first.
        safety_checks:
          type: array
          items:
            type: object
            additionalProperties: {}
          readOnly: true
          description: The safety criteria submitted with this vetting request.
        content_scope:
          type: object
          additionalProperties: {}
          readOnly: true
          description: The content relationships analyzed for this creator.
      required:
      - analysis
      - content_scope
      - created_at
      - creator
      - creator_id
      - id
      - safety_checks
      - status
      - sync_job_id
      - type
      - updated_at
    CreatorVettingRequest:
      type: object
      description: Request body for ``POST /api/v1/creator/vetting/``.
      properties:
        resync_profile:
          type: boolean
          default: false
          description: When true, re-indexes the creator even if they already exist
            in our database.
        profile_url:
          type: string
          format: uri
          minLength: 1
          description: Direct URL to the creator's profile on a supported platform
            (`instagram`, `tiktok`, `youtube`).
        content_scope:
          allOf:
          - $ref: '#/components/schemas/ContentScopeRequest'
          description: The creator's content relationships to analyze. Defaults to
            owned content only when omitted.
        safety_checks:
          type: array
          items:
            $ref: '#/components/schemas/SafetyCheckRequest'
          description: |-
            Safety criteria for the AI pipeline to evaluate for this creator.

            Example:
            ```json
            [
              {
                "type": "DISCLOSURE"
              }
            ]
            ```
      required:
      - profile_url
      x-internal: true
    CreatorVettingResult:
      type: object
      description: |-
        An analysis run together with its downloadable ``result``.

        Returned by `creator_vetting.results` for the analysis `creator_vetting.list`
        points to.
      properties:
        id:
          type: string
          readOnly: true
          description: Identifier of the analysis record.
        scope:
          type: string
          description: 'The subject this analysis covers: `CONTENT` or `COMMENT`.'
        created_at:
          type: string
          format: date-time
          description: Time the analysis was created (UTC, ISO-8601).
        modified_at:
          type: string
          format: date-time
          description: Time the analysis was most recently modified (UTC, ISO-8601).
        vetting_id:
          type: string
          readOnly: true
          description: Identifier of the vetting this analysis belongs to.
        status:
          type: string
          readOnly: true
          description: Status of the analysis. One of `IN_PROGRESS`, `COMPLETED`,
            `EMPTY_RESULT` (finished with no matching content), or `FAILED`.
        result:
          readOnly: true
          description: Signed, time-limited download of the run's analysis payload.
            Contains `download_url` (request it to retrieve the JSON), `expires_at`
            (Unix epoch seconds, approximately 24-hour TTL), and `format` (`json`).
            Returns `null` until the run completes.
          anyOf:
          - $ref: '#/components/schemas/ResultFile'
          - type: 'null'
      required:
      - created_at
      - id
      - modified_at
      - scope
      - status
      - vetting_id
    CreatorVettingRun:
      type: object
      description: |-
        A lightweight analysis run that belongs to a vetting.

        This representation omits the ``result``. Retrieve it from
        `creator_vetting.results` once ``status`` is ``COMPLETED``.
      properties:
        id:
          type: string
          readOnly: true
          description: Identifier of the analysis record.
        scope:
          type: string
          description: 'The subject this analysis covers: `CONTENT` or `COMMENT`.'
        status:
          type: string
          readOnly: true
          description: Status of the analysis. One of `IN_PROGRESS`, `COMPLETED`,
            `EMPTY_RESULT` (finished with no matching content), or `FAILED`.
      required:
      - id
      - scope
      - status
    Detection:
      type: object
      description: One brand placement detection run.
      properties:
        id:
          type: string
          readOnly: true
          description: Identifier of the detection. Use this value on follow-up calls.
        created_at:
          type: string
          format: date-time
          description: Time the detection was created (UTC, ISO-8601).
        updated_at:
          type: string
          format: date-time
          description: Time the detection was most recently modified (UTC, ISO-8601).
        status:
          type: string
          description: Where the detection has got to. One of `SCHEDULED`, `IN_PROGRESS`,
            `SUCCESS`, `FAILED`.
        context:
          allOf:
          - $ref: '#/components/schemas/DetectionContext'
          description: The input this detection was created with.
        result:
          oneOf:
          - $ref: '#/components/schemas/DetectionResult'
          - type: 'null'
          readOnly: true
          description: 'The placements document. `null` until `status` is `SUCCESS`
            or `FAILED`; on `FAILED` it carries the counts and `brands: []`.'
      required:
      - context
      - created_at
      - id
      - result
      - status
      - updated_at
    DetectionBrandHint:
      type: object
      description: One brand to look for, and what counts as evidence for it.
      properties:
        name:
          type: string
          description: Display name of the brand, at most 60 characters. Every placement
            names it verbatim.
          maxLength: 60
        identifiers:
          type: array
          items:
            type: string
            maxLength: 200
            minLength: 2
          description: 2 to 10 words, logos and product names that count as evidence
            of this brand, the brand name included, each 2 to 200 characters. For
            example `['Nike', 'Jordan', 'Air Force 1']`.
          maxItems: 10
          minItems: 2
        negatives:
          type: array
          items:
            type: string
            maxLength: 200
          description: Up to 12 look-alikes that must not count, each at most 200
            characters, for example `['Jordan (the country)']`.
          maxItems: 12
      required:
      - identifiers
      - name
    DetectionBrandHintRequest:
      type: object
      description: One brand to look for, and what counts as evidence for it.
      properties:
        name:
          type: string
          minLength: 1
          description: Display name of the brand, at most 60 characters. Every placement
            names it verbatim.
          maxLength: 60
        identifiers:
          type: array
          items:
            type: string
            minLength: 2
            maxLength: 200
          description: 2 to 10 words, logos and product names that count as evidence
            of this brand, the brand name included, each 2 to 200 characters. For
            example `['Nike', 'Jordan', 'Air Force 1']`.
          maxItems: 10
          minItems: 2
        negatives:
          type: array
          items:
            type: string
            minLength: 1
            maxLength: 200
          description: Up to 12 look-alikes that must not count, each at most 200
            characters, for example `['Jordan (the country)']`.
          maxItems: 12
      required:
      - identifiers
      - name
      x-internal: true
    DetectionBrandResult:
      type: object
      description: Everything found for one requested brand.
      properties:
        brand_name:
          type: string
          description: The requested brand, named exactly as it was asked for.
        found:
          type: boolean
          description: Whether the brand was present at all.
        summary:
          allOf:
          - $ref: '#/components/schemas/DetectionBrandSummary'
          description: Counts for this brand.
        placements:
          type: array
          items:
            $ref: '#/components/schemas/DetectionPlacement'
          description: Every placement for this brand, earliest first.
      required:
      - brand_name
      - found
      - placements
      - summary
    DetectionBrandSummary:
      type: object
      description: Counts for one brand.
      properties:
        placements:
          type: integer
          description: Placements for this brand.
        confirmed:
          type: integer
          description: Of those, confirmed.
        review:
          type: integer
          description: Of those, left for review.
      required:
      - confirmed
      - placements
      - review
    DetectionContext:
      type: object
      description: The input the detection run was given, as stored.
      properties:
        external_url:
          type: string
          default: ''
          description: URL of the content being analysed. Read from the content this
            detection resolved to, falling back to the submitted `content_url` while
            that content is still being indexed.
        hints:
          allOf:
          - $ref: '#/components/schemas/DetectionHints'
          description: The brands to detect and the evidence that counts for each.
      required:
      - hints
    DetectionContextInputRequest:
      type: object
      description: What the caller supplies as the detection's input.
      properties:
        hints:
          allOf:
          - $ref: '#/components/schemas/DetectionHintsRequest'
          description: The brands to detect and the evidence that counts for each.
      required:
      - hints
      x-internal: true
    DetectionCreateRequest:
      type: object
      description: Request body for ``POST /api/v1/detection/``.
      properties:
        content_id:
          type: string
          minLength: 1
          description: Identifier of content Zelf already holds, obtained from `content.list`.
            Supply this or `content_url`, not both.
        content_url:
          type: string
          format: uri
          minLength: 1
          description: Direct URL to the post or video to analyse. Content Zelf does
            not hold yet is indexed first. Supply this or `content_id`, not both.
        context:
          allOf:
          - $ref: '#/components/schemas/DetectionContextInputRequest'
          description: The brands to detect and the evidence that counts for each.
      required:
      - context
      x-internal: true
    DetectionHints:
      type: object
      description: What the detector is asked to look for.
      properties:
        brands:
          type: array
          items:
            $ref: '#/components/schemas/DetectionBrandHint'
          description: 1 to 200 brands to detect, in the order the results report
            them.
          maxItems: 200
          minItems: 1
      required:
      - brands
    DetectionHintsRequest:
      type: object
      description: What the detector is asked to look for.
      properties:
        brands:
          type: array
          items:
            $ref: '#/components/schemas/DetectionBrandHintRequest'
          description: 1 to 200 brands to detect, in the order the results report
            them.
          maxItems: 200
          minItems: 1
      required:
      - brands
      x-internal: true
    DetectionPlacement:
      type: object
      description: One brand present in the content at one moment, with its evidence.
      properties:
        timestamp:
          type: string
          description: '`mm:ss-mm:ss`, or `mm:ss` for a single moment. Past an hour
            the minutes keep counting (`69:23-69:25`).'
        video_url_at_timestamp:
          type:
          - string
          - 'null'
          format: uri
          description: 'YouTube: opens the video 5 seconds before the placement. `null`
            on TikTok and Instagram.'
        modality:
          type: string
          description: How it was detected. One of `Visual`, `Audio`, `Text`.
        what_detected:
          type: string
          description: What was observed, in the detector's own words.
        reasoning:
          type: string
          description: Why the observation counts as this brand.
        confidence:
          type: string
          description: '`confirmed`: passed a final check against the video''s frames.
            `review`: detected but did not pass that final frame check. `Audio` placements
            are always `review`, because a spoken mention has no frames to check;
            they can be used as they are.'
      required:
      - confidence
      - modality
      - reasoning
      - timestamp
      - video_url_at_timestamp
      - what_detected
    DetectionResult:
      type: object
      description: The placements document a finished detection produces.
      properties:
        video:
          allOf:
          - $ref: '#/components/schemas/DetectionVideo'
          description: The content that was analysed.
        summary:
          allOf:
          - $ref: '#/components/schemas/DetectionSummary'
          description: Counts across the whole run.
        brands:
          type: array
          items:
            $ref: '#/components/schemas/DetectionBrandResult'
          description: One entry per requested brand, in request order, including
            the brands that were not found.
      required:
      - brands
      - summary
      - video
    DetectionSummary:
      type: object
      description: Counts across the whole run.
      properties:
        brands_requested:
          type: integer
          description: How many brands the rubric asked for.
        brands_found:
          type: integer
          description: How many of those brands were present.
        placements:
          type: integer
          description: Total placements reported.
        confirmed:
          type: integer
          description: Placements that passed the final frame check.
        review:
          type: integer
          description: Placements that did not pass the final frame check, including
            every `Audio` placement.
      required:
      - brands_found
      - brands_requested
      - confirmed
      - placements
      - review
    DetectionVideo:
      type: object
      description: The content the placements were found in.
      properties:
        video_url:
          type: string
          format: uri
          description: URL of the analysed content.
        platform:
          type: string
          description: Platform the content was posted on.
        duration_s:
          type: number
          format: double
          description: Length of the content in seconds.
      required:
      - duration_s
      - platform
      - video_url
    FollowerAccount:
      type: object
      description: One owned handle and the followers it carries.
      properties:
        platform:
          type: string
          description: The platform the handle is on.
        username:
          type: string
          description: The handle.
        followers:
          type: integer
          description: Follower count for this handle.
      required:
      - followers
      - platform
      - username
    FollowerPeriod:
      type: object
      description: Followers at the end of one period.
      properties:
        followers:
          type: integer
          description: Followers across every owned handle.
        start_date:
          type: string
          format: date
          description: First day of the period.
        end_date:
          type: string
          format: date
          description: Last day of the period.
        accounts:
          type: array
          items:
            $ref: '#/components/schemas/FollowerAccount'
          description: Per handle counts that add up to `followers`.
      required:
      - accounts
      - end_date
      - followers
      - start_date
    FollowerTimeseries:
      type: object
      description: One brand's follower history.
      properties:
        brand:
          allOf:
          - $ref: '#/components/schemas/AnalyticsBrand'
          description: The brand this row covers.
        periods:
          type: array
          items:
            $ref: '#/components/schemas/FollowerPeriod'
          description: One entry per period, oldest first.
      required:
      - brand
      - periods
    IntervalHoursEnum:
      enum:
      - 4
      - 6
      - 12
      - 24
      - 168
      - 720
      type: integer
      x-internal: true
    KindEnum:
      enum:
      - snapshot
      - event
      type: string
      x-internal: true
    LastStatusEnum:
      enum:
      - SUCCESS
      - FAILED
      type: string
      x-internal: true
    MentionTypeEnum:
      enum:
      - content
      - comment
      type: string
      x-internal: true
    NullEnum:
      type: 'null'
      x-internal: true
    PaginatedAiAnalysisList:
      type: array
      items:
        $ref: '#/components/schemas/AiAnalysis'
      description: Paginated response envelope. `results` holds this page of `AiAnalysis`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedAiAnalysisResultList:
      type: array
      items:
        $ref: '#/components/schemas/AiAnalysisResult'
      description: Paginated response envelope. `results` holds this page of `AiAnalysisResult`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedBrandCatalogList:
      type: array
      items:
        $ref: '#/components/schemas/BrandCatalog'
      description: Paginated response envelope. `results` holds this page of `BrandCatalog`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedBrandList:
      type: array
      items:
        $ref: '#/components/schemas/Brand'
      description: Paginated response envelope. `results` holds this page of `Brand`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedCommentList:
      type: array
      items:
        $ref: '#/components/schemas/Comment'
      description: Paginated response envelope. `results` holds this page of `Comment`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedContentList:
      type: array
      items:
        $ref: '#/components/schemas/Content'
      description: Paginated response envelope. `results` holds this page of `Content`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedCreatorList:
      type: array
      items:
        $ref: '#/components/schemas/Creator'
      description: Paginated response envelope. `results` holds this page of `Creator`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedCreatorSyncList:
      type: array
      items:
        $ref: '#/components/schemas/CreatorSync'
      description: Paginated response envelope. `results` holds this page of `CreatorSync`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedCreatorVettingList:
      type: array
      items:
        $ref: '#/components/schemas/CreatorVetting'
      description: Paginated response envelope. `results` holds this page of `CreatorVetting`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedCreatorVettingResultList:
      type: array
      items:
        $ref: '#/components/schemas/CreatorVettingResult'
      description: Paginated response envelope. `results` holds this page of `CreatorVettingResult`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedDetectionList:
      type: array
      items:
        $ref: '#/components/schemas/Detection'
      description: Paginated response envelope. `results` holds this page of `Detection`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedWebhookDeliveryList:
      type: array
      items:
        $ref: '#/components/schemas/WebhookDelivery'
      description: Paginated response envelope. `results` holds this page of `WebhookDelivery`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedWebhookSecretList:
      type: array
      items:
        $ref: '#/components/schemas/WebhookSecret'
      description: Paginated response envelope. `results` holds this page of `WebhookSecret`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PaginatedWebhookSubscriptionList:
      type: array
      items:
        $ref: '#/components/schemas/WebhookSubscription'
      description: Paginated response envelope. `results` holds this page of `WebhookSubscription`
        objects; the envelope fields (`log_id`, `timestamp`, `item_count`, `page`,
        `next`, `previous`) are described under Response shape.
      x-internal: true
    PatchedBrandUpdateRequest:
      type: object
      description: |-
        Partial update for a brand's monitoring keywords.

        Every field is optional, and only the supplied fields are changed, but at
        least one field is required.
      properties:
        search_terms:
          type: array
          items:
            type: string
            minLength: 1
          description: Plain keywords the brand monitors across platforms, matched
            as-is (case-insensitive, no operators).
        inclusion_terms:
          type: array
          items:
            type: string
            minLength: 1
          description: Terms that must be present for content to match.
        exclusion_terms:
          type: array
          items:
            type: string
            minLength: 1
          description: Terms that disqualify content from matching.
        compound_inclusion_terms:
          type: array
          items:
            type: string
            minLength: 1
          description: Secondary inclusion terms layered on top of `inclusion_terms`.
        influencer_terms:
          type: array
          items:
            type: string
            minLength: 1
          description: Terms that mark matched content as influencer content.
        allowed_languages:
          type: array
          items:
            type: string
            minLength: 1
          description: ISO 639-1 codes to track, or the single value `all`. An empty
            array means all languages are tracked. A code outside ISO 639-1 is rejected,
            because a brand tracking it would match no content.
        boolean_query:
          type:
          - string
          - 'null'
          description: |-
            Full-text search across the caption, transcript, summary, and hashtags.

            **Syntax**
            - Multiple words are combined with AND: `nike running` matches content containing both `nike` and `running`.
            - `OR` (case-insensitive) matches either side: `nike OR adidas`.
            - `NOT` (case-insensitive) excludes a term: `running NOT marathon` matches `running` but not `marathon`.
            - Wrap a phrase in double quotes to match the words in order: `"summer collection"`.
            - Negate a phrase with `NOT`: `NOT "out of stock"`.
            - Group with parentheses to combine operators: `(nike OR adidas) running`.
            - `ONEAR/n` (case-insensitive) matches terms within `n` words of each other, in order: `apple ONEAR/2 watch` matches `apple watch` and `apple smart watch`, but not `watch ... apple`. It binds more tightly than `AND` and `OR`, so `apple ONEAR/2 watch AND cheap` is interpreted as `(apple near watch) AND cheap`.

            **Examples**
            - `nike running` - contains both terms.
            - `nike OR adidas` - contains either brand.
            - `"summer sale" NOT clearance` - the exact phrase, without `clearance`.
            - `(running OR cycling) NOT "beginner guide"` - either sport, excluding the phrase.
            - `apple ONEAR/2 watch` - `apple` within two words before `watch`.

            Content matching this query is connected to (associated with) this brand.
        brand_id:
          type: string
          minLength: 1
          description: Brand to update, specified as an `id` from `brands.list`. Omit
            to update the workspace brand.
      x-internal: true
    PatchedWebhookSubscriptionUpdateRequest:
      type: object
      properties:
        url:
          type: string
          format: uri
          minLength: 1
          description: Destination URL where webhook payloads are sent.
          maxLength: 2048
        resource_filters:
          description: Filters that scope which resources are delivered.
        custom_headers:
          description: Extra HTTP headers sent with each webhook request.
        interval_hours:
          description: 'Hours between snapshot deliveries. Allowed values: 4, 6, 12,
            24, 168, 720. The first snapshot is sent within minutes of the subscription
            being saved, not after a full interval has passed.'
          oneOf:
          - $ref: '#/components/schemas/IntervalHoursEnum'
          - $ref: '#/components/schemas/NullEnum'
        max_items:
          type:
          - integer
          - 'null'
          minimum: 0
          description: Maximum number of items per delivery.
        enabled:
          type: boolean
          description: Whether this subscription is active and will be dispatched.
      x-internal: true
    PlatformEnum:
      enum:
      - instagram
      - tiktok
      - youtube
      type: string
      x-internal: true
    ResourceEnum:
      enum:
      - content
      - creator
      type: string
      x-internal: true
    ResultFile:
      type: object
      description: Signed, time-limited download of an analysis run's result payload.
      properties:
        expires_at:
          type: integer
          description: Unix epoch seconds after which `download_url` expires. Re-fetch
            the run to obtain a fresh URL.
        format:
          type: string
          description: Payload encoding. Always `json`.
        download_url:
          type: string
          format: uri
          description: Signed URL to request the JSON payload. Expires at `expires_at`.
      required:
      - download_url
      - expires_at
      - format
    SafetyCheckRequest:
      type: object
      description: |-
        A single safety criterion the AI pipeline evaluates against the creator.

        Only ``type`` is required. The ``extra`` field supplies the label for the
        ``USER_DEFINED_SAFETY_CRITERION`` type and is ignored for the predefined
        types.
      properties:
        type:
          allOf:
          - $ref: '#/components/schemas/TypeEnum'
          description: |-
            Category of the safety criterion. One of `USER_DEFINED_SAFETY_CRITERION`, `ADULT_CONTENT`, `ARMS_AND_AMMUNITION`, `MISINFORMATION`, `PROFANITY`, `POLITICS`, `DISCLOSURE`.

            `DISCLOSURE` looks for explicit sponsorship disclosures (for example `#ad`, a paid partnership, an affiliate commission or a gifted product) in each video's on-screen text, speech transcription, title and caption. It is not scored as a violation: findings are listed per video under `insight_json.disclosures` in the result payload.
        extra:
          type:
          - string
          - 'null'
          description: Label for `USER_DEFINED_SAFETY_CRITERION`, stored as the criterion's
            `value`. Ignored for predefined types.
      required:
      - type
      x-internal: true
    SecretTypeEnum:
      enum:
      - default
      - new
      type: string
      x-internal: true
    StateEnum:
      enum:
      - PENDING
      - SUCCEEDED
      - FAILED
      - EXHAUSTED
      - QUOTA_EXHAUSTED
      type: string
      x-internal: true
    SubBrandTypeEnum:
      enum:
      - COMPETITIVE
      - THEME
      type: string
      x-internal: true
    TypeEnum:
      enum:
      - USER_DEFINED_SAFETY_CRITERION
      - ADULT_CONTENT
      - ARMS_AND_AMMUNITION
      - MISINFORMATION
      - PROFANITY
      - POLITICS
      - DISCLOSURE
      type: string
      x-internal: true
    Usage:
      type: object
      description: Live consumption for the brand behind the current credentials.
      properties:
        brand_id:
          type: string
          description: Brand these figures belong to.
        authenticated_as:
          type: string
          description: '``api_key`` or ``mcp``, whichever made this call. It decides
            which groups your calls are counted against.'
        scopes:
          type: array
          items:
            $ref: '#/components/schemas/UsageScope'
          description: One entry per metered group. Both sections are returned whichever
            way you call.
        history:
          allOf:
          - $ref: '#/components/schemas/UsageHistory'
          description: What was actually spent, day by day, from the usage ledger.
      required:
      - authenticated_as
      - brand_id
      - history
      - scopes
    UsageDay:
      type: object
      description: One day of consumption for one source.
      properties:
        source:
          type: string
          description: '``api`` for direct requests, ``mcp`` for AI clients.'
        general:
          type: integer
          description: Every counted call that day.
        ai_analysis:
          type: integer
          description: Calls counted against the AI analysis group, creator vetting
            included.
        data_records:
          type: integer
          description: Calls counted against data records.
        data_records_items:
          type: integer
          description: Records those calls returned.
        date:
          type: string
          format: date
          description: The day, in the brand's billing timezone.
      required:
      - ai_analysis
      - data_records
      - data_records_items
      - date
      - general
      - source
    UsageHistory:
      type: object
      description: Daily consumption over the window the ledger keeps.
      properties:
        start_date:
          type: string
          format: date
          description: First day covered.
        end_date:
          type: string
          format: date
          description: Last day covered.
        days:
          type: array
          items:
            $ref: '#/components/schemas/UsageDay'
          description: One entry per day and source that saw usage, oldest first.
      required:
      - days
      - end_date
      - start_date
    UsageScope:
      type: object
      description: One metered group, with every window that has a limit.
      properties:
        scope:
          type: string
          description: Identifier of the metered group.
        human_name:
          type: string
          description: Name of the group, as an admin sees it.
        kind:
          type: string
          description: '``request`` counts calls; ``item`` counts rows returned.'
        section:
          type: string
          description: 'Which product the group meters: ``api`` for direct requests,
            ``mcp`` for AI clients. The two never share a counter.'
        windows:
          type: array
          items:
            $ref: '#/components/schemas/UsageWindow'
          description: Every window that has a limit configured.
      required:
      - human_name
      - kind
      - scope
      - section
      - windows
    UsageWindow:
      type: object
      description: Consumption of one window, e.g. requests in the current hour.
      properties:
        window:
          type: string
          description: 'Which window this is: rps, rpm, rph, rpd, rpmonth, or an item
            window.'
        limit:
          type: integer
          description: Most that may be spent in this window.
        used:
          type: integer
          description: Spent so far in this window.
        remaining:
          type: integer
          description: Still available in this window.
        reset_in:
          type: integer
          description: Seconds until the window resets. The window starts on its first
            request rather than on the clock, so this is what is left of it.
      required:
      - limit
      - remaining
      - reset_in
      - used
      - window
    WebhookAvailableEvent:
      type: object
      description: An event type a subscription may ask for.
      properties:
        event:
          type: string
          readOnly: true
          description: Event type identifier, e.g. creator_vetting.completed.
        group:
          type: string
          readOnly: true
          description: Human-readable grouping label.
        description:
          type: string
          readOnly: true
          description: What triggers the event and what the payload holds.
      required:
      - description
      - event
      - group
    WebhookData:
      type: object
      description: |-
        The carried content of a push. The populated key depends on `delivery_mode`.

        `manifest` deliveries populate `files` (downloadable snapshot files) and,
        for the content resource, `statistics`. `inline` deliveries populate
        `items` (the rows directly). The unused key is omitted.
      properties:
        item_count:
          type: integer
          minimum: 0
          description: Total number of rows in this delivery, summed across `files`
            or equal to the length of `items`.
        statistics:
          description: Aggregate totals over the snapshot, including post count, views,
            engagement, earned media value, and unique creators. Present only on `content.snapshot`;
            absent for creator and event pushes.
        files:
          type: array
          items:
            $ref: '#/components/schemas/WebhookFile'
          description: Manifest mode only. The downloadable snapshot files; retrieve
            each `download_url` and stream the NDJSON rows. Absent in `inline` mode.
        items:
          type: array
          items: {}
          description: Inline mode only. The delivered rows themselves, in the same
            shape returned by the matching REST endpoint. Absent in `manifest` mode.
      required:
      - item_count
    WebhookDeleteAcknowledgement:
      type: object
      description: Confirmation that a webhook resource was deleted.
      properties:
        acknowledged:
          type: boolean
          readOnly: true
          description: Always true; confirms the webhook resource delete was acknowledged.
        action:
          type: string
          readOnly: true
          description: Action that was performed. Always 'delete'.
        status:
          type: string
          readOnly: true
          description: Completion status of the action. Always 'complete'.
      required:
      - acknowledged
      - action
      - status
    WebhookDelivery:
      type: object
      description: One attempt to deliver one push, with its outcome.
      properties:
        id:
          type: string
          readOnly: true
          description: Encoded identifier of the webhook delivery.
        subscription:
          type: string
          readOnly: true
          description: Encoded identifier of the parent subscription.
        scheduled_at:
          type: string
          format: date-time
          readOnly: true
          description: When this delivery is scheduled to be sent.
        attempt_count:
          type: integer
          readOnly: true
          description: Number of delivery attempts made so far.
        state:
          allOf:
          - $ref: '#/components/schemas/StateEnum'
          readOnly: true
          description: Current state of the delivery.
        last_status_code:
          type:
          - integer
          - 'null'
          readOnly: true
          description: HTTP status code returned by the most recent attempt.
        last_error:
          type: string
          readOnly: true
          description: Error message from the most recent failed attempt.
        response_excerpt:
          type: string
          readOnly: true
          description: Excerpt of the response body returned by the endpoint.
        finished_at:
          type:
          - string
          - 'null'
          format: date-time
          readOnly: true
          description: When the delivery finished (succeeded or exhausted).
        item_count:
          type:
          - integer
          - 'null'
          readOnly: true
          description: Number of items included in the delivered payload.
        sha256:
          type: string
          readOnly: true
          description: SHA-256 checksum of the delivered payload.
        size_bytes:
          type:
          - integer
          - 'null'
          readOnly: true
          description: Size of the delivered payload in bytes.
        created:
          type: string
          format: date-time
          readOnly: true
          description: Timestamp set once when the row is first created.
        modified:
          type: string
          format: date-time
          readOnly: true
          description: Timestamp updated automatically on every save.
      required:
      - attempt_count
      - created
      - finished_at
      - id
      - item_count
      - last_error
      - last_status_code
      - modified
      - response_excerpt
      - scheduled_at
      - sha256
      - size_bytes
      - state
      - subscription
    WebhookFile:
      type: object
      description: |-
        One downloadable snapshot file in a manifest push.

        `files` is an array, so read every entry rather than assuming one.
      properties:
        format:
          type: string
          description: File encoding. Always `ndjson` (newline-delimited JSON, with
            one row per line).
        expires_at:
          type: integer
          description: Unix epoch seconds after which `download_url` stops working.
            Fetch the file before this time.
        sha256:
          type: string
          description: Hex SHA-256 of the file body. Use it to verify the download.
        size_bytes:
          type: integer
          minimum: 0
          description: Size of the file in bytes.
        item_count:
          type: integer
          minimum: 0
          description: Number of rows in this file.
        download_url:
          type: string
          format: uri
          description: Signed, time-limited URL from which to retrieve the file. Expires
            at `expires_at`.
      required:
      - download_url
      - expires_at
      - format
      - item_count
      - sha256
      - size_bytes
    WebhookPush:
      type: object
      description: |-
        The outer payload Zelf POSTs to receivers, uniform across all pushes.

        Top-level fields are delivery metadata and remain identical across
        modes. `data` carries the content; which key inside it is populated
        (`files` or `items`) is determined by `delivery_mode`. `manifest`
        deliveries use `data.files`, while `inline` event pushes use `data.items`.
      properties:
        id:
          type: string
          description: Unique id of this delivery attempt, stable across retries of
            the same push.
        type:
          type: string
          description: Event type. `<resource>.snapshot` for scheduled snapshots (`content.snapshot`,
            `creator.snapshot`), or the event name for event pushes (`creator_vetting.completed`).
        subscription_id:
          type: string
          description: Id of the subscription that produced this push.
        delivered_at:
          type: integer
          description: Unix epoch seconds at which Zelf sent this push.
        livemode:
          type: boolean
          description: '`true` for production data; `false` for test deliveries.'
        resource:
          type:
          - string
          - 'null'
          description: Resource the push covers (`content` or `creator`) for snapshots;
            `null` for event pushes.
        resource_filters:
          description: The subscription's stored filters that scoped this snapshot,
            using the same query params as the matching list operation (`content.list`
            or `creators.list`). Empty for event pushes.
        delivery_mode:
          enum:
          - manifest
          - inline
          type: string
          description: '`manifest`: `data.files` holds downloadable snapshot files.
            `inline`: `data.items` holds the rows directly.'
        data:
          allOf:
          - $ref: '#/components/schemas/WebhookData'
          description: The push payload. See `delivery_mode` for which key inside
            is populated.
      required:
      - data
      - delivered_at
      - delivery_mode
      - id
      - livemode
      - resource_filters
      - subscription_id
      - type
    WebhookSecret:
      type: object
      description: A signing secret, identified by its prefix rather than its value.
      properties:
        id:
          type: string
          readOnly: true
          description: Encoded identifier of the webhook secret.
        kind:
          allOf:
          - $ref: '#/components/schemas/KindEnum'
          readOnly: true
          description: Type of webhook this secret applies to.
        secret_prefix:
          type: string
          readOnly: true
          description: Non-sensitive prefix of the secret, shown for identification.
        is_active:
          type: boolean
          readOnly: true
          description: Whether this secret is currently active.
        is_default:
          type: boolean
          readOnly: true
          description: Whether this is the default secret for the brand and kind.
        created:
          type: string
          format: date-time
          readOnly: true
          description: Timestamp set once when the row is first created.
        modified:
          type: string
          format: date-time
          readOnly: true
          description: Timestamp updated automatically on every save.
      required:
      - created
      - id
      - is_active
      - is_default
      - kind
      - modified
      - secret_prefix
    WebhookSecretCreateRequest:
      type: object
      properties:
        kind:
          allOf:
          - $ref: '#/components/schemas/KindEnum'
          default: snapshot
          description: Whether the secret is for snapshot or event webhooks.
        make_default:
          type: boolean
          default: false
          description: Set this secret as the default for its kind.
      x-internal: true
    WebhookSecretCreated:
      type: object
      description: |-
        A secret just created or rotated, carrying the value once.

        Store it now: later reads return the prefix alone.
      properties:
        id:
          type: string
          readOnly: true
          description: Encoded identifier of the webhook secret.
        kind:
          allOf:
          - $ref: '#/components/schemas/KindEnum'
          readOnly: true
          description: Type of webhook this secret applies to.
        secret_prefix:
          type: string
          readOnly: true
          description: Non-sensitive prefix of the secret, shown for identification.
        is_active:
          type: boolean
          readOnly: true
          description: Whether this secret is currently active.
        is_default:
          type: boolean
          readOnly: true
          description: Whether this is the default secret for the brand and kind.
        created:
          type: string
          format: date-time
          readOnly: true
          description: Timestamp set once when the row is first created.
        modified:
          type: string
          format: date-time
          readOnly: true
          description: Timestamp updated automatically on every save.
        secret:
          type: string
          readOnly: true
          description: Full signing secret, returned only once on creation.
      required:
      - created
      - id
      - is_active
      - is_default
      - kind
      - modified
      - secret
      - secret_prefix
    WebhookSubscription:
      type: object
      description: A registered endpoint and the events it receives.
      properties:
        id:
          type: string
          readOnly: true
          description: Encoded identifier of the webhook subscription.
        kind:
          allOf:
          - $ref: '#/components/schemas/KindEnum'
          readOnly: true
          description: Type of webhook this subscription delivers.
        resource:
          readOnly: true
          description: Resource type the subscription is for (content or creator).
          oneOf:
          - $ref: '#/components/schemas/ResourceEnum'
          - $ref: '#/components/schemas/NullEnum'
        event_types:
          type: array
          items:
            type: string
            maxLength: 64
          readOnly: true
          description: Event types the subscription listens for.
        resource_filters:
          readOnly: true
          description: Filters applied to select which resources are delivered.
        url:
          type: string
          format: uri
          readOnly: true
          description: Destination URL where webhook payloads are sent.
        custom_headers:
          readOnly: true
          description: Custom HTTP headers to include with each delivery.
        interval_hours:
          type:
          - integer
          - 'null'
          readOnly: true
          description: Hours between snapshot deliveries.
        max_items:
          type:
          - integer
          - 'null'
          readOnly: true
          description: Max rows a real snapshot delivers. Null or 0 falls back to
            the default item limit.
        secret_prefix:
          type: string
          readOnly: true
          description: Public prefix of the subscription signing secret.
        enabled:
          type: boolean
          readOnly: true
          description: Whether this subscription is active and will be dispatched.
        last_run_at:
          type:
          - string
          - 'null'
          format: date-time
          readOnly: true
          description: When this subscription last dispatched a delivery.
        last_status:
          readOnly: true
          description: Outcome status of the most recent delivery. Empty until the
            subscription has dispatched for the first time.
          oneOf:
          - $ref: '#/components/schemas/LastStatusEnum'
          - $ref: '#/components/schemas/BlankEnum'
        created:
          type: string
          format: date-time
          readOnly: true
          description: Timestamp set once when the row is first created.
        modified:
          type: string
          format: date-time
          readOnly: true
          description: Timestamp updated automatically on every save.
      required:
      - created
      - custom_headers
      - enabled
      - event_types
      - id
      - interval_hours
      - kind
      - last_run_at
      - last_status
      - max_items
      - modified
      - resource
      - resource_filters
      - secret_prefix
      - url
    WebhookSubscriptionCreateRequest:
      type: object
      properties:
        kind:
          allOf:
          - $ref: '#/components/schemas/KindEnum'
          default: snapshot
          description: Whether the subscription is snapshot or event based.
        resource:
          description: Resource type for snapshot subscriptions.
          oneOf:
          - $ref: '#/components/schemas/ResourceEnum'
          - $ref: '#/components/schemas/NullEnum'
        event_types:
          type: array
          items:
            type: string
            minLength: 1
            maxLength: 64
          description: Event names the subscription listens for.
        resource_filters:
          description: Filters that scope which resources are delivered.
        url:
          type: string
          format: uri
          minLength: 1
          description: Destination URL where webhook payloads are sent.
          maxLength: 2048
        custom_headers:
          description: Extra HTTP headers sent with each webhook request.
        interval_hours:
          description: 'Hours between snapshot deliveries. Allowed values: 4, 6, 12,
            24, 168, 720. The first snapshot is sent within minutes of the subscription
            being saved, not after a full interval has passed.'
          oneOf:
          - $ref: '#/components/schemas/IntervalHoursEnum'
          - $ref: '#/components/schemas/NullEnum'
        max_items:
          type:
          - integer
          - 'null'
          minimum: 0
          description: Maximum number of items per delivery.
        secret_type:
          allOf:
          - $ref: '#/components/schemas/SecretTypeEnum'
          writeOnly: true
          default: default
          description: Type of signing secret to attach to the subscription.
      required:
      - url
      x-internal: true
    WebhookSubscriptionCreated:
      type: object
      description: |-
        A subscription just created, with the signing secret attached.

        The secret is shown on this response only and never again.
      properties:
        id:
          type: string
          readOnly: true
          description: Encoded identifier of the webhook subscription.
        kind:
          allOf:
          - $ref: '#/components/schemas/KindEnum'
          readOnly: true
          description: Type of webhook this subscription delivers.
        resource:
          readOnly: true
          description: Resource type the subscription is for (content or creator).
          oneOf:
          - $ref: '#/components/schemas/ResourceEnum'
          - $ref: '#/components/schemas/NullEnum'
        event_types:
          type: array
          items:
            type: string
            maxLength: 64
          readOnly: true
          description: Event types the subscription listens for.
        resource_filters:
          readOnly: true
          description: Filters applied to select which resources are delivered.
        url:
          type: string
          format: uri
          readOnly: true
          description: Destination URL where webhook payloads are sent.
        custom_headers:
          readOnly: true
          description: Custom HTTP headers to include with each delivery.
        interval_hours:
          type:
          - integer
          - 'null'
          readOnly: true
          description: Hours between snapshot deliveries.
        max_items:
          type:
          - integer
          - 'null'
          readOnly: true
          description: Max rows a real snapshot delivers. Null or 0 falls back to
            the default item limit.
        secret_prefix:
          type: string
          readOnly: true
          description: Public prefix of the subscription signing secret.
        secret:
          type:
          - string
          - 'null'
          readOnly: true
          description: Full signing secret, returned only once, at the moment it is
            minted. Null when the subscription reuses the brand's existing default
            secret, since that value is never shown twice.
        enabled:
          type: boolean
          readOnly: true
          description: Whether this subscription is active and will be dispatched.
        created:
          type: string
          format: date-time
          readOnly: true
          description: Timestamp set once when the row is first created.
      required:
      - created
      - custom_headers
      - enabled
      - event_types
      - id
      - interval_hours
      - kind
      - max_items
      - resource
      - resource_filters
      - secret
      - secret_prefix
      - url
    Error400:
      type: object
      properties:
        log_id:
          type: string
        timestamp:
          type: string
          format: date-time
        error:
          type: object
          properties:
            code:
              type: string
              enum:
              - VALIDATION_ERROR
              - INVALID_ID
              - BRAND_ID_REQUIRED
            message:
              type: string
            details:
              type: object
              additionalProperties: true
          required:
          - code
          - message
          - details
      required:
      - log_id
      - timestamp
      - error
      example:
        log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        timestamp: '2026-01-01T00:00:00Z'
        error:
          code: VALIDATION_ERROR
          message: Request validation failed.
          details:
            field:
            - This query parameter is required.
      x-internal: true
    Error401:
      type: object
      properties:
        log_id:
          type: string
        timestamp:
          type: string
          format: date-time
        error:
          type: object
          properties:
            code:
              type: string
              enum:
              - INVALID_API_KEY
              - EXPIRED_API_KEY
              - REVOKED_API_KEY
            message:
              type: string
            details:
              type: object
              additionalProperties: true
          required:
          - code
          - message
          - details
      required:
      - log_id
      - timestamp
      - error
      example:
        log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        timestamp: '2026-01-01T00:00:00Z'
        error:
          code: INVALID_API_KEY
          message: API key is invalid.
          details: {}
      x-internal: true
    Error403:
      type: object
      properties:
        log_id:
          type: string
        timestamp:
          type: string
          format: date-time
        error:
          type: object
          properties:
            code:
              type: string
              enum:
              - PERMISSION_DENIED
            message:
              type: string
            details:
              type: object
              additionalProperties: true
          required:
          - code
          - message
          - details
      required:
      - log_id
      - timestamp
      - error
      example:
        log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        timestamp: '2026-01-01T00:00:00Z'
        error:
          code: PERMISSION_DENIED
          message: You do not have access to this resource.
          details: {}
      x-internal: true
    Error404:
      type: object
      properties:
        log_id:
          type: string
        timestamp:
          type: string
          format: date-time
        error:
          type: object
          properties:
            code:
              type: string
              enum:
              - NOT_FOUND
            message:
              type: string
            details:
              type: object
              additionalProperties: true
          required:
          - code
          - message
          - details
      required:
      - log_id
      - timestamp
      - error
      example:
        log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        timestamp: '2026-01-01T00:00:00Z'
        error:
          code: NOT_FOUND
          message: Resource not found.
          details: {}
      x-internal: true
    Error406:
      type: object
      properties:
        log_id:
          type: string
        timestamp:
          type: string
          format: date-time
        error:
          type: object
          properties:
            code:
              type: string
              enum:
              - NOT_ACCEPTABLE
            message:
              type: string
            details:
              type: object
              additionalProperties: true
          required:
          - code
          - message
          - details
      required:
      - log_id
      - timestamp
      - error
      example:
        log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        timestamp: '2026-01-01T00:00:00Z'
        error:
          code: NOT_ACCEPTABLE
          message: Could not satisfy the request Accept header.
          details: {}
      x-internal: true
    Error429:
      type: object
      properties:
        log_id:
          type: string
        timestamp:
          type: string
          format: date-time
        error:
          type: object
          properties:
            code:
              type: string
              enum:
              - RATE_LIMIT_EXCEEDED
            message:
              type: string
            details:
              type: object
              additionalProperties: true
          required:
          - code
          - message
          - details
      required:
      - log_id
      - timestamp
      - error
      example:
        log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        timestamp: '2026-01-01T00:00:00Z'
        error:
          code: RATE_LIMIT_EXCEEDED
          message: You have hit your API rate limit. Try again in 1 minute.
          details:
            retry_after_seconds: 60
            scopes:
            - global
      x-internal: true
    Error500:
      type: object
      properties:
        log_id:
          type: string
        timestamp:
          type: string
          format: date-time
        error:
          type: object
          properties:
            code:
              type: string
              enum:
              - INTERNAL_ERROR
            message:
              type: string
            details:
              type: object
              additionalProperties: true
          required:
          - code
          - message
          - details
      required:
      - log_id
      - timestamp
      - error
      example:
        log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        timestamp: '2026-01-01T00:00:00Z'
        error:
          code: INTERNAL_ERROR
          message: Something went wrong on our side.
          details: {}
      x-internal: true
    Error503:
      type: object
      properties:
        log_id:
          type: string
        timestamp:
          type: string
          format: date-time
        error:
          type: object
          properties:
            code:
              type: string
              enum:
              - SERVICE_UNAVAILABLE
            message:
              type: string
            details:
              type: object
              additionalProperties: true
          required:
          - code
          - message
          - details
      required:
      - log_id
      - timestamp
      - error
      example:
        log_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        timestamp: '2026-01-01T00:00:00Z'
        error:
          code: SERVICE_UNAVAILABLE
          message: The request took too long and was stopped. Narrow the date window
            or pass `brand_id`, then retry.
          details: {}
      x-internal: true
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: API key issued for a brand. Send it in every request as the `x-api-key`
        header. Click **Authorize** and paste the key to apply it to every endpoint
        in this UI.
servers:
- url: https://api.hellozelf.com
tags:
- name: AI Analysis
  description: Run AI analyses over content and comments. Start an analysis with a
    topic and a content filter, poll its runs for status, then fetch each run's result
    payload from `ai_analysis.results`.
- name: Analytics
  description: 'Compare a brand against its competitors: totals per brand and follower
    history over time. Every endpoint takes the same brand, platform and date window.'
- name: Engage
  description: 'What the brand itself published: the comments it posted, by the kind
    of content they were posted on, with the likes and replies they drew. Takes the
    same brand, platform and date window as Analytics.'
- name: Brands
  description: The brands (workspaces) an API key can access. Most content endpoints
    accept a `brand_id` from here to unlock brand-specific signals like ownership,
    tagging, and NPS. `brands.catalog` adds the brands Zelf curates for competitive
    benchmarking, which every key can read content for.
- name: Content
  description: Search, list, and aggregate content (posts, videos, images) across
    the platforms Zelf tracks. Supports rich filtering by metrics, platform, language,
    date window, and creator, plus optional brand context.
- name: Comment
  description: Search, list comments of a content including like count, author information
    and replies.
- name: Creator
  description: Look up creators and their public profile metrics, and run creator
    vetting analyses that score safety and brand fit.
- name: Creator Sync
  description: 'Add a creator''s profile and posts to Zelf so their videos can be
    searched and analyzed. Syncing is asynchronous: create a sync, poll it until it
    finishes, then list the creator''s videos with `content.list`.'
- name: Detection
  description: 'Find brands in social videos. Send a video and a list of brands. Zelf
    reads the whole video and finds each scene where a brand is identifiable: its
    logo or name is visible, or the brand is named out loud. Every appearance comes
    back with its timestamp and what was detected. Detection is asynchronous: create
    a detection, then poll it for the results.'
- name: Usage
  description: What your brand has spent against its limits, per group and per window,
    for both direct requests and AI clients. Reading it consumes nothing.
- name: Webhook Management
  description: Manage webhook subscriptions, signing secrets, test sends, and the
    delivery log for your brand. Create a subscription to start receiving snapshot
    or event pushes; the outbound payload shapes are documented under Webhook snapshots
    and Webhook events.
- name: Webhook snapshots
  description: Scheduled per-resource snapshot pushes. Zelf POSTs a manifest with
    a signed NDJSON download of the matching content or creator set.
- name: Webhook events
  description: Event-driven pushes. Zelf POSTs the payload inline when a tracked event
    (such as a completed creator vetting) occurs.
webhooks:
  ContentSnapshot:
    post:
      description: |
        Scheduled snapshot of the content set that a subscription matches.

        The subscription's `resource_filters` use the same query params as `content.list`, including ordering: rows are ordered by `posted_at` descending by default (override with an `order_by` in `resource_filters`). The snapshot is capped at the subscription's `max_items` (or the maximum when it is unset or `0`); a **test dispatch** always sends a small 10-row sample regardless of `max_items`.

        Delivered in `manifest` mode: `data.files` holds one signed, time-limited NDJSON download, and `data.statistics` carries the aggregate totals over the snapshot. `resource` is `content`.

        **Payload Zelf POSTs to the registered endpoint:**
        ```json
        {
          "id": "whd_8f2c1d4e9a",
          "type": "content.snapshot",
          "subscription_id": "whs_5Kq9Wd2pBxLm",
          "delivered_at": 1718000000,
          "livemode": true,
          "resource": "content",
          "resource_filters": {},
          "delivery_mode": "manifest",
          "data": {
            "item_count": 1,
            "statistics": {
              "post_count": 1280,
              "total_views": 4500000,
              "total_likes": 320000,
              "total_comments": 18000,
              "total_shares": 9500,
              "total_engagement": 14187,
              "total_earned_media_value_cents": 12500000,
              "total_media_duration": 53400.0,
              "engagement_rate": 0.077,
              "unique_creator_count": 412
            },
            "files": [
              {
                "format": "ndjson",
                "expires_at": 1718000000,
                "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                "size_bytes": 184320,
                "item_count": 1280,
                "download_url": "https://storage.googleapis.com/zelf-webhook-snapshots/webhooks/whd_8f2c1d4e.ndjson?X-Goog-Signature=..."
              }
            ]
          }
        }
        ```

        **Inside the NDJSON file** (retrieved from `data.files[0].download_url`), there is one JSON object per line. The file packs each row onto a single line; one row is shown expanded here for readability, matching the `content.list` response shape:
        ```json
        {
          "id": "con_DZaWwnKo5HThZ9bu",
          "platform_post_id": "7376543210987654321",
          "platform": "instagram",
          "content_form": "VIDEO",
          "thumbnail_url": "https://cdn.hellozelf.com/thumbnails/zelfai-launch.png",
          "posted_at": "2026-05-12T16:00:00Z",
          "view_count": 184230,
          "like_count": 12840,
          "comment_count": 932,
          "share_count": 415,
          "total_engagement": 14187,
          "language": "en",
          "media_duration": 42.0,
          "engagement_rate": 0.077,
          "earned_media_value_cents": 248000,
          "creator": {
            "id": "cr_pfqPv0FLsBRimHfi",
            "username": "zelfai",
            "name": "Zelf",
            "platform": "instagram",
            "platform_handle": "zelfai",
            "platform_url": "https://www.instagram.com/zelfai",
            "profile_picture_url": "https://cdn.hellozelf.com/avatars/zelfai.png",
            "profile_bio": "Creator and content intelligence for modern brands.",
            "follower_count": 53120,
            "active_content_count": 217
          },
          "brand_id": "brd_Lw6ZZmNYhW0P9k0x",
          "net_promoter_score": 1,
          "is_owned": true,
          "is_tagged": true,
          "is_influencer": true,
          "mention_type": "content",
          "caption": "Behind the scenes at Zelf.",
          "description": "How Zelf turns social signals into creator insights.",
          "transcript": "We walk through how Zelf ranks creators by brand fit.",
          "platform_url": "https://www.instagram.com/zelfai",
          "last_keyword": "zelf creators",
          "ai_summary": "A product walkthrough of Zelf creator analytics.",
          "hashtags": [
            "zelfai",
            "creatoreconomy"
          ]
        }
        ```
      summary: content.snapshot
      tags:
      - webhook snapshots
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookPush'
            examples:
              Content.snapshot:
                value:
                  id: whd_8f2c1d4e9a
                  type: content.snapshot
                  subscription_id: whs_5Kq9Wd2pBxLm
                  delivered_at: 1718000000
                  livemode: true
                  resource: content
                  resource_filters: {}
                  delivery_mode: manifest
                  data:
                    item_count: 1
                    statistics:
                      post_count: 1280
                      total_views: 4500000
                      total_likes: 320000
                      total_comments: 18000
                      total_shares: 9500
                      total_engagement: 14187
                      total_earned_media_value_cents: 12500000
                      total_media_duration: 53400.0
                      engagement_rate: 0.077
                      unique_creator_count: 412
                    files:
                    - format: ndjson
                      expires_at: 1718000000
                      sha256: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
                      size_bytes: 184320
                      item_count: 1280
                      download_url: https://storage.googleapis.com/zelf-webhook-snapshots/webhooks/whd_8f2c1d4e.ndjson?X-Goog-Signature=...
                summary: content.snapshot
        required: true
      responses:
        '200':
          description: Return a `2xx` status within the endpoint's timeout to acknowledge
            receipt. Any non-2xx status, or a timeout, is treated as a failed delivery
            and retried with backoff.
  CreatorSnapshot:
    post:
      description: |
        Scheduled snapshot of the creator set that a subscription matches.

        Delivered in `manifest` mode: `data.files` holds one signed, time-limited NDJSON download. Unlike `content.snapshot`, no `data.statistics` block is included. `resource` is `creator`.

        **Payload Zelf POSTs to the registered endpoint:**
        ```json
        {
          "id": "whd_8f2c1d4e9a",
          "type": "creator.snapshot",
          "subscription_id": "whs_5Kq9Wd2pBxLm",
          "delivered_at": 1718000000,
          "livemode": true,
          "resource": "creator",
          "resource_filters": {},
          "delivery_mode": "manifest",
          "data": {
            "item_count": 1,
            "files": [
              {
                "format": "ndjson",
                "expires_at": 1718000000,
                "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                "size_bytes": 184320,
                "item_count": 1280,
                "download_url": "https://storage.googleapis.com/zelf-webhook-snapshots/webhooks/whd_8f2c1d4e.ndjson?X-Goog-Signature=..."
              }
            ]
          }
        }
        ```

        **Inside the NDJSON file** (retrieved from `data.files[0].download_url`), there is one JSON object per line. The file packs each row onto a single line; one row is shown expanded here for readability, matching the `creators.list` response shape:
        ```json
        {
          "id": "cr_pfqPv0FLsBRimHfi",
          "username": "zelfai",
          "name": "Zelf",
          "email": "example@zelflive.com",
          "platform": "instagram",
          "platform_handle": "zelfai",
          "follower_count": 53120,
          "active_content_count": 217,
          "profile_bio": "Creator and content intelligence for modern brands.",
          "platform_url": "https://www.instagram.com/zelfai",
          "profile_picture_url": "https://cdn.hellozelf.com/avatars/zelfai.png"
        }
        ```
      summary: creator.snapshot
      tags:
      - webhook snapshots
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookPush'
            examples:
              Creator.snapshot:
                value:
                  id: whd_8f2c1d4e9a
                  type: creator.snapshot
                  subscription_id: whs_5Kq9Wd2pBxLm
                  delivered_at: 1718000000
                  livemode: true
                  resource: creator
                  resource_filters: {}
                  delivery_mode: manifest
                  data:
                    item_count: 1
                    files:
                    - format: ndjson
                      expires_at: 1718000000
                      sha256: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
                      size_bytes: 184320
                      item_count: 1280
                      download_url: https://storage.googleapis.com/zelf-webhook-snapshots/webhooks/whd_8f2c1d4e.ndjson?X-Goog-Signature=...
                summary: creator.snapshot
        required: true
      responses:
        '200':
          description: Return a `2xx` status within the endpoint's timeout to acknowledge
            receipt. Any non-2xx status, or a timeout, is treated as a failed delivery
            and retried with backoff.
  CreatorVettingCompleted:
    post:
      description: |
        Fires when a creator vetting analysis run reaches a terminal state (`COMPLETED` or `FAILED`).

        Delivered in `inline` mode as a lightweight notification: `data.items[0]` carries the vetting id, its `analysis_type`, `modified_at`, and each run's `id` and `status`, rather than the full payload. Retrieve the `result` from `creator_vetting.results` (pass `vetting_id=<id>`). `resource` is `null`.

        **Payload Zelf POSTs to the registered endpoint:**
        ```json
        {
          "id": "whd_8f2c1d4e9a",
          "type": "creator_vetting.completed",
          "subscription_id": "whs_5Kq9Wd2pBxLm",
          "delivered_at": 1718000000,
          "livemode": true,
          "resource": null,
          "resource_filters": {},
          "delivery_mode": "inline",
          "data": {
            "item_count": 1,
            "items": [
              {
                "id": "bod_huNssMQX4FTvALEY",
                "analysis_type": "CREATOR_ANALYSIS",
                "modified_at": "2026-06-21T12:00:00Z",
                "analyses": [
                  {
                    "id": "ban_xJZFbcWNkF9M3brf",
                    "status": "COMPLETED"
                  },
                  {
                    "id": "ban_Nc7m1Pe6Qd4Kf0Az",
                    "status": "IN_PROGRESS"
                  }
                ]
              }
            ]
          }
        }
        ```
      summary: creator_vetting.completed
      tags:
      - webhook events
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookPush'
            examples:
              CreatorVetting.completed:
                value:
                  id: whd_8f2c1d4e9a
                  type: creator_vetting.completed
                  subscription_id: whs_5Kq9Wd2pBxLm
                  delivered_at: 1718000000
                  livemode: true
                  resource: null
                  resource_filters: {}
                  delivery_mode: inline
                  data:
                    item_count: 1
                    items:
                    - id: bod_huNssMQX4FTvALEY
                      analysis_type: CREATOR_ANALYSIS
                      modified_at: '2026-06-21T12:00:00Z'
                      analyses:
                      - id: ban_xJZFbcWNkF9M3brf
                        status: COMPLETED
                      - id: ban_Nc7m1Pe6Qd4Kf0Az
                        status: IN_PROGRESS
                summary: creator_vetting.completed
        required: true
      responses:
        '200':
          description: Return a `2xx` status within the endpoint's timeout to acknowledge
            receipt. Any non-2xx status, or a timeout, is treated as a failed delivery
            and retried with backoff.
  AiAnalysisCompleted:
    post:
      description: |
        Fires when an AI analysis run reaches a terminal state (`COMPLETED` or `FAILED`).

        Delivered in `inline` mode as a lightweight notification: `data.items[0]` carries the analysis id, its `analysis_type`, `modified_at`, and each run's `id` and `status`, rather than the full payload. Retrieve the `result` from `ai_analysis.results` (pass `analysis_id=<id>`). `resource` is `null`. An analysis with multiple scopes fires once per run, as each reaches a terminal state.

        **Payload Zelf POSTs to the registered endpoint:**
        ```json
        {
          "id": "whd_8f2c1d4e9a",
          "type": "ai_analysis.completed",
          "subscription_id": "whs_5Kq9Wd2pBxLm",
          "delivered_at": 1718000000,
          "livemode": true,
          "resource": null,
          "resource_filters": {},
          "delivery_mode": "inline",
          "data": {
            "item_count": 1,
            "items": [
              {
                "id": "bod_huNssMQX4FTvALEY",
                "analysis_type": "AI_ANALYSIS",
                "modified_at": "2026-06-21T12:00:00Z",
                "analyses": [
                  {
                    "id": "ban_xJZFbcWNkF9M3brf",
                    "status": "COMPLETED"
                  },
                  {
                    "id": "ban_Nc7m1Pe6Qd4Kf0Az",
                    "status": "IN_PROGRESS"
                  }
                ]
              }
            ]
          }
        }
        ```
      summary: ai_analysis.completed
      tags:
      - webhook events
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookPush'
            examples:
              AiAnalysis.completed:
                value:
                  id: whd_8f2c1d4e9a
                  type: ai_analysis.completed
                  subscription_id: whs_5Kq9Wd2pBxLm
                  delivered_at: 1718000000
                  livemode: true
                  resource: null
                  resource_filters: {}
                  delivery_mode: inline
                  data:
                    item_count: 1
                    items:
                    - id: bod_huNssMQX4FTvALEY
                      analysis_type: AI_ANALYSIS
                      modified_at: '2026-06-21T12:00:00Z'
                      analyses:
                      - id: ban_xJZFbcWNkF9M3brf
                        status: COMPLETED
                      - id: ban_Nc7m1Pe6Qd4Kf0Az
                        status: IN_PROGRESS
                summary: ai_analysis.completed
        required: true
      responses:
        '200':
          description: Return a `2xx` status within the endpoint's timeout to acknowledge
            receipt. Any non-2xx status, or a timeout, is treated as a failed delivery
            and retried with backoff.
x-tagGroups:
- name: APIs (v1)
  tags:
  - AI Analysis
  - Analytics
  - Brands
  - Comment
  - Content
  - Creator
  - Creator Sync
  - Detection
  - Engage
  - Usage
  - Webhook Management
- name: Webhooks (v1)
  tags:
  - webhooks
- name: Models
  tags:
  - models
security:
- ApiKey: []
