openapi: 3.0.3
info:
  title: Kno Commerce REST API
  version: 1.3.0
  description: >-
    REST API for programmatic access to Kno Commerce survey, response, webhook, and attribution data.


    All requests must be made over TLS (HTTPS). Plaintext HTTP requests will be rejected.


    ## Quick Start


    1. **Register an API client** — Go to Settings > API Access in the Kno Commerce dashboard to create a client and
    receive your `client_id` and `client_secret`.

    2. **Get an access token** — Exchange your credentials for a Bearer token via `POST /api/oauth2/token` (see the
    Authentication section below).

    3. **Make requests** — Include the token as `Authorization: Bearer <token>` on all subsequent API calls.
servers:
  - url: https://app-api.knocommerce.com
    description: Production
components:
  securitySchemes:
    OAuth2:
      type: oauth2
      description: OAuth 2.0 Client Credentials flow. Obtain an access token via POST /api/oauth2/token.
      flows:
        clientCredentials:
          tokenUrl: https://api.knocommerce.com/api/oauth2/token
          scopes:
            ATTRIBUTION: Access attribution analytics
            QUESTIONS: Access benchmark questions
            RESPONSES: Access survey responses
            SURVEYS: Access surveys
            WEBHOOKS: Manage webhook subscriptions
    BearerAuth:
      type: http
      scheme: bearer
      description: Bearer token obtained from the OAuth2 Client Credentials flow.
    BasicAuth:
      type: http
      scheme: basic
      description: HTTP Basic authentication using Base64-encoded clientId:clientSecret.
  schemas:
    OAuthTokenResponse:
      type: object
      properties:
        token_type:
          type: string
          example: bearer
        expires_in:
          type: integer
          description: Token lifetime in seconds.
          example: 3600
        access_token:
          type: string
          description: The Bearer access token.
        scope:
          type: string
          description: Granted scopes.
          example: SURVEYS RESPONSES
    CreateResponseRequest:
      type: object
      required:
        - surveyId
      properties:
        surveyId:
          type: string
          format: uuid
          description: The survey to create a response (view) for.
        customer:
          type: object
          properties:
            email:
              type: string
              format: email
              nullable: true
            phone:
              type: string
    UpdateResponseRequest:
      type: object
      required:
        - responses
      properties:
        responses:
          type: array
          description: Array of question answers.
          items:
            type: object
            required:
              - value
            properties:
              questionId:
                type: string
                format: uuid
              value:
                description: Answer value. Can be a string, number, or array. Null is allowed.
                oneOf:
                  - type: string
                    nullable: true
                  - type: number
                  - type: array
                    items:
                      oneOf:
                        - type: string
                        - type: number
              label:
                type: string
              type:
                type: string
              other:
                type: boolean
              otherValue:
                type: string
              duration:
                type: number
        isCompleted:
          type: boolean
          default: false
          description: Set to true on the final answer submission.
        timeSpent:
          type: number
          nullable: true
        currentQuestionId:
          type: string
          format: uuid
    CreateWebhookRequest:
      type: object
      required:
        - topic
        - url
      properties:
        topic:
          type: string
          enum:
            - response/create
          description: Webhook event topic.
        url:
          type: string
          format: uri
          description: HTTPS endpoint to receive webhook payloads.
    AttributionRequest:
      type: object
      properties:
        dateRange:
          type: object
          nullable: true
          properties:
            first:
              type: object
              properties:
                start:
                  type: string
                  format: date
                end:
                  type: string
                  format: date
            second:
              type: object
              properties:
                start:
                  type: string
                  format: date
                  nullable: true
                end:
                  type: string
                  format: date
                  nullable: true
        tag:
          type: string
          enum:
            - ATTRIBUTION
            - attribution
        modeled:
          type: boolean
          description: Whether to return modeled/computed attribution data.
    Survey:
      type: object
      properties:
        id:
          type: string
          format: uuid
        accountId:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        title:
          type: string
        status:
          type: string
          enum:
            - live
            - draft
        questions:
          type: array
          items:
            $ref: "#/components/schemas/SurveyQuestion"
    SurveyQuestion:
      type: object
      properties:
        id:
          type: string
          format: uuid
        label:
          type: string
        type:
          type: string
        values:
          type: array
          items:
            type: object
    ResponseObject:
      type: object
      properties:
        id:
          type: string
          format: uuid
        account_id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
          nullable: true
        customer_id:
          type: string
          nullable: true
        customer_email:
          type: string
          format: email
          nullable: true
        customer_phone:
          type: string
          nullable: true
        customer_shop:
          type: string
          nullable: true
        customer_lifetime_spent:
          type: number
        customer_lifetime_orders:
          type: number
        time_spent:
          type: number
          nullable: true
        survey_id:
          type: string
          format: uuid
        order:
          type: object
          nullable: true
        response:
          type: array
          nullable: true
          items:
            type: object
        current_response:
          type: object
          nullable: true
    WebhookSubscription:
      type: object
      properties:
        id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
        topic:
          type: string
        url:
          type: string
          format: uri
    WebhookSubscriptionWithSecret:
      type: object
      properties:
        id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
        topic:
          type: string
        url:
          type: string
          format: uri
        secret:
          type: string
          description: Webhook signing secret. Only returned on creation.
    BenchmarkQuestion:
      type: object
      properties:
        id:
          type: string
          format: uuid
        label:
          type: string
        title:
          type: string
    PaginatedSurveys:
      type: object
      properties:
        total:
          type: integer
        results:
          type: array
          items:
            $ref: "#/components/schemas/Survey"
    PaginatedResponses:
      type: object
      properties:
        total:
          type: integer
          description: Total count (only present with deprecated offset pagination).
        nextPageToken:
          type: string
          nullable: true
          description: Opaque token for the next page of results. `null` when no more results. Only present with cursor pagination.
        prevPageToken:
          type: string
          nullable: true
          description: Opaque token for the previous page (toward newer results). Only present when not on the first page. Pass as
            `prevPageToken` query param.
        hasMore:
          type: boolean
          description: Whether additional results exist beyond the current page. Only present with cursor pagination.
        results:
          type: array
          items:
            $ref: "#/components/schemas/ResponseObject"
    DateRangeFilter:
      type: object
      properties:
        gte:
          type: string
          format: date-time
          description: Greater than or equal (ISO 8601 or YYYY-MM-DD).
        lte:
          type: string
          format: date-time
          description: Less than or equal (ISO 8601 or YYYY-MM-DD).
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
        message:
          type: string
    AttributionResponse:
      type: object
      description: Attribution analytics data. Shape varies based on account configuration.
      additionalProperties: true
tags:
  - name: Authentication
    description: >-
      The API uses the **OAuth 2.0 Client Credentials** grant ([RFC 6749
      §4.4](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4)) for machine-to-machine access.


      ## Obtaining a Token


      Send your credentials using **HTTP Basic** authentication ([RFC 6749
      §2.3.1](https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1)) — this is the only supported method for
      passing client credentials.


      Build the `Authorization` header by URL-encoding each value, joining them with a colon, and Base64-encoding the
      result:


      ```js

      const querystring = require('querystring');

      const credentials = `${querystring.escape(clientId)}:${querystring.escape(clientSecret)}`;

      const headerValue = `Basic ${Buffer.from(credentials).toString('base64')}`;

      ```


      Then request a token, specifying the scopes you need:


      ```

      POST https://api.knocommerce.com/api/oauth2/token

      Authorization: Basic <base64-encoded credentials>

      Content-Type: application/x-www-form-urlencoded


      grant_type=client_credentials&scope=SURVEYS+RESPONSES

      ```


      ## Scopes


      | Scope | Access |

      | --- | --- |

      | `ATTRIBUTION` | Attribution analytics |

      | `QUESTIONS` | Benchmark questions |

      | `RESPONSES` | Survey responses |

      | `SURVEYS` | Surveys |

      | `WEBHOOKS` | Webhook subscriptions |


      ## Error Handling


      If a request is unauthorized or the token is invalid, the response includes a `WWW-Authenticate` header with error
      context to help diagnose the issue.
  - name: Surveys
    description: Retrieve surveys and their configuration, including questions, logic, and display settings.
  - name: Responses
    description: Access individual survey responses. Supports cursor-based pagination; offset-based pagination is deprecated.
  - name: Webhooks
    description: Manage webhook subscriptions to receive real-time notifications when survey events occur. Each subscription
      includes a signing secret for verifying payload authenticity.
  - name: Questions
    description: Access benchmark questions and their metadata.
  - name: Attribution
    description: Retrieve attribution analytics data. **Deprecated** — this resource is scheduled for removal in a future version.
paths:
  /api/rest/surveys:
    get:
      operationId: listSurveys
      summary: List surveys
      description: Returns a paginated list of surveys for the authenticated account.
      tags:
        - Surveys
      security:
        - BearerAuth: []
      parameters:
        - in: query
          name: startDate
          schema:
            type: string
            format: date
          description: Filter surveys created on or after this date (ISO 8601 or YYYY-MM-DD).
        - in: query
          name: endDate
          schema:
            type: string
            format: date
          description: Filter surveys created on or before this date.
        - in: query
          name: page
          schema:
            type: integer
            minimum: 1
            default: 1
        - in: query
          name: pageSize
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 25
        - in: query
          name: status
          schema:
            type: string
            enum:
              - live
              - draft
        - in: query
          name: search
          schema:
            type: string
            maxLength: 100
          description: Search surveys by title.
      responses:
        "200":
          description: Paginated list of surveys.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedSurveys"
        "401":
          description: Unauthorized — invalid or expired token.
        "403":
          description: Forbidden — insufficient scope.
        "422":
          description: Validation error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limited.
  /api/rest/responses:
    get:
      operationId: listResponses
      summary: List survey responses
      description: >
        Returns survey responses ordered by `createdAt` descending (newest first).


        **Cursor pagination (recommended):** Use `maxPageSize` with optional `pageToken` or `prevPageToken`.

        - Forward (default): results page from newest to oldest. Use `nextPageToken` from the response to fetch the next
        page.

        - Backward: pass `prevPageToken` to page toward newer results. Cannot be combined with `pageToken`.

        - Both directions return results in descending `createdAt` order.

        - Page tokens are opaque, encrypted, and do not expire. They remain valid as long as the access token
        authenticates.

        - `hasMore` indicates whether additional results exist beyond the current page.


        **Offset pagination (deprecated):** Use `page` + `pageSize`. Migrate to cursor pagination for stable iteration
        over large datasets.
      tags:
        - Responses
      security:
        - BearerAuth: []
      parameters:
        - in: query
          name: maxPageSize
          schema:
            type: integer
            minimum: 1
            maximum: 250
          description: Page size for cursor-based pagination (recommended).
        - in: query
          name: pageToken
          schema:
            type: string
          description: Token for the next page (from previous response's `nextPageToken`). Cannot be combined with `prevPageToken`.
        - in: query
          name: prevPageToken
          schema:
            type: string
          description: Token for the previous page (from previous response's `prevPageToken`). Pages toward newer results. Cannot
            be combined with `pageToken`.
        - in: query
          name: page
          schema:
            type: integer
            minimum: 1
          description: Page number for offset pagination (deprecated — use cursor pagination).
          deprecated: true
        - in: query
          name: pageSize
          schema:
            type: integer
            minimum: 5
            maximum: 250
          description: Page size for offset pagination (deprecated).
          deprecated: true
        - in: query
          name: surveyId
          schema:
            type: string
            format: uuid
          description: Filter by survey ID.
        - in: query
          name: questionId
          schema:
            type: string
            format: uuid
          description: Filter by question ID.
        - in: query
          name: status
          schema:
            type: string
            enum:
              - completed
              - partial
              - view_only
          description: Filter by response status.
        - in: query
          name: updatedAt
          style: deepObject
          explode: true
          schema:
            $ref: "#/components/schemas/DateRangeFilter"
          description: Filter by updatedAt date range.
        - in: query
          name: completedAt
          style: deepObject
          explode: true
          schema:
            $ref: "#/components/schemas/DateRangeFilter"
          description: Filter by completedAt date range.
        - in: query
          name: expand
          schema:
            type: array
            items:
              type: string
              enum:
                - order
          description: Include related resources in the response.
        - in: query
          name: sanitize
          schema:
            type: boolean
          description: Redact PII fields (email, phone) from the response.
      responses:
        "200":
          description: List of responses.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedResponses"
        "401":
          description: Unauthorized — invalid or expired token.
        "403":
          description: Forbidden — insufficient scope.
        "422":
          description: Validation error or invalid page token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limited.
  /api/rest/responses/count:
    get:
      operationId: getResponsesCount
      summary: Get response count
      description: Returns the count of responses matching the given filters.
      tags:
        - Responses
      security:
        - BearerAuth: []
      deprecated: true
      parameters:
        - in: query
          name: surveyId
          schema:
            type: string
            format: uuid
        - in: query
          name: status
          schema:
            type: string
            enum:
              - completed
              - partial
              - view_only
        - in: query
          name: questionId
          schema:
            type: string
            format: uuid
        - in: query
          name: createdAt
          style: deepObject
          explode: true
          schema:
            $ref: "#/components/schemas/DateRangeFilter"
        - in: query
          name: updatedAt
          style: deepObject
          explode: true
          schema:
            $ref: "#/components/schemas/DateRangeFilter"
        - in: query
          name: completedAt
          style: deepObject
          explode: true
          schema:
            $ref: "#/components/schemas/DateRangeFilter"
      responses:
        "200":
          description: Response count.
          content:
            application/json:
              schema:
                type: object
                properties:
                  count:
                    type: integer
        "401":
          description: Unauthorized — invalid or expired token.
        "403":
          description: Forbidden — insufficient scope.
        "422":
          description: Validation error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limited.
  /api/rest/attribution:
    post:
      operationId: getAttribution
      summary: Get customer attribution analytics
      description: |
        Returns customer attribution analytics data for the authenticated account.
        This endpoint is deprecated with no replacement.
      deprecated: true
      tags:
        - Attribution
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AttributionRequest"
      responses:
        "200":
          description: Attribution data.
          headers:
            X-Kno-API-Deprecated:
              schema:
                type: string
              description: Indicates this endpoint is deprecated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AttributionResponse"
        "401":
          description: Unauthorized — invalid or expired token.
        "403":
          description: Forbidden — insufficient scope.
        "422":
          description: Validation error or unprocessable content.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limited.
  /api/rest/webhooks:
    post:
      operationId: createWebhook
      summary: Create a webhook subscription
      description: Registers a new webhook subscription. The response includes a signing secret — store it securely, as it is
        not retrievable later.
      tags:
        - Webhooks
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWebhookRequest"
      responses:
        "201":
          description: Webhook created. Includes signing secret.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      webhook:
                        $ref: "#/components/schemas/WebhookSubscriptionWithSecret"
        "400":
          description: Invalid request body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized — invalid or expired token.
        "403":
          description: Forbidden — insufficient scope.
        "409":
          description: Webhook with this URL and topic already exists.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "422":
          description: Plan does not support API access.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limited.
    get:
      operationId: listWebhooks
      summary: List webhook subscriptions
      description: Returns all webhook subscriptions for the authenticated account. The signing secret is not included.
      tags:
        - Webhooks
      security:
        - BearerAuth: []
      responses:
        "200":
          description: List of webhook subscriptions.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      webhooks:
                        type: array
                        items:
                          $ref: "#/components/schemas/WebhookSubscription"
        "401":
          description: Unauthorized — invalid or expired token.
        "403":
          description: Forbidden — insufficient scope.
        "422":
          description: Plan does not support API access.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limited.
  /api/rest/webhooks/{id}:
    delete:
      operationId: deleteWebhook
      summary: Delete a webhook subscription
      description: Permanently deletes a webhook subscription by ID.
      tags:
        - Webhooks
      security:
        - BearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
          description: Webhook subscription ID.
      responses:
        "204":
          description: Webhook deleted successfully.
        "400":
          description: Invalid webhook ID format.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized — invalid or expired token.
        "403":
          description: Forbidden — insufficient scope.
        "404":
          description: Webhook not found.
        "429":
          description: Rate limited.
  /api/rest/questions/benchmarks:
    get:
      operationId: listBenchmarkQuestions
      summary: List benchmark questions
      description: Returns benchmark survey questions for the authenticated account.
      tags:
        - Questions
      security:
        - BearerAuth: []
      responses:
        "200":
          description: List of benchmark questions.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      questions:
                        type: array
                        items:
                          $ref: "#/components/schemas/BenchmarkQuestion"
        "401":
          description: Unauthorized — invalid or expired token.
        "403":
          description: Forbidden — insufficient scope.
        "422":
          description: Plan does not support API access.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limited.
  /api/oauth2/token:
    post:
      operationId: createAccessToken
      summary: Request an OAuth 2.0 access token
      description: |
        Exchange client credentials for a Bearer access token using the OAuth 2.0 Client Credentials flow.
        Pass your `clientId` and `clientSecret` as HTTP Basic authentication (Base64-encoded `clientId:clientSecret`).
      tags:
        - Authentication
      security:
        - BasicAuth: []
      parameters:
        - in: query
          name: grant_type
          required: true
          schema:
            type: string
            enum:
              - client_credentials
          description: Must be "client_credentials".
        - in: query
          name: scope
          required: true
          schema:
            type: string
          description: Space-separated list of requested scopes (e.g. "SURVEYS RESPONSES").
      responses:
        "200":
          description: Access token issued successfully.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OAuthTokenResponse"
        "400":
          description: Invalid request or credentials.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: invalid_authorization_header
        "422":
          description: API access unavailable on current plan.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limited — too many token requests.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Too many requests, please try again later.
