openapi: 3.1.0
x-customer-voice: true
info:
  title: Segments
  version: 0.1.0
  description: |
    Find audience segments in the Motionworks segment catalog, purchase the
    ones you need, and keep them in your organization's segment library.

    For a search box, use autocomplete: it is free, needs no API key, and
    suggests segments as the user types. For the full picture, search with
    plain text (`q`) — names, phrases, partial words and typos all match,
    and pasting a segment id finds that segment — or with a structured
    `es_query` for precise filtering on any catalog field. Every search
    result carries the segment's full catalog definition, its price, and
    whether your organization already owns it.

    A purchase is permanent and belongs to your whole organization. Each
    segment has a price in credits that includes the standard 1-credit
    charge for the call; some segments are free beyond that charge.
    Purchasing a segment your organization already owns returns the
    existing library entry and costs only the 1-credit call charge.

    Authenticate with an org-scoped API key (`X-API-Key: mw_…`); only
    autocomplete works without one.
  contact:
    name: Motionworks AI
    url: https://mworks.com
    email: api@mworks.com
servers:
  - url: https://api2.mworks.com/v2
    description: Production
security:
  - apiKey: []
components:
  securitySchemes:
    apiKey:
      type: apiKey
      name: X-API-Key
      in: header
  parameters:
    Limit:
      name: limit
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25
    Cursor:
      name: cursor
      in: query
      required: false
      description: The `next_cursor` from the previous page.
      schema:
        type: string
  schemas:
    SegmentCatalogEntry:
      type: object
      description: |
        A segment's full catalog definition. Every field is always
        present. Only `motionworks_segment_id` and `segment_name` are never
        `null`; any other field is `null` when the segment does not define
        it, and list fields are empty arrays.
      required:
        - motionworks_segment_id
        - legacy_segment_id
        - geopath_segment_id
        - segment_name
        - system
        - category
        - age_min
        - age_max
        - gender
        - races
        - hispanic
        - worker
        - language
        - household_size_min
        - household_size_max
        - household_children
        - household_income_min
        - household_income_max
        - household_vehicles_min
        - household_vehicles_max
        - prizm_segments
        - consumer_segments
      properties:
        motionworks_segment_id:
          type: string
          pattern: ^[0-9a-fA-F]{16}$
          example: c0073f01c420a43b
        legacy_segment_id:
          type:
            - string
            - 'null'
          example: UHYW5SG
        geopath_segment_id:
          type:
            - string
            - 'null'
        segment_name:
          type: string
          minLength: 1
        system:
          type:
            - string
            - 'null'
          description: The segment family, e.g. `basic_demographics` or `consumer`.
        category:
          type:
            - string
            - 'null'
        age_min:
          type:
            - integer
            - 'null'
        age_max:
          type:
            - integer
            - 'null'
        gender:
          type:
            - string
            - 'null'
        races:
          type: array
          items:
            type: string
        hispanic:
          type:
            - boolean
            - 'null'
        worker:
          type:
            - boolean
            - 'null'
        language:
          type: array
          items:
            type: string
        household_size_min:
          type:
            - integer
            - 'null'
        household_size_max:
          type:
            - integer
            - 'null'
        household_children:
          type:
            - boolean
            - 'null'
        household_income_min:
          type:
            - integer
            - 'null'
        household_income_max:
          type:
            - integer
            - 'null'
        household_vehicles_min:
          type:
            - integer
            - 'null'
        household_vehicles_max:
          type:
            - integer
            - 'null'
        prizm_segments:
          type: array
          items:
            type: integer
        consumer_segments:
          type: array
          items:
            type: string
    PriceTier:
      type: string
      enum:
        - free
        - standard
        - premium
      description: |
        The segment's price tier. `free` segments cost only the standard
        1-credit call charge to purchase.
    SegmentSuggestion:
      type: object
      description: |
        One autocomplete suggestion. `match` tells you how it matched: `id`
        (the query is the segment's id), `prefix` (a word in the name starts
        with the query) or `fuzzy` (a close spelling, returned only when
        nothing matches by prefix).
      required:
        - motionworks_segment_id
        - legacy_segment_id
        - segment_name
        - system
        - category
        - match
        - match_highlights
      properties:
        motionworks_segment_id:
          type: string
          pattern: ^[0-9a-fA-F]{16}$
        legacy_segment_id:
          type:
            - string
            - 'null'
        segment_name:
          type: string
          minLength: 1
        system:
          type:
            - string
            - 'null'
        category:
          type:
            - string
            - 'null'
        match:
          type: string
          enum:
            - id
            - prefix
            - fuzzy
        match_highlights:
          type: array
          description: Character spans of your query inside `segment_name`, so you can bold the match. Empty for `id` and `fuzzy` matches.
          items:
            type: object
            required:
              - field
              - start
              - end
            additionalProperties: false
            properties:
              field:
                type: string
                enum:
                  - segment_name
              start:
                type: integer
                minimum: 0
                description: Offset of the first highlighted character.
              end:
                type: integer
                minimum: 0
                description: Offset just past the last highlighted character.
    SegmentSearchRequest:
      type: object
      description: Provide exactly one of `q` or `es_query`.
      oneOf:
        - required:
            - q
          not:
            required:
              - es_query
        - required:
            - es_query
          not:
            required:
              - q
      properties:
        q:
          type: string
          minLength: 1
          maxLength: 200
          description: |
            Plain-text search. Results rank exact name or id matches first,
            then phrase-prefix matches, names containing every word,
            partial-word matches, and finally close spellings. For a search
            box that updates on every keystroke, use the free
            autocomplete instead.
          example: hispanic moms
        es_query:
          type: object
          minProperties: 1
          description: |
            A structured query, given as the `query` clause of the search
            query language (no `size`, `sort`, aggregations or scripts —
            paging and ordering are controlled by this API). Allowed clause
            types: `bool`, `match`, `match_phrase`, `match_phrase_prefix`,
            `multi_match`, `simple_query_string`, `term`, `terms`,
            `prefix`, `range` and `exists`, on any catalog field, nested at
            most 6 levels deep and with at most 50 clauses in total.
            Anything else is rejected with `400` and the location of the
            offending clause. Results are ordered by relevance.
          example:
            bool:
              filter:
                - term:
                    system: consumer
                - range:
                    household_income_min:
                      gte: 100000
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
        cursor:
          type: string
          description: The `next_cursor` from the previous page; valid only with the same query.
      additionalProperties: false
    SegmentSearchItem:
      allOf:
        - $ref: '#/components/schemas/SegmentCatalogEntry'
        - type: object
          required:
            - score
            - price_tier
            - credits
            - owned
            - asset_id
          properties:
            score:
              type: number
              description: Relevance score; higher is a better match.
            price_tier:
              $ref: '#/components/schemas/PriceTier'
            credits:
              type: integer
              minimum: 1
              description: |
                The segment's price: total credits a first purchase costs,
                including the 1-credit call charge. When `owned` is true, a
                repeat purchase costs only the 1-credit call charge.
            owned:
              type: boolean
              description: Whether your organization already owns this segment.
            asset_id:
              type:
                - string
                - 'null'
              description: Your library entry for this segment when `owned` is true; otherwise `null`.
    SegmentPurchaseRequest:
      type: object
      description: Identify the segment with exactly one of `motionworks_segment_id` or `legacy_segment_id`.
      required:
        - purchase_request_id
      oneOf:
        - required:
            - motionworks_segment_id
          not:
            required:
              - legacy_segment_id
        - required:
            - legacy_segment_id
          not:
            required:
              - motionworks_segment_id
      properties:
        motionworks_segment_id:
          type: string
          pattern: ^[0-9a-fA-F]{16}$
        legacy_segment_id:
          type: string
          pattern: ^[0-9A-Za-z]{7}$
        purchase_request_id:
          type: string
          format: uuid
          description: |
            Idempotency key you mint (UUID). A retry with the same key and
            the same segment id returns the original response, with its
            original status and the header `X-MW-Idempotent-Replay: 1`, at
            no charge. The same key with a different segment id — including
            the same segment under its other id — is rejected with `409`.
      additionalProperties: false
      example:
        motionworks_segment_id: c0073f01c420a43b
        purchase_request_id: 3f1c2b9e-7a44-4c1e-9a51-0d2f6f0c8b11
    SegmentAsset:
      type: object
      description: A segment in your organization's library.
      required:
        - asset_id
        - motionworks_segment_id
        - legacy_segment_id
        - segment_name
        - state
        - price_tier
        - credits_charged
        - purchased_at
        - purchased_by
        - catalog
      properties:
        asset_id:
          type: string
          pattern: ^seg[0-9A-HJKMNP-TV-Z]{26}$
        motionworks_segment_id:
          type: string
          pattern: ^[0-9a-fA-F]{16}$
        legacy_segment_id:
          type:
            - string
            - 'null'
        segment_name:
          type: string
          minLength: 1
        state:
          type: string
          enum:
            - active
        price_tier:
          $ref: '#/components/schemas/PriceTier'
        credits_charged:
          type: integer
          minimum: 1
          description: Total credits charged for the purchase, including the 1-credit call charge.
        purchased_at:
          type: string
          format: date-time
        purchased_by:
          type:
            - string
            - 'null'
        catalog:
          $ref: '#/components/schemas/SegmentCatalogEntry'
          description: The segment's catalog definition as it was when purchased. It never changes.
    SegmentAssetDetail:
      description: A library entry as retrieved on its own.
      allOf:
        - $ref: '#/components/schemas/SegmentAsset'
        - type: object
          properties:
            catalog_current:
              description: |
                Present only when the catalog definition has changed since
                purchase: the current definition, or `null` if the segment
                is no longer in the catalog.
              oneOf:
                - $ref: '#/components/schemas/SegmentCatalogEntry'
                - type: 'null'
    SegmentAssetSummary:
      type: object
      required:
        - total_owned
        - credits_spent
        - last_purchased_at
      properties:
        total_owned:
          type: integer
          minimum: 0
        credits_spent:
          type: integer
          minimum: 0
        last_purchased_at:
          type:
            - string
            - 'null'
          format: date-time
    Pagination:
      type: object
      required:
        - has_more
        - count
      properties:
        next_cursor:
          type: string
          description: Present only when `has_more` is true.
        has_more:
          type: boolean
        count:
          type: integer
          description: Total number of matches across all pages.
    Provenance:
      type: object
      properties:
        source:
          type: string
        source_doc:
          type: string
          format: uri
        methodology_version:
          type: string
        data_vintage:
          type: string
          format: date
        data_freshness:
          type: string
          enum:
            - hourly
            - daily
            - weekly
            - monthly
            - annually
            - on-demand
            - static
        data_latency_days:
          type: integer
        data_maturity:
          type: string
          enum:
            - production
            - research-preview
            - synthetic-only
            - roadmap
    Meta:
      type: object
      properties:
        request_id:
          type: string
        credits_used:
          type: integer
        credits_remaining:
          type: integer
        product:
          type: string
        version:
          type: string
        provenance:
          $ref: '#/components/schemas/Provenance'
        pagination:
          $ref: '#/components/schemas/Pagination'
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            status:
              type: integer
            request_id:
              type: string
            product:
              type: string
            docs_url:
              type: string
  responses:
    BadRequest:
      description: Invalid request (`VALIDATION_ERROR`), or a `cursor` this API did not issue for this query (`INVALID_CURSOR`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    InsufficientCredits:
      description: Not enough credits (`INSUFFICIENT_CREDITS`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    UpstreamUnavailable:
      description: The segment catalog did not answer (`UPSTREAM_UNAVAILABLE`). Retry shortly.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    DataUnavailable:
      description: The segment catalog is not available right now (`DATA_UNAVAILABLE`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
paths:
  /segments/autocomplete:
    get:
      operationId: autocompleteSegments
      summary: Suggest segments as the user types
      description: |
        Returns up to `limit` suggestions for a search box: an exact segment
        id first, then segments with a word in their name that starts with
        the query, and — only when nothing matches that way — segments with
        a similar spelling. Matching is case-insensitive. Each suggestion
        carries the segment's ids, name, system and category; follow it with
        a purchase, or with a search for the full definition and price.

        Free — 0 credits, no API key required. Calls are rate-limited per
        IP address; over the limit returns `429`. Unknown query parameters
        are ignored.
      tags:
        - Segment Search
      security: []
      x-credit-cost: 0
      x-motionworks-status: production
      parameters:
        - name: q
          in: query
          required: true
          description: The text typed so far.
          schema:
            type: string
            minLength: 1
            maxLength: 200
        - name: limit
          in: query
          required: false
          description: Maximum suggestions to return (1–20).
          schema:
            type: integer
            minimum: 1
            maximum: 20
            default: 10
      responses:
        '200':
          description: Suggestions, best match first.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SegmentSuggestion'
                  meta:
                    $ref: '#/components/schemas/Meta'
        '400':
          description: '`q` is missing or blank, or `limit` is out of range (`VALIDATION_ERROR`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Too many requests from your IP address (`RATE_LIMIT_EXCEEDED`). `Retry-After` says how many seconds to wait.
          headers:
            Retry-After:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '502':
          $ref: '#/components/responses/UpstreamUnavailable'
        '503':
          $ref: '#/components/responses/DataUnavailable'
  /segments/search:
    post:
      operationId: searchSegments
      summary: Search the segment catalog
      description: |
        Searches the segment catalog with plain text (`q`) or a structured
        query (`es_query`) and returns one page of matches, each with its
        full catalog definition, its price, and whether your organization
        owns it. 1 credit per call.
      tags:
        - Segment Search
      x-credit-cost: 1
      x-motionworks-status: production
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SegmentSearchRequest'
      responses:
        '200':
          description: One page of matches; `meta.pagination` carries the cursor.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SegmentSearchItem'
                  meta:
                    $ref: '#/components/schemas/Meta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '402':
          $ref: '#/components/responses/InsufficientCredits'
        '502':
          $ref: '#/components/responses/UpstreamUnavailable'
        '503':
          $ref: '#/components/responses/DataUnavailable'
x-tagGroups:
  - name: Segments
    tags:
      - Segment Search
      - Segment Assets
tags:
  - name: Segment Search
    description: Suggest and search segments in the audience segment catalog.
  - name: Segment Assets
    description: Purchase segments and read your organization's segment library.
