> ## Documentation Index
> Fetch the complete documentation index at: https://formbricks.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# List surveys

> Returns surveys for the workspace. Session cookie or x-api-key.




## OpenAPI

````yaml /api-v3-reference/openapi.yml get /api/v3/surveys
openapi: 3.1.1
info:
  title: Formbricks API v3
  description: >
    **Surveys**: **GET /api/v3/surveys**, **POST /api/v3/surveys**, **POST
    /api/v3/surveys/generate**, **POST /api/v3/surveys/validate**, **GET
    /api/v3/surveys/{surveyId}**, **PATCH /api/v3/surveys/{surveyId}**, and
    **DELETE /api/v3/surveys/{surveyId}**.


    **Workflows extension**: **GET /api/v3/workflows**, **POST
    /api/v3/workflows**, **GET /api/v3/workflows/{workflowId}**, **PATCH
    /api/v3/workflows/{workflowId}**, **DELETE /api/v3/workflows/{workflowId}**,
    **POST /api/v3/workflows/{workflowId}/duplicate**, **POST
    /api/v3/workflows/{workflowId}/enable**, **POST
    /api/v3/workflows/{workflowId}/disable**, **POST
    /api/v3/workflows/{workflowId}/archive**, **POST
    /api/v3/workflows/{workflowId}/unarchive**, **POST
    /api/v3/workflows/{workflowId}/test**, **GET /api/v3/workflows/runs**, and
    **GET /api/v3/workflows/runs/{runId}**.


    Authenticate all management endpoints with **session cookie** or
    **`x-api-key`** (management key with access to the workspace).


    **Spec location:** source of truth is the multi-file tree at
    `docs/api-v3-reference/src/` (root `openapi.yml` plus one file per path and
    component); `docs/api-v3-reference/openapi.yml` is the generated single-file
    bundle (alongside v2 at `docs/api-v2-reference/openapi.yml`).


    **workspaceId**

    `workspaceId` is the canonical container identifier for this API. It is a
    **required query parameter on collection read endpoints** (`GET
    /api/v3/surveys`, `GET /api/v3/action-classes`, `GET
    /api/v3/contact-attribute-keys`) because those operate within a workspace.
    **`POST /api/v3/surveys` instead requires `workspaceId` in the request
    body**, not as a query parameter. Single-resource endpoints
    (`GET`/`PATCH`/`DELETE /api/v3/surveys/{surveyId}`) locate the resource by
    its globally-unique id and resolve the workspace from it, so they do **not**
    accept a `workspaceId` query parameter — sending one returns **400**
    (`Unrecognized key: "workspaceId"`).


    **Auth**

    Authenticate with either a session cookie or **`x-api-key`**. In dual-auth
    mode, V3 checks the API key first when the header is present, otherwise it
    uses the session path. Unauthenticated callers get **401** before query
    validation.


    **Pagination**

    Cursor-based pagination with **limit** + opaque **cursor** token. Responses
    return `meta.nextCursor`; pass that value back as `cursor` to fetch the next
    page. Survey list responses also include `meta.totalCount`, the total number
    of surveys matching the current filters across all pages. Workflow list
    responses do not require total counts in Scope 1. There is no `offset` in
    this contract.


    **Filtering**

    Filters use explicit operator-style query parameters under the
    **`filter[...]` family**. The survey list supports `filter[name][contains]`,
    `filter[status][in]`, and `filter[type][in]`. The workflow list supports
    `filter[name][contains]` and `filter[status][in]`; the workflow run list
    supports `filter[status][in]` and `filter[isDryRun][eq]`. Resource-scoping
    identifiers (`workspaceId`, `workflowId`, `responseId`) stay plain query
    parameters. Multi-value filters use repeated keys or comma-separated values
    (e.g. `filter[status][in]=draft&filter[status][in]=inProgress` or
    `filter[status][in]=draft,inProgress`). Sorting remains a flat `sortBy`
    query parameter.


    **Security**

    Missing/forbidden workspace returns **403** with a generic message (not
    **404**) so resource existence is not leaked. Workflow and workflow-run
    detail endpoints also return **403** for unknown IDs or inaccessible IDs to
    avoid IDOR leaks. List responses use `private, no-store`.


    **AI survey creation**

    `POST /api/v3/surveys/generate` returns a draft `POST /api/v3/surveys`
    payload plus validation metadata. It does not create a survey. It generates
    survey content for both `link` and `app` surveys; for `app` it seeds a
    default `distribution` (display once, no triggers, no targeting) that you
    finish configuring before publishing.

    Prompt text is sent to the configured AI provider for generation, but it is
    not stored by this endpoint, not logged by default, and not included in
    audit data.


    **Feedback Records** `/api/v3/feedbackRecords` is served by the **API
    gateway** (Envoy on Formbricks Cloud, Traefik in the one-click Docker
    install), which authorizes each call against the dataset and forwards it to
    the feedback store — not by the application routes that serve the rest of
    v3. Three consequences: the payload is the store's own, so members and
    filters are **`snake_case`** (`tenant_id`, `value_text`) rather than v3's
    camelCase; the dataset is named **`tenant_id`** on the wire and is required
    on every collection operation (it is the feedback dataset id, which the MCP
    `list_feedback_datasets` tool reports as `dataset_id`); and errors come in
    two shapes — gateway refusals (**400**, **401**, **403**, **413**, **503**)
    are `text/plain`, while the store's own problems are
    `application/problem+json` with a stable `code`. These eight operations are
    the whole of the gateway-served surface; any other path it receives under
    the prefix answers **400**. Authenticate with **`x-api-key`**: the key needs
    a permission on a workspace the dataset is assigned to — `read` to list, get
    and search, `write` to create and update, `manage` to delete — and may
    update or delete only in a dataset assigned to exactly one workspace.

    **OpenAPI**

    This spec is **not** produced by `pnpm generate-api-specs` (that script only
    builds v2 → `docs/api-v2-reference/openapi.yml`). When the route contract
    changes, edit the files under `docs/api-v3-reference/src/`, then run `pnpm
    api:v3:bundle` to regenerate the committed single-file bundle. `pnpm
    api:v3:lint` lints the source tree; `pnpm api:v3:check` (CI) fails when the
    bundle is stale.


    **Workflows Scope 1**

    Workflows are a follow-up extension of the existing v3 Survey API work. The
    Survey API establishes API-first survey authoring; Workflows build on that
    same v3 style to automate actions around survey responses through
    workspace-scoped JSON definitions. Scope 1 models the existing Follow-ups
    capability as workflows: one `response.completed` trigger with an optional
    `endingCardIds` filter, and `send_email` actions with Follow-up email field
    parity. The definition and run shapes mirror the shared Zod schemas in
    `packages/workflows` (ENG-1100), which are the implementation source of
    truth for this contract.


    Workflows use `draft`, `enabled`, and `disabled` lifecycle states plus an
    `archived` soft-delete state. Status only changes through the lifecycle
    endpoints (`enable`, `disable`, `archive`, `unarchive`); it is not writable
    via `POST` or `PATCH`. Enabling validates that the definition is executable
    and snapshots an immutable workflow version; runs reference that snapshot
    via `workflowVersionId`. `POST .../test` is a dry run: it validates the
    definition and resolves the trigger's references synchronously, creating no
    run and sending no email, and returns `{ workflowId, ok, problems }`
    directly. Real runs execute asynchronously and are polled via `GET
    /api/v3/workflows/runs/{runId}`.


    **Overview migration note**

    The v3-backed survey overview page intentionally removes actions that are
    not yet exposed by this contract: `Created by` filtering, `Duplicate`,
    `Copy...`, `Preview`, and `Copy link`.


    **Next steps (out of scope for this spec)**

    Additional v3 survey update endpoints, optional ETag/304, field selection,
    translation-specific writes, and survey version history.
  version: 0.1.0
  x-implementation-notes:
    route: apps/web/app/api/v3/surveys/route.ts
    generate-route: apps/web/app/api/v3/surveys/generate/route.ts
    query-parser: apps/web/app/api/v3/surveys/parse-v3-surveys-list-query.ts
    auth: apps/web/app/api/v3/lib/auth.ts
    workspace-resolution: apps/web/app/api/v3/lib/workspace-context.ts
    workflow-contract-ticket: ENG-1101
    workflow-shared-schemas: packages/workflows/src/types (feat/workflows-types)
    workflow-prisma-schema: packages/database/schema/workflows.prisma (feat/workflows-prisma-schema)
    openapi-generated: false
    spec-source: docs/api-v3-reference/src (regenerate bundle with `pnpm api:v3:bundle`)
    pagination-model: cursor
    cursor-pagination: supported
servers:
  - url: https://app.formbricks.com
    description: >-
      Formbricks Cloud. Paths in this spec already include the `/api/v3` prefix;
      self-hosted instances substitute their own origin.
security: []
paths:
  /api/v3/surveys:
    get:
      tags:
        - V3 Surveys
      summary: List surveys
      description: |
        Returns surveys for the workspace. Session cookie or x-api-key.
      operationId: getSurveysV3
      parameters:
        - in: query
          name: workspaceId
          required: true
          schema:
            type: string
            format: cuid2
          description: >
            Workspace identifier. This is the canonical container ID for v3
            APIs.
        - in: query
          name: limit
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Page size (max 100)
        - in: query
          name: cursor
          schema:
            type: string
          description: >
            Opaque cursor returned as `meta.nextCursor` from the previous page.
            Omit on the first request.
        - in: query
          name: includeTotalCount
          schema:
            type: boolean
            default: true
          description: >
            Whether to calculate `meta.totalCount` for this request. Set to
            `false` on cursor-pagination follow-up requests to skip the extra
            count query; in that case `meta.totalCount` is `null`.
        - in: query
          name: filter[name][contains]
          schema:
            type: string
            maxLength: 512
          description: >-
            Case-insensitive substring match on survey name (same as in-app list
            filters).
        - in: query
          name: filter[status][in]
          schema:
            type: array
            items:
              type: string
              enum:
                - draft
                - inProgress
                - paused
                - completed
                - archived
          style: form
          explode: true
          description: >
            Survey status filter. Repeat the parameter
            (`filter[status][in]=draft&filter[status][in]=inProgress`) or use
            comma-separated values (`filter[status][in]=draft,inProgress`).
            Invalid values → **400**.

            `archived` is a pseudo-status: it does not match a real survey
            status but includes archived (soft-deleted) surveys in the result
            set. Archived surveys are otherwise excluded from the default list.
        - in: query
          name: filter[type][in]
          schema:
            type: array
            items:
              type: string
              enum:
                - link
                - app
          style: form
          explode: true
          description: >-
            Survey type filter (`link` / `app`). Same repeat-or-comma rules as
            `filter[status][in]`.
        - in: query
          name: sortBy
          schema:
            type: string
            enum:
              - createdAt
              - updatedAt
              - name
              - relevance
          description: >-
            Sort order. Defaults to `updatedAt`. The `cursor` token is bound to
            the selected sort order.
      responses:
        '200':
          description: Surveys retrieved successfully
          headers:
            X-Request-Id:
              schema:
                type: string
              description: Request correlation ID
            Cache-Control:
              schema:
                type: string
              example: private, no-store
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SurveyListItem'
                  meta:
                    type: object
                    required:
                      - limit
                      - nextCursor
                      - totalCount
                      - workspaceSurveyCount
                    properties:
                      limit:
                        type: integer
                      nextCursor:
                        type:
                          - string
                          - 'null'
                        description: >-
                          Opaque cursor for the next page. `null` when there are
                          no more results.
                      totalCount:
                        type:
                          - integer
                          - 'null'
                        minimum: 0
                        description: >-
                          Total number of surveys matching the current filters
                          across all pages. `null` when
                          `includeTotalCount=false`.
                      workspaceSurveyCount:
                        type:
                          - integer
                          - 'null'
                        minimum: 0
                        description: >-
                          Total number of surveys in the workspace, archived
                          (soft-deleted) ones included and independent of the
                          current filters. `null` when `includeTotalCount=false`
                          (the same gate as `totalCount`). Replaces the boolean
                          `hasArchived` field of earlier releases.
        '400':
          $ref: '#/components/responses/V3BadRequest'
        '401':
          $ref: '#/components/responses/V3Unauthorized'
        '403':
          $ref: '#/components/responses/V3Forbidden'
        '429':
          $ref: '#/components/responses/V3TooManyRequests'
        '500':
          $ref: '#/components/responses/V3InternalServerError'
      security:
        - sessionAuth: []
        - apiKeyAuth: []
components:
  schemas:
    SurveyListItem:
      type: object
      description: >
        Shape returned by `GET /api/v3/surveys`. Serialized dates are ISO 8601
        strings.

        The v3 overview contract intentionally omits internal fields such as
        `_count`.

        Legacy DB rows may include survey **type** values `website` or `web`
        (see Prisma); filter **type** only accepts `link` | `app`.
      required:
        - id
        - name
        - workspaceId
        - type
        - status
        - publishOn
        - createdAt
        - updatedAt
        - archivedAt
        - responseCount
        - completedResponseCount
        - creator
      properties:
        id:
          type: string
        name:
          type: string
        workspaceId:
          type: string
        type:
          type: string
          enum:
            - link
            - app
            - website
            - web
        status:
          type: string
          enum:
            - draft
            - inProgress
            - paused
            - completed
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        archivedAt:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            Soft-delete/archive marker (ISO 8601); `null` when the survey is
            active.
        publishOn:
          type:
            - string
            - 'null'
          format: date-time
          description: Scheduled publish time (ISO 8601), or null if not scheduled.
        responseCount:
          type: integer
          description: Number of responses, including partial ones.
        completedResponseCount:
          type: integer
          description: Number of responses the respondent finished.
        creator:
          type:
            - object
            - 'null'
          description: >-
            The user who created the survey, or null for API-key/system-created
            surveys.
          required:
            - name
          properties:
            name:
              type: string
          additionalProperties: false
    Problem:
      type: object
      description: >-
        RFC 9457 Problem Details for HTTP APIs (`application/problem+json`).
        Responses typically include a machine-readable `code` field alongside
        `title`, `status`, `detail`, and `requestId`.
      required:
        - title
        - status
        - detail
        - requestId
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        instance:
          type: string
        code:
          type: string
          enum:
            - ai_features_not_enabled
            - ai_generated_payload_invalid
            - ai_instance_not_configured
            - ai_output_too_long
            - ai_smart_tools_disabled
            - bad_gateway
            - bad_request
            - conflict
            - forbidden
            - internal_server_error
            - invalid_workflow_state
            - not_authenticated
            - not_found
            - payload_too_large
            - service_unavailable
            - too_many_requests
            - unprocessable_content
            - workflow_not_executable
        requestId:
          type: string
        details:
          type: object
        invalid_params:
          type: array
          items:
            $ref: '#/components/schemas/InvalidParam'
    InvalidParam:
      type: object
      description: >
        Field-level validation error. `name` and `reason` are always intended
        for humans.

        Optional machine-readable fields are included when the API can identify
        the semantic error,

        especially for survey locale, identifier, and reference validation used
        by MCP and agent clients.
      required:
        - name
        - reason
      properties:
        name:
          type: string
          description: Dot-path to the invalid request field.
        reason:
          type: string
          description: Human-readable repair guidance.
        code:
          type: string
          enum:
            - dangling_reference
            - duplicate_identifier
            - duplicate_locale
            - forbidden_identifier
            - immutable_identifier
            - invalid_graph
            - invalid_locale
            - invalid_reference
            - missing_required_field
            - missing_translation
            - unsupported_field
            - unsupported_locale
          description: >
            Machine-readable validation category, when available.
            `invalid_graph` covers workflow

            graph-structure violations such as a duplicated trigger edge, an
            unreachable node, or a

            cycle.
        identifier:
          type: string
          description: >-
            Identifier involved in the validation failure, when available. For
            language errors, this can be the normalized language code.
        referenceType:
          type: string
          enum:
            - block
            - element
            - ending
            - hiddenField
            - language
            - variable
            - variableName
            - recall
            - node
            - edge
            - survey
            - endingCard
          description: >-
            Identifier namespace involved in the validation failure. `node`,
            `edge`, `survey`, and `endingCard` are used by workflow validation.
        missingId:
          type: string
          description: Referenced id that does not exist in the submitted survey document.
        firstUsedAt:
          type: string
          description: Dot-path where a duplicate identifier was first used.
        conflictsWith:
          type: string
          description: Dot-path to the existing identifier that conflicts with this field.
      additionalProperties: false
  responses:
    V3BadRequest:
      description: >-
        Bad Request — malformed JSON, invalid query/body/params, duplicate name,
        or unsupported field.
      headers:
        X-Request-Id:
          schema:
            type: string
          description: Request correlation ID
        Cache-Control:
          schema:
            type: string
          example: private, no-store
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    V3Unauthorized:
      description: Not authenticated (no valid session or API key).
      headers:
        X-Request-Id:
          schema:
            type: string
          description: Request correlation ID
        Cache-Control:
          schema:
            type: string
          example: private, no-store
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    V3Forbidden:
      description: >-
        Forbidden — no workspace access, or resource does not exist (404 not
        used; avoids existence leak).
      headers:
        X-Request-Id:
          schema:
            type: string
          description: Request correlation ID
        Cache-Control:
          schema:
            type: string
          example: private, no-store
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    V3TooManyRequests:
      description: Rate limit exceeded.
      headers:
        X-Request-Id:
          schema:
            type: string
          description: Request correlation ID
        Cache-Control:
          schema:
            type: string
          example: private, no-store
        Retry-After:
          schema:
            type: integer
          description: Seconds until the current rate-limit window resets.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    V3InternalServerError:
      description: Internal Server Error.
      headers:
        X-Request-Id:
          schema:
            type: string
          description: Request correlation ID
        Cache-Control:
          schema:
            type: string
          example: private, no-store
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  securitySchemes:
    sessionAuth:
      type: apiKey
      in: cookie
      name: next-auth.session-token
      description: >
        NextAuth session JWT cookie. **Development:** often
        `next-auth.session-token`.

        **Production (HTTPS):** often `__Secure-next-auth.session-token`. Send
        the cookie your browser receives after sign-in.
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >
        Management API key; must include **workspaceId** as an allowed workspace
        with read, write, or manage permission.

````