openapi: 3.0.3
info:
  title: Metricanic API
  version: 1.0.0
  description: >
    # Metricanic API


    Use the Metricanic REST API to manage campaigns, traffic sources, affiliate networks, landings,
    offers and rotators, and to read performance reports and individual events.


    Base URL: `https://panel.metricanic.com/api/v1`. Requests require an account-scoped Bearer key
    created in **Profile → API keys**. Use `read` for GET requests or `write` for supported
    mutations. Keys are intended for server-side integrations. Cross-origin browser writes are
    rejected.


    Start with the [API quickstart](https://docs.metricanic.com/api-reference/introduction), then
    follow the guides for
    [authentication](https://docs.metricanic.com/api-reference/authentication), [reports and event
    pagination](https://docs.metricanic.com/api-reference/reports-and-events), and [errors and
    retries](https://docs.metricanic.com/api-reference/errors).


    Only the operations in this specification are supported under `/api/v1`. Individual endpoint
    schemas define the request and response shapes. List endpoints do not all share the same
    pagination model. The product's OAuth MCP connection has separate, read-only capabilities.
  license:
    name: Proprietary
    url: https://metricanic.com/legal/terms
servers:
  - url: https://panel.metricanic.com
security:
  - bearerAuth: []
paths:
  /api/v1/whoami:
    get:
      summary: Who am I
      description:
        Identifies the caller. Works with both Bearer tokens and panel sessions. The primary
        connectivity and auth smoke check for integrations. For Bearer callers returns the key id
        and label the token was issued for.
      responses:
        '200':
          description: Caller identity
          content:
            application/json:
              example:
                ok: true
                data:
                  tenantId: 7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d
                  provider: api-key
                  scope: read
                  keyId: 3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f
                  label: integration-prod
              schema:
                $ref: '#/components/schemas/WhoAmIResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: whoAmI
      tags:
        - Auth
  /api/v1/campaigns:
    get:
      summary: List campaigns
      description: Public campaign list using the canonical campaign response shape.
      parameters:
        - in: query
          name: includeArchived
          schema:
            type: string
            enum:
              - '1'
        - in: query
          name: limit
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 100
        - in: query
          name: query
          schema:
            type: string
        - in: query
          name: ids
          description: Comma-separated campaign ids. When present, filters by ids and still applies limit.
          schema:
            type: string
      responses:
        '200':
          description: Campaign list
          content:
            application/json:
              example:
                ok: true
                data:
                  campaigns:
                    - id: 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
                      name: us-search-mainstream
                      displayName: US Search Mainstream
                      status: active
                      trafficSourceId: 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
                      country: US
                      cost:
                        mode: cpv
                        amount: 0.012
                      tracking:
                        method: redirect
                        domainId: null
                        includePrefetch: false
                      destination:
                        type: rotator
                        rotatorId: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
                      createdAt: 2026-05-15 09:30:00
                  meta:
                    limit: 100
                    count: 1
              schema:
                $ref: '#/components/schemas/CampaignListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: listCampaigns
      tags:
        - Campaigns
    post:
      summary: Create campaign
      description: >
        Creates a campaign using a domain-oriented public contract. Campaigns attach to an existing
        rotator with `destination.mode=use_rotator`. Create or edit rotators through the rotator
        API.
      requestBody:
        required: true
        content:
          application/json:
            example:
              name: us-search-mainstream
              trafficSourceId: 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
              country: US
              cost:
                mode: cpv
                amount: 0.012
              tracking:
                method: redirect
              destination:
                mode: use_rotator
                rotatorId: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
            schema:
              $ref: '#/components/schemas/CampaignCreatePayload'
      responses:
        '201':
          description: Campaign created
          content:
            application/json:
              example:
                ok: true
                data:
                  campaign:
                    id: 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
                    name: us-search-mainstream
                    displayName: US Search Mainstream
                    status: active
                    trafficSourceId: 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
                    country: US
                    cost:
                      mode: cpv
                      amount: 0.012
                    tracking:
                      method: redirect
                      domainId: null
                      includePrefetch: false
                    destination:
                      type: rotator
                      rotatorId: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
                    createdAt: 2026-05-15 09:30:00
                  publish:
                    status: queued
              schema:
                $ref: '#/components/schemas/CampaignMutationResponse'
        '400':
          description: Invalid payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Referenced traffic source, rotator, domain, offer, or landing not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Referenced resource is not publishable or compatible
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: Request body too large
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: createCampaign
      tags:
        - Campaigns
  /api/v1/campaigns/{id}:
    get:
      summary: Get campaign
      description: Returns a single campaign in the canonical campaign shape.
      responses:
        '200':
          description: Campaign
          content:
            application/json:
              example:
                ok: true
                data:
                  campaign:
                    id: 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
                    name: us-search-mainstream
                    displayName: US Search Mainstream
                    status: active
                    trafficSourceId: 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
                    country: US
                    cost:
                      mode: cpv
                      amount: 0.012
                    tracking:
                      method: redirect
                      domainId: null
                      includePrefetch: false
                    destination:
                      type: rotator
                      rotatorId: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
                    createdAt: 2026-05-15 09:30:00
              schema:
                $ref: '#/components/schemas/CampaignResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Campaign not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: getCampaign
      tags:
        - Campaigns
    patch:
      summary: Update campaign
      description: >
        Partially updates campaign-level fields. PATCH may switch to an existing rotator using
        `destination.mode=use_rotator`. Creating a new rotator after campaign creation should use
        the rotator API followed by this PATCH.
      requestBody:
        required: true
        content:
          application/json:
            example:
              cost:
                mode: cpv
                amount: 0.015
            schema:
              $ref: '#/components/schemas/CampaignPatchPayload'
      responses:
        '200':
          description: Campaign updated
          content:
            application/json:
              example:
                ok: true
                data:
                  campaign:
                    id: 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
                    name: us-search-mainstream
                    displayName: US Search Mainstream
                    status: active
                    trafficSourceId: 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
                    country: US
                    cost:
                      mode: cpv
                      amount: 0.015
                    tracking:
                      method: redirect
                      domainId: null
                      includePrefetch: false
                    destination:
                      type: rotator
                      rotatorId: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
                    createdAt: 2026-05-15 09:30:00
                  publish:
                    status: published
              schema:
                $ref: '#/components/schemas/CampaignMutationResponse'
        '400':
          description: Invalid payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Campaign or referenced resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Referenced resource is not publishable or compatible
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: Request body too large
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: updateCampaign
      tags:
        - Campaigns
    delete:
      summary: Archive campaign
      description:
        Archives the campaign. Archived campaigns stop being served and can be restored via
        POST /api/v1/campaigns/restore.
      responses:
        '200':
          description: Archived
          content:
            application/json:
              example:
                ok: true
                data: null
              schema:
                $ref: '#/components/schemas/Ok'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: archiveCampaign
      tags:
        - Campaigns
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
          minLength: 36
          maxLength: 36
  /api/v1/campaigns/{id}/goals:
    get:
      summary: List goals
      description: Returns the campaign goal rule hierarchy - prioritized rules with conditions and
        per-goal actions (payout, cost, conversion and postback flags). Read-only in public API v1.
        Goal rules are managed in the panel.
      responses:
        '200':
          description: Campaign goal hierarchy
          content:
            application/json:
              example:
                ok: true
                data:
                  rules:
                    - id: 0f1e2d3c-4b5a-4c6d-8e7f-9a0b1c2d3e4f
                      priority: 0
                      enabled: true
                      label: Default
                      conditions: null
                      actions:
                        - idx: 0
                          label: purchase
                          enabled: true
                          payoutType: auto
                          payoutFixed: null
                          costType: none
                          costFixed: null
                          costShare: null
                          postbackUrl: null
                          postbackEnabled: false
                          storeValue: true
                          isConversion: true
                          affectsCost: false
                          affectsRevenue: true
              schema:
                $ref: '#/components/schemas/GoalRuleHierarchyResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: listCampaignGoals
      tags:
        - Campaigns
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
  /api/v1/landings:
    get:
      summary: List landings
      description: Lists landings of the tenant ordered by creation time.
      parameters:
        - in: query
          name: query
          description: 'Case-insensitive name filter. Alias: q.'
          schema:
            type: string
        - in: query
          name: ids
          description: Comma-separated ids. When present, filters by ids.
          schema:
            type: string
        - in: query
          name: country
          description: Filter by ISO 3166-1 alpha-2 country code.
          schema:
            type: string
        - in: query
          name: includeArchived
          schema:
            type: string
      responses:
        '200':
          description: Landing list
          content:
            application/json:
              example:
                ok: true
                data:
                  - id: 4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f
                    name: Main LP EN
                    url: https://lp.example.com/en/
                    country: US
                    status: active
                    createdAt: 2026-05-20 14:02:11
              schema:
                $ref: '#/components/schemas/LandingListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: listLandings
      tags:
        - Landings
    post:
      summary: Create landing
      description: Creates a landing and returns its id.
      requestBody:
        required: true
        content:
          application/json:
            example:
              name: Main LP EN
              url: https://lp.example.com/en/
              country: US
            schema:
              $ref: '#/components/schemas/LandingCreatePayload'
      responses:
        '200':
          description: Created landing id
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f
              schema:
                $ref: '#/components/schemas/LandingCreateResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: createLanding
      tags:
        - Landings
  /api/v1/landings/{id}:
    get:
      summary: Get landing
      description: Returns a single landing of the tenant by id.
      responses:
        '200':
          description: Landing record
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f
                  name: Main LP EN
                  url: https://lp.example.com/en/
                  country: US
                  status: active
                  createdAt: 2026-05-20 14:02:11
              schema:
                $ref: '#/components/schemas/LandingDetailResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: getLanding
      tags:
        - Landings
    patch:
      summary: Update landing
      description: Partially updates a landing. Only provided fields change.
      requestBody:
        required: true
        content:
          application/json:
            example:
              name: Main LP EN v2
            schema:
              $ref: '#/components/schemas/LandingPatchPayload'
      responses:
        '200':
          description: Updated
          content:
            application/json:
              example:
                ok: true
                data: null
              schema:
                $ref: '#/components/schemas/Ok'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: updateLanding
      tags:
        - Landings
    delete:
      summary: Delete landing
      description: Archives the landing (soft delete). Fails with 409 while the landing is used by a
        rotator stream. Restore later via POST /api/v1/landings/restore.
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              example:
                ok: true
                data: null
              schema:
                $ref: '#/components/schemas/Ok'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Landing is used by rotators
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: deleteLanding
      tags:
        - Landings
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
  /api/v1/offers:
    get:
      summary: List offers
      description: Lists offers of the tenant, including the linked affiliate network reference.
      parameters:
        - in: query
          name: query
          description: 'Case-insensitive name filter. Alias: q.'
          schema:
            type: string
        - in: query
          name: ids
          description: Comma-separated ids. When present, filters by ids.
          schema:
            type: string
        - in: query
          name: country
          description: Filter by ISO 3166-1 alpha-2 country code.
          schema:
            type: string
        - in: query
          name: includeArchived
          schema:
            type: string
      responses:
        '200':
          description: Offer list
          content:
            application/json:
              example:
                ok: true
                data:
                  - id: 2e3f4a5b-6c7d-4e8f-9a0b-1c2d3e4f5a6b
                    name: Sweeps CC Submit US
                    url: https://network.example.com/click?offer=123
                    appendClickId: true
                    affiliateNetwork:
                      id: 8d9e0f1a-2b3c-4d5e-8f6a-7b8c9d0e1f2a
                      name: ExampleAds
                    country: US
                    status: active
                    createdAt: 2026-05-21 10:12:45
              schema:
                $ref: '#/components/schemas/OfferListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: listOffers
      tags:
        - Offers
    post:
      summary: Create offer
      description: Creates an offer and returns its id.
      requestBody:
        required: true
        content:
          application/json:
            example:
              name: Sweeps CC Submit US
              url: https://network.example.com/click?offer=123
              affiliateNetworkId: 8d9e0f1a-2b3c-4d5e-8f6a-7b8c9d0e1f2a
              appendClickId: true
              country: US
            schema:
              $ref: '#/components/schemas/OfferCreatePayload'
      responses:
        '200':
          description: Created offer id
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 2e3f4a5b-6c7d-4e8f-9a0b-1c2d3e4f5a6b
              schema:
                $ref: '#/components/schemas/OfferCreateResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: createOffer
      tags:
        - Offers
  /api/v1/offers/{id}:
    get:
      summary: Get offer
      description: Returns a single offer of the tenant by id.
      responses:
        '200':
          description: Offer record
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 2e3f4a5b-6c7d-4e8f-9a0b-1c2d3e4f5a6b
                  name: Sweeps CC Submit US
                  url: https://network.example.com/click?offer=123
                  appendClickId: true
                  affiliateNetwork:
                    id: 8d9e0f1a-2b3c-4d5e-8f6a-7b8c9d0e1f2a
                    name: ExampleAds
                  country: US
                  status: active
                  createdAt: 2026-05-21 10:12:45
              schema:
                $ref: '#/components/schemas/OfferDetailResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: getOffer
      tags:
        - Offers
    patch:
      summary: Update offer
      description: Partially updates an offer. Only provided fields change.
      requestBody:
        required: true
        content:
          application/json:
            example:
              appendClickId: false
            schema:
              $ref: '#/components/schemas/OfferUpdatePayload'
      responses:
        '200':
          description: Updated
          content:
            application/json:
              example:
                ok: true
                data: null
              schema:
                $ref: '#/components/schemas/Ok'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: updateOffer
      tags:
        - Offers
    delete:
      summary: Delete offer
      description:
        Archives the offer (soft delete). Fails with 409 while the offer is used by a rotator
        stream. Restore later via POST /api/v1/offers/restore.
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              example:
                ok: true
                data: null
              schema:
                $ref: '#/components/schemas/Ok'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Offer is used by rotators
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: deleteOffer
      tags:
        - Offers
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
  /api/v1/reports:
    get:
      summary: Query reports
      description: >-
        Returns aggregate performance grouped by your chosen dimensions. Use it to compare
        campaigns, destinations, and traffic segments. Filter and sort the results to investigate a
        specific question.


        **Dimensions** (`dimensions`, comma-separated):


        - Time: `day`, `hour`, `month`, `dayOfWeek`.

        - Entities: `campaign`, `traffic`, `landing`, `offer`, `affiliateNetwork`, `rotator`,
        `rotatorPath`.

        - Device: `deviceType`, `deviceBrand`, `deviceModel`, `osFamily`, `osVersion`,
        `browserName`, `browserVersion`.

        - Location and network: `country`, `continent`, `region`, `city`, `timezone`, `language`,
        `ipType`, `asn`, `asnOrg`, `isp`, `colo`, `connType`, `connSpeed`.

        - Traffic quality: `threat`, `isBot`.

        - Referrer: `refDomain`.

        - Custom source parameters: `token1`..`token15`.

        - Tags: `tag`, which requires a single campaign filter and a base dimension.


        **Metrics** (`metrics`, comma-separated): `visits`, `uniqueVisits`, `lpclicks`,
        `uniqueClicks`, `ctr`, `uctr`, `conversions`, `cr`, `cv`, `ucr`, `ucv`, `revenue`, `cost`,
        `profit`, `roi`, `profitMargin`, `epv`, `epc`, `epa`, `epuc`, `cpv`, `cpc`, `cpa`, `cpuc`,
        `ppuc`, `suspicious`, `suspiciousPct`, and per-goal breakdowns `event1`..`event10`,
        `revenue1`..`revenue10`, `cost1`..`cost10`, `value1`..`value10`.


        Raw event dimensions like `id`, `external`, `ip`, `refUrl` and other non-rollup fields are
        not supported here. Use `/api/v1/events` for raw inspection. Row keys in the response match
        the requested dimension and metric IDs.
      parameters:
        - in: query
          name: timezone
          description:
            IANA timezone for date bucketing and range boundaries. Defaults to the account timezone
            when omitted. Supply it explicitly for reproducible reports.
          schema:
            type: string
        - in: query
          name: filter
          description:
            'Comma-separated filter expressions, each URL-encoded as `field<op>value[|value2...]`
            where `<op>` is one of `=`, `!=`, `>`, `<`. Example: `filter=country=US|CA,ctr>0.05`.
            Field is any report dimension or metric. Max 6 filters, 1000 values per filter.'
          schema:
            type: string
        - in: query
          name: dimensions
          description:
            Comma-separated aggregate report dimensions. Raw event dimensions are rejected. `tag`
            is a synthetic report column and requires a single campaign plus a base dimension.
          schema:
            type: string
        - in: query
          name: metrics
          schema:
            type: string
        - in: query
          name: status
          schema:
            type: string
            enum:
              - all
              - active
              - archived
        - in: query
          name: from
          schema:
            type: string
        - in: query
          name: to
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 100
        - in: query
          name: offset
          schema:
            type: integer
            minimum: 0
            default: 0
        - in: query
          name: totals
          schema:
            type: string
        - in: query
          name: sortBy
          schema:
            type: string
        - in: query
          name: sortDir
          schema:
            type: string
            enum:
              - asc
              - desc
        - in: query
          name: primaryDimension
          schema:
            type: string
          description: Canonical dimension id used for tagging.
      responses:
        '200':
          description: Report rows
          content:
            application/json:
              example:
                ok: true
                data:
                  meta:
                    limit: 100
                    offset: 0
                    totalRows: 1
                    timezone: UTC
                    totals:
                      visits: 1520
                      conversions: 12
                      cost: 18.24
                      revenue: 54
                  rows:
                    - campaign: 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
                      visits: 1520
                      conversions: 12
                      cost: 18.24
                      revenue: 54
              schema:
                $ref: '#/components/schemas/ReportListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: queryReports
      tags:
        - Reports
  /api/v1/rotators:
    get:
      summary: List rotators
      description: Lists rotators of the tenant ordered by creation time.
      parameters:
        - in: query
          name: query
          description: 'Case-insensitive name filter. Alias: q.'
          schema:
            type: string
        - in: query
          name: ids
          description: Comma-separated ids. When present, filters by ids.
          schema:
            type: string
        - in: query
          name: country
          description: Filter by ISO 3166-1 alpha-2 country code.
          schema:
            type: string
        - in: query
          name: all
          schema:
            type: string
        - in: query
          name: includeArchived
          schema:
            type: string
      responses:
        '200':
          description: Rotator list
          content:
            application/json:
              example:
                ok: true
                data:
                  - id: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
                    name: US Mainstream Split
                    shared: false
                    country: US
                    status: active
                    createdAt: 2026-05-22 11:05:00
              schema:
                $ref: '#/components/schemas/RotatorListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: listRotators
      tags:
        - Rotators
    post:
      summary: Create rotator
      description:
        Creates a rotator and returns its id. Routing rules are managed in the panel. Inspect
        them via GET /api/v1/rotators/{id}/rules.
      requestBody:
        required: true
        content:
          application/json:
            example:
              name: US Mainstream Split
              shared: false
              country: US
            schema:
              $ref: '#/components/schemas/RotatorNewPayload'
      responses:
        '200':
          description: Created rotator id
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
              schema:
                $ref: '#/components/schemas/RotatorCreateResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: createRotator
      tags:
        - Rotators
  /api/v1/rotators/{id}:
    get:
      summary: Get rotator
      description: Returns a single rotator of the tenant by id.
      responses:
        '200':
          description: Rotator detail
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
                  name: US Mainstream Split
                  shared: false
                  country: US
                  status: active
                  createdAt: 2026-05-22 11:05:00
              schema:
                $ref: '#/components/schemas/RotatorDetailResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: getRotator
      tags:
        - Rotators
    patch:
      summary: Update rotator
      description: Partially updates a rotator. Only provided fields change. Returns the updated entity.
      requestBody:
        required: true
        content:
          application/json:
            example:
              name: US Mainstream Split v2
            schema:
              $ref: '#/components/schemas/RotatorUpdatePayload'
      responses:
        '200':
          description: Updated
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
                  name: US Mainstream Split v2
                  shared: false
                  country: US
                  status: active
                  createdAt: 2026-05-22 11:05:00
              schema:
                $ref: '#/components/schemas/RotatorDetailResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: updateRotator
      tags:
        - Rotators
    delete:
      summary: Archive rotator
      description: Archives the rotator. Restore later via POST /api/v1/rotators/restore.
      responses:
        '200':
          description: Archived
          content:
            application/json:
              example:
                ok: true
                data: null
              schema:
                $ref: '#/components/schemas/Ok'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: archiveRotator
      tags:
        - Rotators
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
  /api/v1/rotators/{id}/rules:
    get:
      summary: Get rotator rules
      description: 'Returns rotator routing rules ordered by priority: rule conditions plus weighted
        streams of landings and offers. Read-only in public API v1. Rules are managed in the panel.'
      responses:
        '200':
          description: Rules list
          content:
            application/json:
              example:
                ok: true
                data:
                  - id: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5e
                    label: Default
                    priority: 0
                    enabled: true
                    conditions: {}
                    streams:
                      - streamIdx: 0
                        name: Stream A
                        weight: 100
                        enabled: true
                        pathId: 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e
                        deletedAt: null
                        landers:
                          - landingId: 4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f
                            weight: 100
                            enabled: true
                        offers:
                          - offerId: 2e3f4a5b-6c7d-4e8f-9a0b-1c2d3e4f5a6b
                            weight: 100
                            enabled: true
              schema:
                $ref: '#/components/schemas/RotatorRulesResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: getRotatorRules
      tags:
        - Rotators
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
  /api/v1/rotator-paths:
    get:
      summary: List rotator paths
      description: Lists rotator paths - stable stream identities that attribute traffic to a stream
        across rule edits.
      parameters:
        - in: query
          name: rotatorId
          schema:
            type: string
          description: Filter by rotator id
        - in: query
          name: ids
          schema:
            type: string
          description: Comma separated list of path ids
        - in: query
          name: includeArchived
          schema:
            type: string
          description: Include archived paths when set to 1
      responses:
        '200':
          description: Path list
          content:
            application/json:
              example:
                ok: true
                data:
                  - id: 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e
                    rotatorId: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
                    name: Stream A
                    deletedAt: null
                    createdAt: 1749980000
                    updatedAt: 1750980000
              schema:
                $ref: '#/components/schemas/RotatorPathsResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      tags:
        - Rotators
      operationId: listRotatorPaths
  /api/v1/traffic-sources:
    get:
      summary: List traffic sources
      description:
        Lists traffic sources of the tenant, including click id / cost parameter mapping and
        source postback settings.
      parameters:
        - in: query
          name: query
          description: 'Case-insensitive name filter. Alias: q.'
          schema:
            type: string
        - in: query
          name: ids
          description: Comma-separated ids. When present, filters by ids.
          schema:
            type: string
        - in: query
          name: includeArchived
          schema:
            type: string
      responses:
        '200':
          description: Source list
          content:
            application/json:
              example:
                ok: true
                data:
                  - id: 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
                    name: PropellerAds
                    clickIdParam: clickid
                    costParam: cost
                    status: active
                    createdAt: 2026-05-18 08:00:00
                    postbackUrl: https://ads.example.com/postback?cid={clickid}&payout={payout}
                    clickIdMacro: ${SUBID}
                    costMacro: ${COST}
              schema:
                $ref: '#/components/schemas/TrafficSourceListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: listTrafficSources
      tags:
        - TrafficSources
    post:
      summary: Create traffic source
      description: Creates a traffic source and returns its id.
      requestBody:
        required: true
        content:
          application/json:
            example:
              name: PropellerAds
              clickIdParam: clickid
              costParam: cost
              postbackUrl: https://ads.example.com/postback
              clickIdMacro: ${SUBID}
              costMacro: ${COST}
            schema:
              $ref: '#/components/schemas/TrafficSourcePayload'
      responses:
        '200':
          description: Created id
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
              schema:
                $ref: '#/components/schemas/TrafficSourceCreateResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: createTrafficSource
      tags:
        - TrafficSources
  /api/v1/traffic-sources/{id}:
    get:
      summary: Get traffic source
      description: Returns a single traffic source.
      responses:
        '200':
          description: Source data
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
                  name: PropellerAds
                  clickIdParam: clickid
                  costParam: cost
                  status: active
                  createdAt: 2026-05-18 08:00:00
                  postbackUrl: https://ads.example.com/postback?cid={clickid}&payout={payout}
                  clickIdMacro: ${SUBID}
                  costMacro: ${COST}
              schema:
                $ref: '#/components/schemas/TrafficSourceDetailResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: getTrafficSource
      tags:
        - TrafficSources
    patch:
      summary: Update traffic source
      description: Partially updates a traffic source. Only provided fields change. Returns the updated entity.
      requestBody:
        required: true
        content:
          application/json:
            example:
              costParam: bid
            schema:
              $ref: '#/components/schemas/TrafficSourcePatchPayload'
      responses:
        '200':
          description: Updated
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
                  name: PropellerAds
                  clickIdParam: clickid
                  costParam: bid
                  status: active
                  createdAt: 2026-05-18 08:00:00
                  postbackUrl: https://ads.example.com/postback?cid={clickid}&payout={payout}
                  clickIdMacro: ${SUBID}
                  costMacro: ${COST}
              schema:
                $ref: '#/components/schemas/TrafficSourceDetailResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: updateTrafficSource
      tags:
        - TrafficSources
    delete:
      summary: Delete traffic source
      description:
        Archives the traffic source (soft delete). Fails with 409 while the traffic source is
        used by campaigns. Restore later via POST /api/v1/traffic-sources/restore.
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              example:
                ok: true
                data: null
              schema:
                $ref: '#/components/schemas/Ok'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Traffic source is used by campaigns
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: deleteTrafficSource
      tags:
        - TrafficSources
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
  /api/v1/affiliate-networks:
    get:
      summary: List affiliate networks
      description: Lists affiliate networks of the tenant.
      parameters:
        - in: query
          name: query
          description: 'Case-insensitive name filter. Alias: q.'
          schema:
            type: string
        - in: query
          name: ids
          description: Comma-separated ids. When present, filters by ids.
          schema:
            type: string
        - in: query
          name: includeArchived
          schema:
            type: string
      responses:
        '200':
          description: Networks list
          content:
            application/json:
              example:
                ok: true
                data:
                  - id: 8d9e0f1a-2b3c-4d5e-8f6a-7b8c9d0e1f2a
                    name: ExampleAds
                    displayName: ExampleAds
                    clickIdEnabled: true
                    clickIdParam: aff_click_id
                    conversionPostbackTemplate: null
                    status: active
                    createdAt: 2026-05-19 16:40:22
              schema:
                $ref: '#/components/schemas/AffiliateNetworkListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: listAffiliateNetworks
      tags:
        - AffiliateNetworks
    post:
      summary: Create affiliate network
      description: Creates an affiliate network and returns its id.
      requestBody:
        required: true
        content:
          application/json:
            example:
              name: ExampleAds
              clickIdEnabled: true
              clickIdParam: aff_click_id
            schema:
              $ref: '#/components/schemas/AffiliateNetworkInput'
      responses:
        '200':
          description: Created id
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 8d9e0f1a-2b3c-4d5e-8f6a-7b8c9d0e1f2a
              schema:
                $ref: '#/components/schemas/AffiliateNetworkCreateResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: createAffiliateNetwork
      tags:
        - AffiliateNetworks
  /api/v1/affiliate-networks/{id}:
    get:
      summary: Affiliate network detail
      description: Returns a single affiliate network.
      responses:
        '200':
          description: Details
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 8d9e0f1a-2b3c-4d5e-8f6a-7b8c9d0e1f2a
                  name: ExampleAds
                  displayName: ExampleAds
                  clickIdEnabled: true
                  clickIdParam: aff_click_id
                  conversionPostbackTemplate: null
                  status: active
                  createdAt: 2026-05-19 16:40:22
              schema:
                $ref: '#/components/schemas/AffiliateNetworkDetailResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: getAffiliateNetwork
      tags:
        - AffiliateNetworks
    patch:
      summary: Update affiliate network
      description: Partially updates an affiliate network. Only provided fields change. Returns the
        updated entity.
      requestBody:
        required: true
        content:
          application/json:
            example:
              clickIdParam: click_id
            schema:
              $ref: '#/components/schemas/AffiliateNetworkUpdate'
      responses:
        '200':
          description: Updated
          content:
            application/json:
              example:
                ok: true
                data:
                  id: 8d9e0f1a-2b3c-4d5e-8f6a-7b8c9d0e1f2a
                  name: ExampleAds
                  displayName: ExampleAds
                  clickIdEnabled: true
                  clickIdParam: click_id
                  conversionPostbackTemplate: null
                  status: active
                  createdAt: 2026-05-19 16:40:22
              schema:
                $ref: '#/components/schemas/AffiliateNetworkDetailResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: updateAffiliateNetwork
      tags:
        - AffiliateNetworks
    delete:
      summary: Delete affiliate network
      description:
        Archives the affiliate network (soft delete). Fails with 409 while the network has
        active offers. Restore later via POST /api/v1/affiliate-networks/restore.
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              example:
                ok: true
                data: null
              schema:
                $ref: '#/components/schemas/Ok'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Affiliate network has active offers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: deleteAffiliateNetwork
      tags:
        - AffiliateNetworks
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
  /api/v1/events:
    get:
      summary: List events
      description: 'Raw event stream for inspection and debugging - visits, landing page clicks,
        conversion events and outbound postbacks. Uses cursor pagination: pass meta.nextCursor back
        via the cursor parameter while hasMore is true. Rows include request-level fields
        (requestIp, requestUa, requestReferer) captured from your own tracked traffic - treat
        responses as sensitive and query server-side. This endpoint is backed by the analytical
        store and can respond 503 while data is temporarily unavailable.'
      parameters:
        - in: query
          name: campaignId
          description: Comma-separated campaign ids to filter by.
          schema:
            type: string
        - in: query
          name: trafficSourceId
          description: Comma-separated traffic source ids to filter by.
          schema:
            type: string
        - in: query
          name: rotatorId
          description: Comma-separated rotator ids to filter by.
          schema:
            type: string
        - in: query
          name: rotatorPathId
          description: Comma-separated rotator path ids to filter by.
          schema:
            type: string
        - in: query
          name: requestIp
          description: Comma-separated visitor IP addresses to filter by.
          schema:
            type: string
        - in: query
          name: requestUa
          description: Comma-separated User-Agent values to filter by (exact match).
          schema:
            type: string
        - in: query
          name: requestReferer
          description: Comma-separated referrer URLs to filter by (exact match).
          schema:
            type: string
        - in: query
          name: from
          required: true
          schema:
            type: string
        - in: query
          name: to
          required: true
          schema:
            type: string
        - in: query
          name: timezone
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: mode
          description: Optional explicit value `scroll` for cursor pagination (default behavior).
          schema:
            type: string
            enum:
              - scroll
        - in: query
          name: cursor
          description: Opaque cursor token from previous response.
          schema:
            type: string
        - in: query
          name: idx
          schema:
            type: integer
        - in: query
          name: type
          schema:
            type: string
            enum:
              - visit
              - lpclick
              - event
              - postback
      responses:
        '200':
          description: Event list
          content:
            application/json:
              example:
                ok: true
                data:
                  meta:
                    limit: 100
                    hasMore: false
                    nextCursor: null
                  rows:
                    - id: 0197f3a2-b4c5-7000-8000-000000000001
                      clickTime: 1751362800000
                      visitId: 0197f3a2-b4c5-7000-8000-00000000000a
                      externalId: src-click-8842
                      campaignId: 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
                      trafficSourceId: 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
                      rotatorId: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
                      rotatorPathId: 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e
                      type: visit
                      eventTime: 1751362800000
                      deltaMs: 0
                      requestIp: 203.0.113.42
                      requestUa: Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36
                      requestReferer: https://ads.example.com/feed
                    - id: 0197f3a2-b4c5-7000-8000-000000000002
                      clickTime: 1751362800000
                      visitId: 0197f3a2-b4c5-7000-8000-00000000000a
                      externalId: src-click-8842
                      campaignId: 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
                      trafficSourceId: 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
                      rotatorId: 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
                      rotatorPathId: 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e
                      type: event
                      eventTime: 1751363115000
                      deltaMs: 315000
                      idx: 0
                      payout: 4.5
                      value: 4.5
                      affectsRevenue: true
              schema:
                $ref: '#/components/schemas/EventListResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Scroll budget exceeded when request cannot safely advance cursor.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: listEvents
      tags:
        - Events
  /api/v1/affiliate-networks/archive:
    post:
      summary: Archive affiliate networks
      description:
        'Archives multiple affiliate networks by id. Ids are de-duplicated. The operation is
        all-or-nothing: if any id is unknown the request fails with 404 and `details.missing` lists
        the unknown ids.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 8d9e0f1a-2b3c-4d5e-8f6a-7b8c9d0e1f2a
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Archived
          content:
            application/json:
              example:
                ok: true
                data:
                  archived: 1
              schema:
                $ref: '#/components/schemas/BulkArchiveResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: In use
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: archiveAffiliateNetworksBulk
      tags:
        - Affiliate Networks
  /api/v1/affiliate-networks/restore:
    post:
      summary: Restore affiliate networks
      description:
        'Restores previously archived affiliate networks by id. Ids are de-duplicated. The
        operation is all-or-nothing: if any id is unknown the request fails with 404 and
        `details.missing` lists the unknown ids.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 8d9e0f1a-2b3c-4d5e-8f6a-7b8c9d0e1f2a
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Restored
          content:
            application/json:
              example:
                ok: true
                data:
                  restored: 1
              schema:
                $ref: '#/components/schemas/BulkRestoreResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Restore blocked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: restoreAffiliateNetworksBulk
      tags:
        - Affiliate Networks
  /api/v1/campaigns/archive:
    post:
      summary: Archive campaigns
      description: 'Archives multiple campaigns by id. Ids are de-duplicated. The operation is
        all-or-nothing: if any id is unknown the request fails with 404 and `details.missing` lists
        the unknown ids.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Archived
          content:
            application/json:
              example:
                ok: true
                data:
                  archived: 1
              schema:
                $ref: '#/components/schemas/BulkArchiveResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: In use
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: archiveCampaignsBulk
      tags:
        - Campaigns
  /api/v1/campaigns/restore:
    post:
      summary: Restore campaigns
      description:
        'Restores previously archived campaigns by id. Ids are de-duplicated. The operation is
        all-or-nothing: if any id is unknown the request fails with 404 and `details.missing` lists
        the unknown ids. Runtime republish is scheduled automatically when required.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Restored
          content:
            application/json:
              example:
                ok: true
                data:
                  restored: 1
              schema:
                $ref: '#/components/schemas/BulkRestoreResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Restore blocked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: restoreCampaignsBulk
      tags:
        - Campaigns
  /api/v1/landings/archive:
    post:
      summary: Archive landings
      description: 'Archives multiple landings by id. Ids are de-duplicated. The operation is
        all-or-nothing: if any id is unknown the request fails with 404 and `details.missing` lists
        the unknown ids.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Archived
          content:
            application/json:
              example:
                ok: true
                data:
                  archived: 1
              schema:
                $ref: '#/components/schemas/BulkArchiveResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: In use
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: archiveLandingsBulk
      tags:
        - Landings
  /api/v1/landings/restore:
    post:
      summary: Restore landings
      description:
        'Restores previously archived landings by id. Ids are de-duplicated. The operation is
        all-or-nothing: if any id is unknown the request fails with 404 and `details.missing` lists
        the unknown ids.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Restored
          content:
            application/json:
              example:
                ok: true
                data:
                  restored: 1
              schema:
                $ref: '#/components/schemas/BulkRestoreResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Restore blocked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: restoreLandingsBulk
      tags:
        - Landings
  /api/v1/offers/archive:
    post:
      summary: Archive offers
      description: 'Archives multiple offers by id. Ids are de-duplicated. The operation is
        all-or-nothing: if any id is unknown the request fails with 404 and `details.missing` lists
        the unknown ids.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 2e3f4a5b-6c7d-4e8f-9a0b-1c2d3e4f5a6b
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Archived
          content:
            application/json:
              example:
                ok: true
                data:
                  archived: 1
              schema:
                $ref: '#/components/schemas/BulkArchiveResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: In use
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: archiveOffersBulk
      tags:
        - Offers
  /api/v1/offers/restore:
    post:
      summary: Restore offers
      description:
        'Restores previously archived offers by id. Ids are de-duplicated. The operation is
        all-or-nothing: if any id is unknown the request fails with 404 and `details.missing` lists
        the unknown ids.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 2e3f4a5b-6c7d-4e8f-9a0b-1c2d3e4f5a6b
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Restored
          content:
            application/json:
              example:
                ok: true
                data:
                  restored: 1
              schema:
                $ref: '#/components/schemas/BulkRestoreResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Restore blocked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: restoreOffersBulk
      tags:
        - Offers
  /api/v1/rotators/archive:
    post:
      summary: Archive rotators
      description: 'Archives multiple rotators by id. Ids are de-duplicated. The operation is
        all-or-nothing: if any id is unknown the request fails with 404 and `details.missing` lists
        the unknown ids.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Archived
          content:
            application/json:
              example:
                ok: true
                data:
                  archived: 1
              schema:
                $ref: '#/components/schemas/BulkArchiveResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: In use
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: archiveRotatorsBulk
      tags:
        - Rotators
  /api/v1/rotators/restore:
    post:
      summary: Restore rotators
      description:
        'Restores previously archived rotators by id. Ids are de-duplicated. The operation is
        all-or-nothing: if any id is unknown the request fails with 404 and `details.missing` lists
        the unknown ids.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 9b8c7d6e-5f4a-4b3c-8d1e-0f9a8b7c6d5e
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Restored
          content:
            application/json:
              example:
                ok: true
                data:
                  restored: 1
              schema:
                $ref: '#/components/schemas/BulkRestoreResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Restore blocked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: restoreRotatorsBulk
      tags:
        - Rotators
  /api/v1/traffic-sources/archive:
    post:
      summary: Archive traffic sources
      description: 'Archives multiple traffic sources by id. Ids are de-duplicated. The operation is
        all-or-nothing: if any id is unknown the request fails with 404 and `details.missing` lists
        the unknown ids.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Archived
          content:
            application/json:
              example:
                ok: true
                data:
                  archived: 1
              schema:
                $ref: '#/components/schemas/BulkArchiveResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: In use
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: archiveTrafficSourcesBulk
      tags:
        - Traffic Sources
  /api/v1/traffic-sources/restore:
    post:
      summary: Restore traffic sources
      description: 'Restores previously archived traffic sources by id. Ids are de-duplicated. The
        operation is all-or-nothing: if any id is unknown the request fails with 404 and
        `details.missing` lists the unknown ids.'
      requestBody:
        required: true
        content:
          application/json:
            example:
              ids:
                - 6f9d2b4e-8a1c-4d3e-9f5a-7b2c4d6e8f0a
            schema:
              $ref: '#/components/schemas/IdListPayload'
      responses:
        '200':
          description: Restored
          content:
            application/json:
              example:
                ok: true
                data:
                  restored: 1
              schema:
                $ref: '#/components/schemas/BulkRestoreResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Missing ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Restore blocked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitedError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
        default:
          $ref: '#/components/responses/ErrorResponse'
      operationId: restoreTrafficSourcesBulk
      tags:
        - Traffic Sources
components:
  schemas:
    WhoAmIResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            tenantId:
              type: string
            provider:
              type: string
              enum:
                - api-key
                - session
            scope:
              type: string
              enum:
                - read
                - write
            keyId:
              type: string
              nullable: true
              description: Id of the API key the token was issued for. Null for panel sessions.
            label:
              type: string
              nullable: true
              description: Label of the API key. Null for panel sessions.
          required:
            - tenantId
            - provider
            - scope
            - keyId
            - label
          additionalProperties: false
      required:
        - ok
        - data
      additionalProperties: false
    ErrorResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - false
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            details:
              type: object
              additionalProperties: true
          required:
            - code
            - message
          additionalProperties: false
        requestId:
          type: string
      required:
        - ok
        - error
      additionalProperties: false
    CampaignListResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            campaigns:
              type: array
              items:
                $ref: '#/components/schemas/Campaign'
            meta:
              type: object
              properties:
                limit:
                  type: integer
                  minimum: 1
                  maximum: 200
                count:
                  type: integer
                  minimum: 0
              required:
                - limit
                - count
              additionalProperties: false
          required:
            - campaigns
            - meta
          additionalProperties: false
      required:
        - ok
        - data
      additionalProperties: false
    Campaign:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        displayName:
          type: string
        status:
          type: string
          enum:
            - active
            - archived
            - paused
        trafficSourceId:
          type: string
        country:
          type: string
        cost:
          $ref: '#/components/schemas/CampaignCost'
        tracking:
          $ref: '#/components/schemas/CampaignTrackingResponse'
        destination:
          $ref: '#/components/schemas/CampaignPersistedDestination'
        createdAt:
          type: string
          nullable: true
      required:
        - id
        - name
        - displayName
        - status
        - trafficSourceId
        - country
        - cost
        - tracking
        - destination
      additionalProperties: false
    CampaignCost:
      type: object
      oneOf:
        - type: object
          properties:
            mode:
              type: string
              enum:
                - none
          required:
            - mode
          additionalProperties: false
        - type: object
          properties:
            mode:
              type: string
              enum:
                - auto
          required:
            - mode
          additionalProperties: false
        - type: object
          properties:
            mode:
              type: string
              enum:
                - cpv
            amount:
              type: number
              minimum: 0
          required:
            - mode
            - amount
          additionalProperties: false
    CampaignTrackingResponse:
      type: object
      properties:
        method:
          type: string
          enum:
            - redirect
            - direct
        domainId:
          type: string
          nullable: true
        includePrefetch:
          type: boolean
      required:
        - method
        - domainId
        - includePrefetch
      additionalProperties: false
    CampaignPersistedDestination:
      type: object
      properties:
        type:
          type: string
          enum:
            - rotator
        rotatorId:
          type: string
      required:
        - type
        - rotatorId
      additionalProperties: false
    CampaignCreatePayload:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        trafficSourceId:
          type: string
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code. Use ZZ for Global.
        cost:
          $ref: '#/components/schemas/CampaignCost'
        tracking:
          $ref: '#/components/schemas/CampaignTracking'
        destination:
          $ref: '#/components/schemas/CampaignUseRotatorDestination'
      required:
        - name
        - trafficSourceId
        - destination
      additionalProperties: false
    CampaignTracking:
      type: object
      properties:
        method:
          type: string
          enum:
            - redirect
            - direct
        domainId:
          type: string
          nullable: true
        includePrefetch:
          type: boolean
      required:
        - method
      additionalProperties: false
    CampaignUseRotatorDestination:
      type: object
      properties:
        mode:
          type: string
          enum:
            - use_rotator
        rotatorId:
          type: string
      required:
        - mode
        - rotatorId
      additionalProperties: false
    CampaignMutationResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            campaign:
              $ref: '#/components/schemas/Campaign'
            publish:
              $ref: '#/components/schemas/CampaignPublish'
          required:
            - campaign
            - publish
          additionalProperties: false
      required:
        - ok
        - data
      additionalProperties: false
    CampaignPublish:
      type: object
      properties:
        status:
          type: string
          enum:
            - published
            - queued
            - not_required
      required:
        - status
      additionalProperties: false
    CampaignResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            campaign:
              $ref: '#/components/schemas/Campaign'
          required:
            - campaign
          additionalProperties: false
      required:
        - ok
        - data
      additionalProperties: false
    CampaignPatchPayload:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        trafficSourceId:
          type: string
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code. Use ZZ for Global.
        cost:
          $ref: '#/components/schemas/CampaignCost'
        tracking:
          $ref: '#/components/schemas/CampaignTrackingPatch'
        destination:
          $ref: '#/components/schemas/CampaignUseRotatorDestination'
      additionalProperties: false
    CampaignTrackingPatch:
      type: object
      properties:
        method:
          type: string
          enum:
            - redirect
            - direct
        domainId:
          type: string
          nullable: true
        includePrefetch:
          type: boolean
      additionalProperties: false
    Ok:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          nullable: true
      required:
        - ok
        - data
      additionalProperties: false
    GoalRuleHierarchyResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            rules:
              type: array
              items:
                $ref: '#/components/schemas/GoalRuleNode'
      required:
        - ok
        - data
      additionalProperties: false
    GoalRuleNode:
      type: object
      allOf:
        - $ref: '#/components/schemas/GoalRuleNodeInput'
        - type: object
          properties:
            id:
              type: string
            actions:
              type: array
              items:
                $ref: '#/components/schemas/GoalRuleAction'
          required:
            - id
    GoalRuleNodeInput:
      type: object
      properties:
        priority:
          type: integer
        enabled:
          type: boolean
        label:
          type: string
          nullable: true
        conditions:
          type: object
          nullable: true
        actions:
          type: array
          items:
            $ref: '#/components/schemas/GoalRuleActionInput'
      required:
        - priority
        - actions
    GoalRuleActionInput:
      type: object
      properties:
        idx:
          type: integer
        label:
          type: string
          nullable: true
        enabled:
          type: boolean
        payoutType:
          type: string
        payoutFixed:
          type: number
          nullable: true
        costType:
          type: string
        costFixed:
          type: number
          nullable: true
        costShare:
          type: number
          minimum: 0
          maximum: 1000
          description:
            'Revshare percentage in percent points. Example: 30 means 30%, 0.3 means 0.3%, 250
            means 250%.'
          nullable: true
        postbackUrl:
          type: string
          nullable: true
        postbackEnabled:
          type: boolean
        storeValue:
          type: boolean
        isConversion:
          type: boolean
        affectsCost:
          type: boolean
        affectsRevenue:
          type: boolean
      required:
        - idx
    GoalRuleAction:
      type: object
      allOf:
        - $ref: '#/components/schemas/GoalRuleActionInput'
    LandingListResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: array
          items:
            $ref: '#/components/schemas/LandingRow'
      required:
        - ok
        - data
      additionalProperties: false
    LandingRow:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        url:
          type: string
        country:
          type: string
        status:
          type: string
          enum:
            - active
            - archived
        createdAt:
          type: string
    LandingCreatePayload:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        url:
          type: string
          format: uri
          pattern: ^[hH][tT][tT][pP][sS]://
          minLength: 1
          maxLength: 2048
          description:
            Absolute HTTPS URL starting with https://. Private, loopback, and local metadata hosts
            are rejected.
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code. Use 'ZZ' for Global
      required:
        - name
        - url
        - country
      additionalProperties: false
    LandingCreateResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            id:
              type: string
      required:
        - ok
        - data
      additionalProperties: false
    LandingDetailResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          $ref: '#/components/schemas/LandingRow'
      required:
        - ok
        - data
      additionalProperties: false
    LandingPatchPayload:
      type: object
      description: Partial landing update. All fields are optional.
      minProperties: 1
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        url:
          type: string
          format: uri
          pattern: ^[hH][tT][tT][pP][sS]://
          minLength: 1
          maxLength: 2048
          description:
            Absolute HTTPS URL starting with https://. Private, loopback, and local metadata hosts
            are rejected.
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code. Use 'ZZ' for Global
      additionalProperties: false
    OfferListResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: array
          items:
            $ref: '#/components/schemas/OfferRow'
      required:
        - ok
        - data
      additionalProperties: false
    OfferRow:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        url:
          type: string
        appendClickId:
          type: boolean
        affiliateNetwork:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/EntityRef'
        country:
          type: string
        status:
          type: string
          enum:
            - active
            - archived
        createdAt:
          type: string
    EntityRef:
      type: object
      properties:
        id:
          type: string
          format: uuid
          minLength: 36
          maxLength: 36
        name:
          type: string
    OfferCreatePayload:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        url:
          type: string
          format: uri
          pattern: ^[hH][tT][tT][pP][sS]://
          minLength: 1
          maxLength: 2048
          description:
            Absolute HTTPS URL starting with https://. Private, loopback, and local metadata hosts
            are rejected.
        affiliateNetworkId:
          type: string
          format: uuid
          minLength: 36
          maxLength: 36
          nullable: true
        appendClickId:
          type: boolean
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code. Use 'ZZ' for Global
      required:
        - name
        - url
        - country
      additionalProperties: false
    OfferCreateResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            id:
              type: string
      required:
        - ok
        - data
      additionalProperties: false
    OfferDetailResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          $ref: '#/components/schemas/OfferRow'
      required:
        - ok
        - data
      additionalProperties: false
    OfferUpdatePayload:
      type: object
      description: Partial offer update. All fields are optional.
      minProperties: 1
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        url:
          type: string
          format: uri
          pattern: ^[hH][tT][tT][pP][sS]://
          minLength: 1
          maxLength: 2048
          description:
            Absolute HTTPS URL starting with https://. Private, loopback, and local metadata hosts
            are rejected.
        affiliateNetworkId:
          type: string
          format: uuid
          minLength: 36
          maxLength: 36
          nullable: true
        appendClickId:
          type: boolean
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code. Use 'ZZ' for Global
      additionalProperties: false
    ReportListResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            meta:
              type: object
              properties:
                limit:
                  type: integer
                offset:
                  type: integer
                totalRows:
                  type: integer
                timezone:
                  type: string
                totals:
                  type: object
                  additionalProperties: true
                labels:
                  type: object
                  additionalProperties: true
              additionalProperties: false
            rows:
              type: array
              items:
                type: object
          additionalProperties: false
      required:
        - ok
        - data
      additionalProperties: false
    RotatorListResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              shared:
                type: boolean
              country:
                type: string
              status:
                type: string
                enum:
                  - active
                  - archived
              createdAt:
                type: string
      required:
        - ok
        - data
      additionalProperties: false
    RotatorNewPayload:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        shared:
          type: boolean
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code. Use ZZ for Global asset compatibility.
      required:
        - name
      additionalProperties: false
    RotatorCreateResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            id:
              type: string
      required:
        - ok
        - data
      additionalProperties: false
    RotatorDetailResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            shared:
              type: boolean
            country:
              type: string
            status:
              type: string
              enum:
                - active
                - archived
            createdAt:
              type: string
      required:
        - ok
        - data
      additionalProperties: false
    RotatorUpdatePayload:
      type: object
      description: Partial rotator update. All fields are optional.
      minProperties: 1
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        country:
          type: string
      additionalProperties: false
    RotatorRulesResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: array
          items:
            $ref: '#/components/schemas/RotatorRule'
      required:
        - ok
        - data
      additionalProperties: false
    RotatorRule:
      type: object
      properties:
        id:
          type: string
          description: Server-owned stable rule identity returned by the API.
        label:
          type: string
          maxLength: 120
          description: User-facing rule label. It is metadata only and does not affect routing.
          nullable: true
        priority:
          type: integer
        conditions:
          type: object
        enabled:
          type: boolean
          description:
            Disabled rules are retained but ignored when routing traffic. Priority 0 rule is always
            enabled.
        streams:
          type: array
          items:
            $ref: '#/components/schemas/RotatorRuleStream'
      required:
        - id
        - label
        - priority
        - streams
    RotatorRuleStream:
      type: object
      allOf:
        - $ref: '#/components/schemas/RotatorRuleStreamInput'
        - type: object
          properties:
            deletedAt:
              type: integer
              nullable: true
    RotatorRuleStreamInput:
      type: object
      properties:
        streamIdx:
          type: integer
        weight:
          type: integer
        name:
          type: string
        enabled:
          type: boolean
          description: Toggle to temporarily exclude the path from rotation.
        pathId:
          type: string
          nullable: true
        landers:
          type: array
          items:
            $ref: '#/components/schemas/RotatorStreamLander'
        offers:
          type: array
          items:
            $ref: '#/components/schemas/RotatorStreamOffer'
      required:
        - streamIdx
        - name
        - landers
        - offers
    RotatorStreamLander:
      type: object
      properties:
        landingId:
          type: string
        weight:
          type: integer
        enabled:
          type: boolean
          description: When false the landing stays attached but does not participate in traffic split.
      required:
        - landingId
    RotatorStreamOffer:
      type: object
      properties:
        offerId:
          type: string
        weight:
          type: integer
        enabled:
          type: boolean
          description: Disable to keep the offer configured without sending traffic to it.
      required:
        - offerId
    RotatorPathsResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: array
          items:
            $ref: '#/components/schemas/RotatorPath'
      required:
        - ok
        - data
      additionalProperties: false
    RotatorPath:
      type: object
      properties:
        id:
          type: string
        rotatorId:
          type: string
        name:
          type: string
        deletedAt:
          type: integer
          nullable: true
        createdAt:
          type: integer
        updatedAt:
          type: integer
    TrafficSourceListResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: array
          items:
            $ref: '#/components/schemas/TrafficSourceRow'
      required:
        - ok
        - data
      additionalProperties: false
    TrafficSourceRow:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        clickIdParam:
          type: string
          nullable: true
        costParam:
          type: string
          nullable: true
        status:
          type: string
          enum:
            - active
            - archived
        createdAt:
          type: string
        postbackUrl:
          type: string
          nullable: true
        clickIdMacro:
          type: string
          nullable: true
        costMacro:
          type: string
          nullable: true
    TrafficSourcePayload:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        clickIdParam:
          type: string
          maxLength: 120
          nullable: true
        costParam:
          type: string
          maxLength: 120
          nullable: true
        postbackUrl:
          type: string
          format: uri
          maxLength: 2048
          description: Absolute http(s) URL. Private, loopback, and local metadata hosts are rejected.
          nullable: true
        clickIdMacro:
          type: string
          maxLength: 120
          nullable: true
        costMacro:
          type: string
          maxLength: 120
          nullable: true
      required:
        - name
      additionalProperties: false
    TrafficSourceCreateResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            id:
              type: string
      required:
        - ok
        - data
      additionalProperties: false
    TrafficSourceDetailResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          $ref: '#/components/schemas/TrafficSourceRow'
      required:
        - ok
        - data
      additionalProperties: false
    TrafficSourcePatchPayload:
      type: object
      description: Partial traffic source update. All fields are optional.
      minProperties: 1
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        clickIdParam:
          type: string
          maxLength: 120
          nullable: true
        costParam:
          type: string
          maxLength: 120
          nullable: true
        postbackUrl:
          type: string
          format: uri
          maxLength: 2048
          description: Absolute http(s) URL. Private, loopback, and local metadata hosts are rejected.
          nullable: true
        clickIdMacro:
          type: string
          maxLength: 120
          nullable: true
        costMacro:
          type: string
          maxLength: 120
          nullable: true
      additionalProperties: false
    AffiliateNetworkListResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: array
          items:
            $ref: '#/components/schemas/AffiliateNetwork'
      required:
        - ok
        - data
      additionalProperties: false
    AffiliateNetwork:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        displayName:
          type: string
        clickIdEnabled:
          type: boolean
        clickIdParam:
          type: string
          nullable: true
        conversionPostbackTemplate:
          type: string
          nullable: true
        status:
          type: string
          enum:
            - active
            - archived
        createdAt:
          type: string
    AffiliateNetworkInput:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        clickIdEnabled:
          type: boolean
        clickIdParam:
          type: string
          maxLength: 40
          nullable: true
        conversionPostbackTemplate:
          type: string
          maxLength: 2048
          nullable: true
      required:
        - name
      additionalProperties: false
    AffiliateNetworkCreateResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            id:
              type: string
      required:
        - ok
        - data
      additionalProperties: false
    AffiliateNetworkDetailResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          $ref: '#/components/schemas/AffiliateNetwork'
      required:
        - ok
        - data
      additionalProperties: false
    AffiliateNetworkUpdate:
      type: object
      description: Partial affiliate network update. All fields are optional, but the body must not be empty.
      minProperties: 1
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 160
        clickIdEnabled:
          type: boolean
        clickIdParam:
          type: string
          maxLength: 40
          nullable: true
        conversionPostbackTemplate:
          type: string
          maxLength: 2048
          nullable: true
      additionalProperties: false
    EventListResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            meta:
              $ref: '#/components/schemas/EventListMetaScroll'
            rows:
              type: array
              items:
                $ref: '#/components/schemas/EventRow'
      required:
        - ok
        - data
      additionalProperties: false
    EventListMetaScroll:
      type: object
      properties:
        limit:
          type: integer
        hasMore:
          type: boolean
        nextCursor:
          type: string
          nullable: true
      required:
        - limit
        - hasMore
        - nextCursor
    EventRow:
      type: object
      properties:
        id:
          type: string
        clickTime:
          type: integer
        visitId:
          type: string
        externalId:
          type: string
          nullable: true
        campaignId:
          type: string
          nullable: true
        trafficSourceId:
          type: string
          nullable: true
        rotatorId:
          type: string
          nullable: true
        rotatorPathId:
          type: string
          nullable: true
        type:
          type: string
          enum:
            - visit
            - lpclick
            - event
            - postback
        eventTime:
          type: integer
        deltaMs:
          type: integer
        requestIp:
          type: string
          nullable: true
          description: Visitor IP address captured by the tracker (the tenant's own traffic data).
        requestUa:
          type: string
          nullable: true
          description: Visitor User-Agent string captured by the tracker.
        requestReferer:
          type: string
          nullable: true
          description: Referrer URL captured on the tracked request.
        idx:
          type: integer
          nullable: true
        payout:
          type: number
          nullable: true
        cost:
          type: number
          nullable: true
        value:
          type: number
          nullable: true
        affectsCost:
          type: boolean
          nullable: true
        affectsRevenue:
          type: boolean
          nullable: true
        postbackStatus:
          type: integer
          nullable: true
        postbackBody:
          type: string
          nullable: true
        postbackError:
          type: string
          nullable: true
        postbackTry:
          type: integer
          nullable: true
        postbackDuration:
          type: integer
          nullable: true
        postbackUrl:
          type: string
          nullable: true
        postbackSuccess:
          type: boolean
          nullable: true
    IdListPayload:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
          minItems: 1
          maxItems: 200
      required:
        - ids
      additionalProperties: false
    BulkArchiveResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            archived:
              type: integer
          required:
            - archived
          additionalProperties: false
      required:
        - ok
        - data
      additionalProperties: false
    BulkRestoreResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        data:
          type: object
          properties:
            restored:
              type: integer
            runtimePendingIds:
              type: array
              items:
                type: string
            runtimePublishedIds:
              type: array
              items:
                type: string
          required:
            - restored
          additionalProperties: true
      required:
        - ok
        - data
      additionalProperties: false
  responses:
    RateLimitedError:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          description: Seconds to wait before retrying when available.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ServiceUnavailableError:
      description: A required platform dependency is temporarily unavailable.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ErrorResponse:
      description:
        Standard API error envelope. Common platform errors include authentication failures,
        forbidden scope or origin, billing gate, rate limiting, and temporary service
        unavailability.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ForbiddenError:
      description: The authenticated caller is not allowed to perform this operation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
tags:
  - name: Auth
    description: Authentication and session endpoints.
  - name: Campaigns
    description: Campaign and campaign-related management endpoints.
  - name: Landings
    description: Landing page management endpoints.
  - name: Offers
    description: Offer management endpoints.
  - name: Reports
    description: Aggregated reporting and chart endpoints.
  - name: Rotators
    description: Rotator setup and routing-rule endpoints.
  - name: TrafficSources
    description: Traffic source configuration endpoints.
  - name: AffiliateNetworks
    description: Affiliate network management endpoints.
  - name: Events
    description: Raw event and error inspection endpoints.
