openapi: 3.1.0
info:
  title: BAI Analytics API
  version: 1.0.0
  description: 'BAI Analytics is the intelligence layer across all your customer feedback.


    Use the **Input API** to bring feedback into BAI Analytics programmatically, and **webhooks** to receive real-time notifications
    when events occur.


    **Authentication.** Send your Input API key as a bearer token:


    ```

    Authorization: Bearer inpk_live_...

    ```


    Create keys under **Integrations → API** in the dashboard. Use a `test` key while developing — `test` keys are not charged
    Analysis Points (APS).


    **Concepts.** A *feedback group* organises related *data sources*; a data source holds *questions*; you push *feedback
    text* to a question for AI analysis. Account, survey-building, and analytics features are managed in the dashboard and
    are not part of this API.'
  contact:
    name: BoundaryAI Engineering
    email: dev@boundary-ai.com
  license:
    name: Proprietary — BoundaryAI Terms of Service
    url: https://boundary-ai.com/terms
  termsOfService: https://boundary-ai.com/terms
  x-public-spec-generated: true
servers:
- url: https://app.boundary-ai.com
  description: Production
tags:
- name: InputAPI
  x-displayName: Input API
  description: Bring feedback into BAI Analytics programmatically with an Input API key. Create feedback groups and data sources,
    publish them, and push feedback text to questions for analysis.
- name: Webhooks
  description: Receive real-time notifications when events occur in BAI Analytics. Deliveries are signed with HMAC-SHA256
    in the `X-Boundary-Signature` header; the event type is in `X-Boundary-Event`.
paths:
  /api/input/content/push:
    post:
      summary: Push feedback text to a question
      description: Push feedback text to a specific question — the primary endpoint for importing data. Each string in `content`
        becomes one piece of feedback attached to the question. Pushing to a published data source consumes Analysis Points
        (APS).
      tags:
      - InputAPI
      operationId: inputapi_post_content_push
      parameters:
      - name: Idempotency-Key
        in: header
        required: false
        schema:
          type: string
          maxLength: 255
        description: Opaque client-supplied key for safe retries (24h window).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContentPushRequest'
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/ContentPushResponse'
                required:
                - data
        '400':
          description: Bad Request — validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Unauthorized — missing or invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '402':
          description: Payment Required — insufficient credits / APS
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: Forbidden — insufficient permission
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: Conflict (idempotency key reuse or resource conflict)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
      - apiKeyAuth: []
  /api/input/content/push/bulk:
    post:
      summary: Push feedback to multiple questions
      description: Push feedback text to several questions in a single request. Returns a task id you can poll for completion.
      tags:
      - InputAPI
      operationId: inputapi_post_content_push_bulk
      parameters:
      - name: Idempotency-Key
        in: header
        required: false
        schema:
          type: string
          maxLength: 255
        description: Opaque retry key (24h window).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BulkContentPushRequest'
      responses:
        '202':
          description: Accepted — task queued for async processing
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/BulkPushAccepted'
                required:
                - data
        '400':
          description: Bad Request — validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Unauthorized — missing or invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: Forbidden — insufficient permission
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: Conflict (idempotency key reuse or resource conflict)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
      - apiKeyAuth: []
  /api/input/content/push/bulk/status/{task_id}:
    get:
      summary: Check bulk push status
      description: Poll the status of a bulk content push submitted to `/api/input/content/push/bulk`.
      tags:
      - InputAPI
      operationId: inputapi_get_content_push_bulk_status
      parameters:
      - name: task_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/BulkPushStatus'
                required:
                - data
        '202':
          description: Accepted — task queued for async processing
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/BulkPushStatus'
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
      - apiKeyAuth: []
  /api/input/survey/create:
    post:
      summary: Create a data source
      description: Create a new data source, with its questions, inside a feedback group.
      tags:
      - InputAPI
      operationId: inputapi_post_survey_create
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSurveyRequest'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/CreateSurveyResponse'
                required:
                - data
        '400':
          description: Bad Request — validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Unauthorized — missing or invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: Forbidden — insufficient permission
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
      - apiKeyAuth: []
  /api/input/survey/publish:
    post:
      summary: Publish a data source
      description: Publish a data source to enable data collection and analysis.
      tags:
      - InputAPI
      operationId: inputapi_post_survey_publish
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublishSurveyRequest'
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/PublishSurveyResponse'
                required:
                - data
        '400':
          description: Bad Request — validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Unauthorized — missing or invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: Forbidden — insufficient permission
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
      - apiKeyAuth: []
  /api/input/survey_series/create:
    post:
      summary: Create a feedback group
      description: Create a new feedback group to organise related data sources.
      tags:
      - InputAPI
      operationId: inputapi_post_survey_series_create
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSurveySeriesRequest'
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/CreateSurveySeriesResponse'
                required:
                - data
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/CreateSurveySeriesResponse'
                required:
                - data
        '400':
          description: Bad Request — validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Unauthorized — missing or invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: Forbidden — insufficient permission
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
      - apiKeyAuth: []
  /api/input/surveys/list:
    get:
      summary: List feedback groups and data sources
      description: Retrieve all feedback groups and data sources your API key can access.
      tags:
      - InputAPI
      operationId: inputapi_get_surveys_list
      parameters:
      - name: survey_series_id
        in: query
        required: false
        schema:
          type: string
      - name: survey_id
        in: query
        required: false
        schema:
          type: string
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/SurveysListResponse'
                required:
                - data
        '400':
          description: Bad Request — validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Unauthorized — missing or invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: Forbidden — insufficient permission
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
      - apiKeyAuth: []
  /api/webhooks:
    post:
      summary: Create a webhook
      description: 'Register an endpoint to receive real-time event notifications. The signing secret is returned once on
        creation — save it immediately, it cannot be retrieved again. Subscribable events: `content.pushed`, `survey.created`,
        `survey.published`, `series.created`.'
      tags:
      - Webhooks
      operationId: webhooks_post_create
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookRequest'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/CreateWebhookResponse'
                required:
                - data
        '400':
          description: Bad Request — validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Unauthorized — missing or invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: Forbidden — insufficient permission
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
      - bearerAuth: []
      callbacks:
        content.pushed:
          '{$request.body#/url}':
            post:
              summary: Receive a content.pushed event
              description: 'BoundaryAI will POST to your subscribed URL when the `content.pushed` event fires. The body is
                signed with the subscription''s HMAC secret via the `X-Boundary-Signature: sha256=<hex>` header. Respond 2xx
                within 10s or the delivery is retried with exponential backoff.'
              security: []
              requestBody:
                required: true
                content:
                  application/json:
                    schema:
                      $ref: '#/components/schemas/ContentPushedEvent'
              responses:
                '200':
                  description: Acknowledged — no retry
                '204':
                  description: Acknowledged — no retry
                4XX:
                  description: Receiver rejected the delivery — BoundaryAI retries with exponential backoff.
                5XX:
                  description: Receiver errored — BoundaryAI retries with exponential backoff.
                default:
                  description: Any non-2xx triggers retry with exponential backoff up to N attempts.
        series.created:
          '{$request.body#/url}':
            post:
              summary: Receive a series.created event
              description: 'BoundaryAI will POST to your subscribed URL when the `series.created` event fires. The body is
                signed with the subscription''s HMAC secret via the `X-Boundary-Signature: sha256=<hex>` header. Respond 2xx
                within 10s or the delivery is retried with exponential backoff.'
              security: []
              requestBody:
                required: true
                content:
                  application/json:
                    schema:
                      $ref: '#/components/schemas/SeriesCreatedEvent'
              responses:
                '200':
                  description: Acknowledged — no retry
                '204':
                  description: Acknowledged — no retry
                4XX:
                  description: Receiver rejected the delivery — BoundaryAI retries with exponential backoff.
                5XX:
                  description: Receiver errored — BoundaryAI retries with exponential backoff.
                default:
                  description: Any non-2xx triggers retry with exponential backoff up to N attempts.
        survey.created:
          '{$request.body#/url}':
            post:
              summary: Receive a survey.created event
              description: 'BoundaryAI will POST to your subscribed URL when the `survey.created` event fires. The body is
                signed with the subscription''s HMAC secret via the `X-Boundary-Signature: sha256=<hex>` header. Respond 2xx
                within 10s or the delivery is retried with exponential backoff.'
              security: []
              requestBody:
                required: true
                content:
                  application/json:
                    schema:
                      $ref: '#/components/schemas/SurveyCreatedEvent'
              responses:
                '200':
                  description: Acknowledged — no retry
                '204':
                  description: Acknowledged — no retry
                4XX:
                  description: Receiver rejected the delivery — BoundaryAI retries with exponential backoff.
                5XX:
                  description: Receiver errored — BoundaryAI retries with exponential backoff.
                default:
                  description: Any non-2xx triggers retry with exponential backoff up to N attempts.
        survey.published:
          '{$request.body#/url}':
            post:
              summary: Receive a survey.published event
              description: 'BoundaryAI will POST to your subscribed URL when the `survey.published` event fires. The body
                is signed with the subscription''s HMAC secret via the `X-Boundary-Signature: sha256=<hex>` header. Respond
                2xx within 10s or the delivery is retried with exponential backoff.'
              security: []
              requestBody:
                required: true
                content:
                  application/json:
                    schema:
                      $ref: '#/components/schemas/SurveyPublishedEvent'
              responses:
                '200':
                  description: Acknowledged — no retry
                '204':
                  description: Acknowledged — no retry
                4XX:
                  description: Receiver rejected the delivery — BoundaryAI retries with exponential backoff.
                5XX:
                  description: Receiver errored — BoundaryAI retries with exponential backoff.
                default:
                  description: Any non-2xx triggers retry with exponential backoff up to N attempts.
  /webhooks:
    post:
      summary: Receive a signed webhook delivery
      description: Webhook deliveries are sent as HTTP POST requests to your endpoint, signed with HMAC-SHA256 in the `X-Boundary-Signature`
        header. The event type is in `X-Boundary-Event`; the body carries `event`, `timestamp`, and `data`.
      tags:
      - Webhooks
      operationId: webhooks_post_clerk_event
      parameters:
      - name: svix-id
        in: header
        required: true
        schema:
          type: string
        description: Unique delivery ID from Svix
      - name: svix-timestamp
        in: header
        required: true
        schema:
          type: string
        description: Unix timestamp; replay-protected by Svix
      - name: svix-signature
        in: header
        required: true
        schema:
          type: string
        description: HMAC-SHA256 signature over body+timestamp
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClerkWebhookResult'
        '400':
          description: Bad Request — validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Unauthorized — missing or invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
      - webhookSignature: []
  /webhooks/supported-events:
    get:
      summary: List supported webhook events
      description: Returns the catalogue of event types you can subscribe to.
      tags:
      - Webhooks
      operationId: webhooks_get_supported_events
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/SupportedEventsResponse'
                required:
                - data
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
      security: []
components:
  schemas:
    ContentPushedData:
      properties:
        survey_series_id:
          title: Survey Series Id
          type: integer
        survey_id:
          title: Survey Id
          type: integer
        question_id:
          title: Question Id
          type: integer
        original_survey_id:
          title: Original Survey Id
          type: integer
        original_question_id:
          title: Original Question Id
          type: integer
        lines_pushed:
          minimum: 0
          title: Lines Pushed
          type: integer
        api_key_id:
          title: Api Key Id
          type: integer
      required:
      - survey_series_id
      - survey_id
      - question_id
      - original_survey_id
      - original_question_id
      - lines_pushed
      - api_key_id
      title: ContentPushedData
      type: object
    ContentPushedEvent:
      additionalProperties: false
      properties:
        event:
          const: content.pushed
          title: Event
          type: string
        timestamp:
          description: ISO-8601 UTC timestamp
          title: Timestamp
          type: string
        data:
          $ref: '#/components/schemas/ContentPushedData'
      required:
      - event
      - timestamp
      - data
      title: ContentPushedEvent
      type: object
    SeriesCreatedData:
      properties:
        survey_series_id:
          title: Survey Series Id
          type: integer
        name:
          title: Name
          type: string
        api_key_id:
          title: Api Key Id
          type: integer
      required:
      - survey_series_id
      - name
      - api_key_id
      title: SeriesCreatedData
      type: object
    SeriesCreatedEvent:
      additionalProperties: false
      properties:
        event:
          const: series.created
          title: Event
          type: string
        timestamp:
          description: ISO-8601 UTC timestamp
          title: Timestamp
          type: string
        data:
          $ref: '#/components/schemas/SeriesCreatedData'
      required:
      - event
      - timestamp
      - data
      title: SeriesCreatedEvent
      type: object
    SurveyCreatedData:
      properties:
        survey_series_id:
          title: Survey Series Id
          type: integer
        survey_id:
          title: Survey Id
          type: integer
        survey_title:
          title: Survey Title
          type: string
        questions_created:
          minimum: 0
          title: Questions Created
          type: integer
        api_key_id:
          title: Api Key Id
          type: integer
      required:
      - survey_series_id
      - survey_id
      - survey_title
      - questions_created
      - api_key_id
      title: SurveyCreatedData
      type: object
    SurveyCreatedEvent:
      additionalProperties: false
      properties:
        event:
          const: survey.created
          title: Event
          type: string
        timestamp:
          description: ISO-8601 UTC timestamp
          title: Timestamp
          type: string
        data:
          $ref: '#/components/schemas/SurveyCreatedData'
      required:
      - event
      - timestamp
      - data
      title: SurveyCreatedEvent
      type: object
    SurveyPublishedData:
      properties:
        survey_series_id:
          title: Survey Series Id
          type: integer
        survey_id:
          title: Survey Id
          type: integer
        survey_name:
          title: Survey Name
          type: string
        api_key_id:
          title: Api Key Id
          type: integer
      required:
      - survey_series_id
      - survey_id
      - survey_name
      - api_key_id
      title: SurveyPublishedData
      type: object
    SurveyPublishedEvent:
      additionalProperties: false
      properties:
        event:
          const: survey.published
          title: Event
          type: string
        timestamp:
          description: ISO-8601 UTC timestamp
          title: Timestamp
          type: string
        data:
          $ref: '#/components/schemas/SurveyPublishedData'
      required:
      - event
      - timestamp
      - data
      title: SurveyPublishedEvent
      type: object
    ErrorObject:
      description: 'The inner ``error`` field of an error envelope.


        Schema name in the OpenAPI document: ``ErrorObject``.'
      example:
        code: VALIDATION_ERROR
        message: Invalid email address
      properties:
        code:
          description: Machine-readable error code drawn from ErrorCode enum
          examples:
          - VALIDATION_ERROR
          title: Code
          type: string
        message:
          description: Human-readable error message, safe to display
          examples:
          - Invalid email address
          title: Message
          type: string
      required:
      - code
      - message
      title: ErrorObject
      type: object
    ErrorEnvelope:
      description: 'Top-level error response.


        Schema name in the OpenAPI document: ``ErrorEnvelope``.'
      example:
        details:
          field: email
        error:
          code: VALIDATION_ERROR
          message: Invalid email address
      properties:
        error:
          $ref: '#/components/schemas/ErrorObject'
        details:
          anyOf:
          - {}
          - type: 'null'
          default: null
          description: Optional structured details (e.g. validation field map)
          title: Details
      required:
      - error
      title: ErrorEnvelope
      type: object
    ClerkWebhookResult:
      additionalProperties: true
      description: 'Body of POST /webhooks (after successful signature verification).


        The 200 is intentional even on handler errors — see the route

        docstring for the retry-storm rationale. The discriminating field is

        ``status``, not the HTTP code.'
      properties:
        status:
          enum:
          - success
          - unhandled
          - error
          title: Status
          type: string
        event_type:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          description: The Clerk event type (e.g. user.created, organization.updated)
          title: Event Type
        event_id:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          description: Clerk's event ID
          title: Event Id
        error:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          description: Handler error message — only present on status=error
          title: Error
        message:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Message
      required:
      - status
      title: ClerkWebhookResult
      type: object
    SupportedEventsResponse:
      properties:
        supported_events:
          description: List of Clerk event types this server knows how to handle
          items:
            type: string
          title: Supported Events
          type: array
        total_count:
          minimum: 0
          title: Total Count
          type: integer
      required:
      - supported_events
      - total_count
      title: SupportedEventsResponse
      type: object
    QuestionContentPush:
      example:
        content:
        - Customer feedback line 1
        - Customer feedback line 2
        question_id: '12345'
      properties:
        question_id:
          description: Target question id (integer as string)
          title: Question Id
          type: string
        content:
          description: Free-text responses, one per array entry
          items:
            type: string
          minItems: 1
          title: Content
          type: array
      required:
      - question_id
      - content
      title: QuestionContentPush
      type: object
    ContentPushRequest:
      properties:
        survey_series_id:
          description: Target survey series id
          title: Survey Series Id
          type: string
        survey_id:
          description: Target survey id within the series
          title: Survey Id
          type: string
        question:
          $ref: '#/components/schemas/QuestionContentPush'
        source_reference:
          anyOf:
          - maxLength: 255
            type: string
          - type: 'null'
          default: null
          description: Free-form identifier the customer attaches to the push for their own downstream tracking (e.g. an upstream
            ticket id).
          title: Source Reference
      required:
      - survey_series_id
      - survey_id
      - question
      title: ContentPushRequest
      type: object
    ContentPushResponse:
      description: Success body of POST /api/input/content/push.
      example:
        aps_deducted: 12
        inserted: 12
        message: Content pushed successfully
        question_id: 100
        status: success
        survey_id: 42
      properties:
        status:
          const: success
          description: Always 'success' on this path
          title: Status
          type: string
        survey_id:
          description: Resolved survey id where content landed
          title: Survey Id
          type: integer
        question_id:
          description: Resolved question id where content landed
          title: Question Id
          type: integer
        inserted:
          description: Number of content rows inserted
          minimum: 0
          title: Inserted
          type: integer
        aps_deducted:
          description: APS units deducted from the org's quota
          minimum: 0
          title: Aps Deducted
          type: integer
        message:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          description: Human-readable confirmation
          title: Message
      required:
      - status
      - survey_id
      - question_id
      - inserted
      - aps_deducted
      title: ContentPushResponse
      type: object
    BulkQuestionContentPush:
      properties:
        question_id:
          description: Target question id
          title: Question Id
          type: string
        content:
          description: Free-text responses
          items:
            type: string
          minItems: 1
          title: Content
          type: array
      required:
      - question_id
      - content
      title: BulkQuestionContentPush
      type: object
    BulkContentPushRequest:
      example:
        questions:
        - content:
          - Customer feedback line 1
          - Customer feedback line 2
          question_id: '100'
        - content:
          - Another verbatim from a different question
          question_id: '101'
        source_reference: zendesk-ticket-9001
        survey_id: '42'
        survey_series_id: '7'
      properties:
        survey_series_id:
          title: Survey Series Id
          type: string
        survey_id:
          title: Survey Id
          type: string
        questions:
          description: Up to 100 question-content pairs in a single batch
          items:
            $ref: '#/components/schemas/BulkQuestionContentPush'
          maxItems: 100
          minItems: 1
          title: Questions
          type: array
        source_reference:
          anyOf:
          - maxLength: 255
            type: string
          - type: 'null'
          default: null
          title: Source Reference
      required:
      - survey_series_id
      - survey_id
      - questions
      title: BulkContentPushRequest
      type: object
    BulkPushAccepted:
      description: 202 body of POST /api/input/content/push/bulk.
      example:
        status: accepted
        status_url: /api/input/content/push/bulk/status/bp_abc123
        survey_id: 42
        survey_series_id: 7
        task_id: bp_abc123
      properties:
        status:
          const: accepted
          title: Status
          type: string
        task_id:
          title: Task Id
          type: string
        survey_series_id:
          title: Survey Series Id
          type: integer
        survey_id:
          title: Survey Id
          type: integer
        status_url:
          title: Status Url
          type: string
      required:
      - status
      - task_id
      - survey_series_id
      - survey_id
      - status_url
      title: BulkPushAccepted
      type: object
    BulkPushStatus:
      description: 200/202 body of GET /api/input/content/push/bulk/status/{task_id}.
      example:
        completed_at: null
        created_at: '2026-06-08T10:15:00Z'
        error: null
        progress: 0.6
        progress_message: Inserting batch 3/5
        result: null
        status: processing
        survey_id: 42
        survey_series_id: 7
        task_id: bp_abc123
      properties:
        task_id:
          title: Task Id
          type: string
        status:
          enum:
          - pending
          - processing
          - completed
          - failed
          title: Status
          type: string
        survey_series_id:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Survey Series Id
        survey_id:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Survey Id
        progress:
          default: 0
          description: 0..1 fraction complete
          maximum: 1
          minimum: 0
          title: Progress
          type: number
        progress_message:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Progress Message
        created_at:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          description: ISO-8601 UTC
          title: Created At
        completed_at:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          description: ISO-8601 UTC
          title: Completed At
        result:
          anyOf:
          - additionalProperties: true
            type: object
          - type: 'null'
          default: null
          description: Result payload mirroring ContentPushResponse, present on status=completed
          title: Result
        error:
          anyOf:
          - additionalProperties: true
            type: object
          - type: 'null'
          default: null
          description: Error payload, present on status=failed
          title: Error
      required:
      - task_id
      - status
      title: BulkPushStatus
      type: object
    CreateSurveySeriesRequest:
      properties:
        name:
          description: Series display name
          maxLength: 255
          minLength: 2
          title: Name
          type: string
      required:
      - name
      title: CreateSurveySeriesRequest
      type: object
    CreatedSeriesMinimal:
      properties:
        id:
          title: Id
          type: integer
        name:
          title: Name
          type: string
      required:
      - id
      - name
      title: CreatedSeriesMinimal
      type: object
    CreateSurveySeriesResponse:
      description: 200 (already exists) / 201 (created) body.
      properties:
        status:
          const: success
          title: Status
          type: string
        created:
          description: False when the series already existed
          title: Created
          type: boolean
        message:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Message
        survey_series:
          $ref: '#/components/schemas/CreatedSeriesMinimal'
      required:
      - status
      - created
      - survey_series
      title: CreateSurveySeriesResponse
      type: object
    CreateSurveyQuestion:
      description: 'One question in the create-survey payload. Permissive on

        question_type to match the existing case-insensitive parser.'
      properties:
        question_title:
          maxLength: 500
          minLength: 1
          title: Question Title
          type: string
        question_type:
          description: One of DEPTH_TEXT, TEXT, MCQ, SCQ, RATING, NPS, METADATA (case-insensitive; also accepts long_answer
            / short_answer).
          title: Question Type
          type: string
        question_options:
          items:
            type: string
          maxItems: 100
          title: Question Options
          type: array
        is_mandatory:
          default: false
          title: Is Mandatory
          type: boolean
        min_value:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Min Value
        max_value:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Max Value
        min_label:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Min Label
        max_label:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Max Label
      required:
      - question_title
      - question_type
      title: CreateSurveyQuestion
      type: object
    CreateSurveyRequest:
      properties:
        survey_series_id:
          title: Survey Series Id
          type: string
        survey_title:
          maxLength: 100
          minLength: 1
          title: Survey Title
          type: string
        survey_description:
          default: ''
          maxLength: 1000
          title: Survey Description
          type: string
        questions:
          items:
            $ref: '#/components/schemas/CreateSurveyQuestion'
          title: Questions
          type: array
        language:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          description: EN | FR | ES (or full names; case-insensitive)
          title: Language
      required:
      - survey_series_id
      - survey_title
      title: CreateSurveyRequest
      type: object
    CreatedQuestion:
      properties:
        id:
          title: Id
          type: integer
        title:
          title: Title
          type: string
        type:
          description: The normalized question type echoed back
          title: Type
          type: string
      required:
      - id
      - title
      - type
      title: CreatedQuestion
      type: object
    CreateSurveyResponse:
      properties:
        status:
          const: success
          title: Status
          type: string
        survey_series_id:
          title: Survey Series Id
          type: integer
        survey_id:
          title: Survey Id
          type: integer
        questions_created:
          title: Questions Created
          type: integer
        questions:
          items:
            $ref: '#/components/schemas/CreatedQuestion'
          title: Questions
          type: array
        message:
          title: Message
          type: string
      required:
      - status
      - survey_series_id
      - survey_id
      - questions_created
      - questions
      - message
      title: CreateSurveyResponse
      type: object
    PublishSurveyRequest:
      properties:
        survey_id:
          title: Survey Id
          type: integer
        survey_series_id:
          title: Survey Series Id
          type: integer
      required:
      - survey_id
      - survey_series_id
      title: PublishSurveyRequest
      type: object
    PublishSurveyResponse:
      properties:
        status:
          const: success
          title: Status
          type: string
        survey_id:
          title: Survey Id
          type: integer
        survey_series_id:
          title: Survey Series Id
          type: integer
        mode:
          const: ACCESSIBLE
          title: Mode
          type: string
        message:
          title: Message
          type: string
      required:
      - status
      - survey_id
      - survey_series_id
      - mode
      - message
      title: PublishSurveyResponse
      type: object
    QuestionListing:
      properties:
        id:
          title: Id
          type: integer
        title:
          title: Title
          type: string
        type:
          title: Type
          type: string
        options:
          anyOf:
          - items:
              type: string
            type: array
          - type: 'null'
          default: null
          title: Options
        accepts_content:
          anyOf:
          - type: boolean
          - type: 'null'
          default: null
          title: Accepts Content
        content_format:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          description: One of text | numeric | single_choice | multi_choice
          title: Content Format
        min:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Min
        max:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Max
      required:
      - id
      - title
      - type
      title: QuestionListing
      type: object
    SeriesListing:
      properties:
        id:
          title: Id
          type: integer
        name:
          title: Name
          type: string
        surveys:
          items:
            $ref: '#/components/schemas/SurveyListing'
          title: Surveys
          type: array
      required:
      - id
      - name
      title: SeriesListing
      type: object
    SurveyListing:
      properties:
        id:
          title: Id
          type: integer
        title:
          title: Title
          type: string
        description:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Description
        mode:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Mode
        questions:
          items:
            $ref: '#/components/schemas/QuestionListing'
          title: Questions
          type: array
      required:
      - id
      - title
      title: SurveyListing
      type: object
    SurveysListResponse:
      description: 'Body of GET /api/input/surveys and /api/input/surveys/list.


        The `org_id` field is present on the API-key endpoint and omitted on

        the Clerk endpoint — declare it Optional.'
      example:
        org_id: 31
        survey_series:
        - id: 7
          name: Customer Support
          surveys:
          - description: Monthly post-ticket survey
            id: 42
            mode: ACCESSIBLE
            questions:
            - accepts_content: true
              content_format: text
              id: 100
              title: What could we improve?
              type: long_answer
            title: CSAT — March 2026
      properties:
        survey_series:
          items:
            $ref: '#/components/schemas/SeriesListing'
          title: Survey Series
          type: array
        org_id:
          anyOf:
          - type: integer
          - type: 'null'
          default: null
          title: Org Id
        message:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Message
      title: SurveysListResponse
      type: object
    WebhookDict:
      additionalProperties: true
      description: Public representation of a configured webhook subscription.
      properties:
        id:
          title: Id
          type: integer
        org_id:
          title: Org Id
          type: integer
        name:
          title: Name
          type: string
        url:
          title: Url
          type: string
        events:
          items:
            type: string
          title: Events
          type: array
        is_active:
          title: Is Active
          type: boolean
        created_at:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Created At
        last_delivery_at:
          anyOf:
          - type: string
          - type: 'null'
          default: null
          title: Last Delivery At
      required:
      - id
      - org_id
      - name
      - url
      - events
      - is_active
      title: WebhookDict
      type: object
    CreateWebhookRequest:
      properties:
        name:
          maxLength: 255
          minLength: 1
          title: Name
          type: string
        url:
          description: HTTPS endpoint to POST events to
          title: Url
          type: string
        events:
          description: One or more WebhookEventType values
          items:
            type: string
          minItems: 1
          title: Events
          type: array
      required:
      - name
      - url
      - events
      title: CreateWebhookRequest
      type: object
    CreateWebhookResponse:
      properties:
        webhook:
          $ref: '#/components/schemas/WebhookDict'
        secret:
          description: HMAC secret shown once. Returned again only via regenerate-secret.
          title: Secret
          type: string
        message:
          title: Message
          type: string
      required:
      - webhook
      - secret
      - message
      title: CreateWebhookResponse
      type: object
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Clerk-issued JWT. Validated by the before_app_request hook in src/boundaryai_auth/routes/authentication.py.
        Token is expected in the Authorization header as `Bearer <jwt>`.
    apiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: BoundaryAI API key
      description: 'Input API key. Format `inpk_{env}_{key_id}_{secret}` — send as `Authorization: Bearer <key>`. Create keys
        under Integrations → API; `test` keys are not charged Analysis Points.'
    webhookSignature:
      type: apiKey
      in: header
      name: svix-signature
      description: HMAC-SHA256 signature of the raw request body in the `X-Boundary-Signature` header, computed with the signing
        secret returned at webhook creation.
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: Opaque client-supplied key for safe POST retries. Repeated requests with the same key within a 24-hour
        window replay the original response instead of executing the mutation twice. Reusing the same key with a DIFFERENT
        request body returns 409 `IDEMPOTENCY_ERROR`. Backed by ``input_api_idempotency_keys`` in Postgres.
      schema:
        type: string
        minLength: 1
        maxLength: 255
        examples:
        - evt_2025_05_19_a1b2c3
  headers:
    RetryAfter:
      description: Seconds to wait before retrying. Emitted on 429 and on 503 responses from the global rate limiter. See
        ``src/utils/middleware/rate_limiting.py``.
      schema:
        type: integer
        minimum: 1
    XRateLimitLimit:
      description: Total requests allowed in the current rate-limit window.
      schema:
        type: integer
        minimum: 1
    XRateLimitRemaining:
      description: Requests remaining in the current rate-limit window.
      schema:
        type: integer
        minimum: 0
    XRateLimitReset:
      description: Unix timestamp when the rate-limit window resets.
      schema:
        type: integer
        minimum: 0
  responses:
    RateLimited:
      description: Rate limit exceeded — see Retry-After header.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        X-RateLimit-Limit:
          $ref: '#/components/headers/XRateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/XRateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/XRateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
