Viewpoints AI Studies API

Create studies and retrieve results

OpenAPI Specification

viewpoints-ai-studies-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  description: 'The Viewpoints Study API allows you to programmatically create and retrieve AI-powered research studies with simulated participants.


    ## Authentication


    - **API Key** — pass your key in the `X-API-Key` header

    To obtain an API key, visit your account settings in the Viewpoints dashboard.


    ## Workflow


    1. **Upload stimuli** (optional, not needed for text only stimuli) — use the presigned URL endpoint to upload images, video, audio, or PDFs to S3

    2. **Create a study** — submit your study configuration with audience segments, assigned materials, and questions

    3. **Poll for completion** — use the job status endpoint to track progress and get completed study ID

    4. **Retrieve results** — once complete, fetch the full study results with all participant responses using the study ID'
  title: Viewpoints Study Studies API
  version: 1.0.0
security:
- apiKey: []
- bearerAuth: []
tags:
- description: Create studies and retrieve results
  name: Studies
paths:
  /v1/studies:
    post:
      description: Launch a new study with the given parameters. Upload file stimuli via the presigned URL endpoint first and include the resulting S3 keys as URLs. Returns a job ID for tracking study creation progress via the job status endpoint.
      operationId: post__v1_studies
      parameters: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateStudyRequest'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateStudyResponse'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Validation error (invalid fields, too many characters, etc.)
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Authentication failed
        '402':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Entitlement limit exceeded (participants, questions, or question type not allowed by current plan)
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Not authorized
      summary: Create a new study
      tags:
      - Studies
  /v1/studies/jobs/{job_id}:
    get:
      description: Poll the status of a study creation job. Returns `processing` while the study is being generated, `completed` with the study ID when done, or `failed` if an error occurred.
      operationId: get__v1_studies_jobs_{job_id}
      parameters:
      - description: The job ID returned by the create study endpoint
        in: path
        name: job_id
        required: true
        schema:
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobStatusResponse'
          description: Job completed or failed
        '202':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobStatusResponse'
          description: Job still processing
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Bad request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Authentication failed
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Job not found
      summary: Get study creation job status
      tags:
      - Studies
  /v1/studies/{id}:
    get:
      description: Retrieve study configuration and all participant responses.
      operationId: get__v1_studies_{id}
      parameters:
      - description: The UUID of the study to retrieve
        in: path
        name: id
        required: true
        schema:
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetStudyResponse'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Bad request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Authentication failed
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Not authorized to view this study
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Study not found
      summary: Get study results
      tags:
      - Studies
    put:
      description: 'Replace the full configuration of an existing draft study. The same `launch` flag accepted by POST /v1/studies controls whether this also enqueues the background job: launch=false keeps the study as a draft, launch=true flips it to running and starts execution. Returns 409 if the study has already been launched (status != ''draft''), or if `expected_version` is provided and another save has bumped the draft''s version since it was read.'
      operationId: put__v1_studies_{id}
      parameters:
      - description: ''
        in: path
        name: id
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateStudyRequest'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateStudyResponse'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Validation error
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Authentication failed
        '402':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Entitlement limit exceeded
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Not authorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Study not found
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Study is not a draft
      summary: Replace a draft study
      tags:
      - Studies
components:
  schemas:
    QuestionItem:
      additionalProperties: false
      properties:
        allowMultiple:
          default: false
          description: Only applies when scale is 'multiple_choice'. When false (default), each persona selects exactly one option and the response is a single string. When true, each persona may select one or more options and the response is a JSON array of strings.
          title: Allowmultiple
          type: boolean
        choice_matrix_rows:
          anyOf:
          - items:
              type: string
            type: array
          - type: 'null'
          default: null
          description: Row labels for question_subtype 'choice_matrix'. Each row receives exactly one choice.
          title: Choice Matrix Rows
        choices:
          anyOf:
          - items:
              type: string
            type: array
          - type: 'null'
          default: null
          description: Required when scale is 'multiple_choice' or 'allocation' (party names, minimum 2 required). The list of answer options.
          title: Choices
        name:
          title: Name
          type: string
        question_subtype:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          description: Optional subtype for question variants. Use 'choice_matrix' with scale 'multiple_choice' to ask one single-select choice per row.
          title: Question Subtype
        questions:
          items:
            type: string
          title: Questions
          type: array
        scale:
          description: 'The response scale type. Either a point scale (2 - 100 points) in the format: ''<N> point scale (1 = <low label>; <N> = <high label>)'', or amount scale in the format: ''amount scale (<$ or £ or ¥><min amount> - <$ or £ or ¥><max amount>)'', or one of the keywords: ''qualitative'', ''multiple_choice'', ''allocation''.'
          examples:
          - qualitative
          - multiple_choice
          - 2 point scale (1 = No; 2 = Yes)
          - 3 point scale (1 = 1 star; 3 = 3 stars)
          - 5 point scale (1 = 1 star; 5 = 5 stars)
          - 7 point scale (1 = Not at all appealing; 7 = Extremely appealing)
          - 9 point scale (1 = Not at all likely; 9 = Extremely likely)
          - 100 point scale (1 = Not at all likely; 100 = Extremely likely)
          - amount scale ($5 - $100)
          - amount scale (£20 - £200)
          - amount scale ($)
          - allocation
          pattern: ^(?:qualitative|ranking|points_allocation|allocation|matrix|multiple_choice|(?:100|[1-9]\d|[2-9]) point scale \(1 = [^\r\n;]+; (?:100|[1-9]\d|[2-9]) = [^\r\n)]+\)|amount scale \(\$-?\d+(?:\.\d+)? - \$-?\d+(?:\.\d+)?\)|amount scale \(£-?\d+(?:\.\d+)? - £-?\d+(?:\.\d+)?\)|amount scale \(¥-?\d+(?:\.\d+)? - ¥-?\d+(?:\.\d+)?\)|amount scale \(\$\)|amount scale \(£\)|amount scale \(€\)|amount scale \(¥\))$
          title: Scale
          type: string
        sequenceOpenTextMode:
          anyOf:
          - enum:
            - per_stimulus
            - overall
            type: string
          - type: 'null'
          default: null
          description: Only applies to qualitative questions in sequence studies. 'per_stimulus' asks once for each sequence item; 'overall' asks one overall open-ended question.
          title: Sequenceopentextmode
      required:
      - name
      - questions
      - scale
      title: QuestionItem
      type: object
    CreateStudyRequest:
      examples:
      - added_captions:
        - content: A new smartphone with a 6.5 inch display...
          name: Product description
        age_parameters:
          max_age: 65
          min_age: 18
        assigned_materials:
        - materials_name: Control
          stimuli:
          - media:
            - A new smartphone with a 6.5 inch display...
            stimulus_name: Product description
            stimulus_type: text
        number_participants: 50
        question_groups:
        - name: Purchase Intent
          questions:
          - How likely are you to purchase this product?
          scale: 7 point scale (1 = Not at all likely; 7 = Extremely likely)
        research_goal: ''
        segments:
        - name: Gender
          subcategories:
          - name: Male
            percentage: 50
          - name: Female
            percentage: 50
        title: Example Study
        uploaded_audios: []
        uploaded_images: []
        uploaded_pdfs: []
        uploaded_videos: []
      properties:
        added_captions:
          items:
            $ref: '#/components/schemas/TextCaptionItem'
          title: Added Captions
          type: array
        age_parameters:
          $ref: '#/components/schemas/AgeParametersItem'
        assigned_materials:
          items:
            $ref: '#/components/schemas/AssignedMaterialsItem'
          title: Assigned Materials
          type: array
        expected_version:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Expected Version
        generate_life_stories:
          default: true
          title: Generate Life Stories
          type: boolean
        launch:
          default: true
          title: Launch
          type: boolean
        number_participants:
          maximum: 1000
          minimum: 1
          title: Number Participants
          type: integer
        persona_context:
          default: ''
          maxLength: 10000
          title: Persona Context
          type: string
        question_groups:
          items:
            $ref: '#/components/schemas/QuestionItem'
          title: Question Groups
          type: array
        research_goal:
          default: ''
          maxLength: 10000
          title: Research Goal
          type: string
        segments:
          items:
            $ref: '#/components/schemas/SegmentItem'
          title: Segments
          type: array
        title:
          maxLength: 70
          minLength: 1
          title: Title
          type: string
        uploaded_audios:
          items:
            $ref: '#/components/schemas/UploadedMediaItem'
          title: Uploaded Audios
          type: array
        uploaded_images:
          items:
            $ref: '#/components/schemas/UploadedMediaItem'
          title: Uploaded Images
          type: array
        uploaded_pdfs:
          items:
            $ref: '#/components/schemas/UploadedMediaItem'
          title: Uploaded Pdfs
          type: array
        uploaded_videos:
          items:
            $ref: '#/components/schemas/UploadedMediaItem'
          title: Uploaded Videos
          type: array
      required:
      - title
      - uploaded_images
      - uploaded_videos
      - uploaded_audios
      - uploaded_pdfs
      - added_captions
      - segments
      - age_parameters
      - assigned_materials
      - question_groups
      - number_participants
      title: CreateStudyRequest
      type: object
    UploadedMediaItem:
      description: Media stimulus to be included in the study
      properties:
        caption:
          default: ''
          title: Caption
          type: string
        is_sequential:
          default: false
          title: Is Sequential
          type: boolean
        name:
          title: Name
          type: string
        url:
          title: Url
          type: string
      required:
      - name
      - url
      title: UploadedMediaItem
      type: object
    StudyDetailsResponse:
      properties:
        added_captions:
          items:
            $ref: '#/components/schemas/CaptionResponse'
          title: Added Captions
          type: array
        age_parameters:
          $ref: '#/components/schemas/AgeParametersItem'
        assigned_materials:
          items:
            $ref: '#/components/schemas/AssignedMaterialsItem'
          title: Assigned Materials
          type: array
        jury_panel_size:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Jury Panel Size
        log_to_memory:
          default: true
          title: Log To Memory
          type: boolean
        num_participants_in_paper:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Num Participants In Paper
        number_participants:
          title: Number Participants
          type: integer
        persona_context:
          default: ''
          title: Persona Context
          type: string
        question_blocks:
          anyOf:
          - additionalProperties: true
            type: object
          - type: 'null'
          default: null
          title: Question Blocks
        question_groups:
          items:
            $ref: '#/components/schemas/QuestionItem'
          title: Question Groups
          type: array
        research_goal:
          default: ''
          title: Research Goal
          type: string
        segments:
          items:
            $ref: '#/components/schemas/SegmentItem'
          title: Segments
          type: array
        stimuli_prompt_append:
          default: ''
          title: Stimuli Prompt Append
          type: string
        title:
          title: Title
          type: string
        uploaded_audios:
          anyOf:
          - items:
              $ref: '#/components/schemas/UploadedMediaItem'
            type: array
          - type: 'null'
          default: null
          title: Uploaded Audios
        uploaded_images:
          items:
            $ref: '#/components/schemas/UploadedMediaItem'
          title: Uploaded Images
          type: array
        uploaded_participants:
          anyOf:
          - items: {}
            type: array
          - type: 'null'
          default: null
          title: Uploaded Participants
        uploaded_pdfs:
          anyOf:
          - items:
              $ref: '#/components/schemas/UploadedMediaItem'
            type: array
          - type: 'null'
          default: null
          title: Uploaded Pdfs
        uploaded_videos:
          anyOf:
          - items:
              $ref: '#/components/schemas/UploadedMediaItem'
            type: array
          - type: 'null'
          default: null
          title: Uploaded Videos
      required:
      - title
      - number_participants
      - age_parameters
      - assigned_materials
      - uploaded_images
      - added_captions
      - segments
      - question_groups
      title: StudyDetailsResponse
      type: object
    StimulusItem:
      properties:
        caption:
          default: ''
          title: Caption
          type: string
        materials_name:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Materials Name
        media:
          description: Text stimulus, a URL string, or an object with url/type/name
          items:
            anyOf:
            - type: string
            - $ref: '#/components/schemas/MediaItem'
          title: Media
          type: array
        stimulus_name:
          title: Stimulus Name
          type: string
        stimulus_type:
          enum:
          - text
          - image
          - video
          - audio
          - pdf
          title: Stimulus Type
          type: string
      required:
      - stimulus_name
      - stimulus_type
      - media
      title: StimulusItem
      type: object
    CaptionResponse:
      properties:
        caption:
          default: ''
          title: Caption
          type: string
        content:
          title: Content
          type: string
        name:
          title: Name
          type: string
      required:
      - name
      - content
      title: CaptionResponse
      type: object
    AgeParametersItem:
      properties:
        max_age:
          title: Max Age
          type: integer
        mean:
          anyOf:
          - type: number
          - type: 'null'
          default: null
          title: Mean
        min_age:
          title: Min Age
          type: integer
        st_dev:
          anyOf:
          - type: number
          - type: 'null'
          default: null
          title: St Dev
        venue:
          anyOf:
          - maxLength: 200
            type: string
          - type: 'null'
          default: null
          title: Venue
      required:
      - min_age
      - max_age
      title: AgeParametersItem
      type: object
    SegmentItem:
      properties:
        name:
          title: Name
          type: string
        subcategories:
          items:
            $ref: '#/components/schemas/SegmentSubCategoryItem'
          title: Subcategories
          type: array
      required:
      - name
      - subcategories
      title: SegmentItem
      type: object
    CreateStudyResponse:
      properties:
        job_id:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Job Id
        study_id:
          title: Study Id
          type: string
        version:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Version
      required:
      - study_id
      title: CreateStudyResponse
      type: object
    ParticipantResultItem:
      properties:
        assigned_materials:
          title: Assigned Materials
          type: string
        id:
          title: Id
          type: integer
        participant_assigned_materials:
          items:
            $ref: '#/components/schemas/AssignedMaterialsItem'
          title: Participant Assigned Materials
          type: array
        participant_response:
          additionalProperties:
            anyOf:
            - type: string
            - type: integer
            - type: number
            - items: {}
              type: array
            - additionalProperties: true
              type: object
            - type: 'null'
          title: Participant Response
          type: object
        public_participant_id:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Public Participant Id
        public_response_id:
          title: Public Response Id
          type: string
        response_valid:
          title: Response Valid
          type: boolean
        segments:
          additionalProperties:
            anyOf:
            - type: string
            - type: integer
            - type: number
            - items: {}
              type: array
            - additionalProperties: true
              type: object
            - type: 'null'
          title: Segments
          type: object
      required:
      - id
      - public_response_id
      - assigned_materials
      - response_valid
      - segments
      - participant_assigned_materials
      - participant_response
      title: ParticipantResultItem
      type: object
    ErrorResponse:
      properties:
        details:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Details
        error:
          title: Error
          type: string
      required:
      - error
      title: ErrorResponse
      type: object
    SegmentSubCategoryItem:
      properties:
        name:
          title: Name
          type: string
        percentage:
          title: Percentage
          type: integer
      required:
      - name
      - percentage
      title: SegmentSubCategoryItem
      type: object
    MediaItem:
      description: Media item that can be either a URL string or an object with url/type/name.
      properties:
        name:
          default: ''
          title: Name
          type: string
        type:
          default: ''
          title: Type
          type: string
        url:
          title: Url
          type: string
      required:
      - url
      title: MediaItem
      type: object
    TextCaptionItem:
      description: Text stimulus to be included in the study
      properties:
        caption:
          default: ''
          title: Caption
          type: string
        content:
          default: ''
          title: Content
          type: string
        is_sequential:
          default: false
          title: Is Sequential
          type: boolean
        name:
          title: Name
          type: string
      required:
      - name
      title: TextCaptionItem
      type: object
    GetStudyResponse:
      properties:
        completed_at:
          anyOf:
          - format: date-time
            type: string
          - type: 'null'
          default: null
          title: Completed At
        created_at:
          format: date-time
          title: Created At
          type: string
        folder_id:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Folder Id
        is_shared:
          default: false
          title: Is Shared
          type: boolean
        results:
          items:
            $ref: '#/components/schemas/ParticipantResultItem'
          title: Results
          type: array
        status:
          title: Status
          type: string
        study_details:
          $ref: '#/components/schemas/StudyDetailsResponse'
        user_id:
          title: User Id
          type: string
        version:
          default: 0
          title: Version
          type: integer
      required:
      - study_details
      - results
      - created_at
      - status
      - user_id
      title: GetStudyResponse
      type: object
    AssignedMaterialsItem:
      properties:
        materials_name:
          title: Materials Name
          type: string
        stimuli:
          items:
            $ref: '#/components/schemas/StimulusItem'
          title: Stimuli
          type: array
      required:
      - materials_name
      - stimuli
      title: AssignedMaterialsItem
      type: object
    JobStatusResponse:
      properties:
        analyze_done:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Analyze Done
        analyze_item_elapsed_ms:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Analyze Item Elapsed Ms
        analyze_item_expected_ms:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Analyze Item Expected Ms
        analyze_total:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Analyze Total
        analyze_video_seconds:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Analyze Video Seconds
        error_message:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Error Message
        has_analyzing_media:
          anyOf:
          - type: boolean
          - type: 'null'
          default: null
          title: Has Analyzing Media
        has_enriched_context:
          anyOf:
          - type: boolean
          - type: 'null'
          default: null
          title: Has Enriched Context
        has_life_stories:
          anyOf:
          - type: boolean
          - type: 'null'
          default: null
          title: Has Life Stories
        has_media:
          anyOf:
          - type: boolean
          - type: 'null'
          default: null
          title: Has Media
        has_qualitative_questions:
          anyOf:
          - type: boolean
          - type: 'null'
          default: null
          title: Has Qualitative Questions
        jury_panel_size:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Jury Panel Size
        media_done:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Media Done
        media_item_elapsed_ms:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Media Item Elapsed Ms
        media_item_expected_ms:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Media Item Expected Ms
        media_total:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Media Total
        number_participants:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Number Participants
        phase:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Phase
        phase_entered_at_ms:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Phase Entered At Ms
        progress:
          anyOf:
          - type: number
          - type: 'null'
          default: null
          title: Progress
        qual_questions_count:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Qual Questions Count
        status:
          enum:
          - processing
          - preparing_context
          - completed
          - failed
          title: Status
          type: string
        study_id:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Study Id
        title:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Title
      required:
      - status
      title: JobStatusResponse
      type: object
  securitySchemes:
    apiKey:
      in: header
      name: X-API-Key
      type: apiKey
    bearerAuth:
      bearerFormat: JWT
      scheme: bearer
      type: http