Files
formbricks/docs/api-v3-reference/openapi.yml
T
2026-04-20 11:40:33 +02:00

330 lines
13 KiB
YAML

# V3 API — Surveys (hand-maintained; not generated by generate-api-specs).
# Implementation: apps/web/app/api/v3/surveys/route.ts and apps/web/app/api/v3/surveys/[surveyId]/route.ts
# See apps/web/app/api/v3/README.md and docs/Survey-Server-Actions.md (Part III) for full context.
openapi: 3.1.0
info:
title: Formbricks API v3
description: |
**GET /api/v3/surveys** and **DELETE /api/v3/surveys/{surveyId}** — authenticate with **session cookie** or **`x-api-key`** (management key with access to the workspace).
**Spec location:** `docs/api-v3-reference/openapi.yml` (alongside v2 at `docs/api-v2-reference/openapi.yml`).
**workspaceId**
Query param `workspaceId` is the canonical container identifier for this API.
**Deprecated compatibility:** the older `environmentId` identifier is still accepted
for compatibility but should no longer be used in new integrations.
**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. Responses also include `meta.totalCount`, the total number of surveys matching the current filters across all pages. There is no `offset` in this contract.
**Filtering**
Filters use explicit operator-style query parameters under the **`filter[...]` family**. This endpoint supports `filter[name][contains]`, `filter[status][in]`, and `filter[type][in]`. 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. List responses use `private, no-store`.
**OpenAPI**
This YAML is **not** produced by `pnpm generate-api-specs` (that script only builds v2 → `docs/api-v2-reference/openapi.yml`). Update this file when the route contract changes.
**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 endpoints, optional ETag/304, field selection — see Survey-Server-Actions.md Part III.
version: 0.1.0
x-implementation-notes:
route: apps/web/app/api/v3/surveys/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
openapi-generated: false
pagination-model: cursor
cursor-pagination: supported
paths:
/api/v3/surveys:
get:
operationId: getSurveysV3
summary: List surveys
description: |
Returns surveys for the workspace. Session cookie or x-api-key.
Note: Environments are deprecated. Use workspace/workspaceId terminology.
tags:
- V3 Surveys
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: environmentId
required: false
deprecated: true
schema:
type: string
format: cuid2
description: |
Deprecated: use `workspaceId`. This alias is retained for backward compatibility.
- 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: 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]
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**.
- 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]
properties:
limit: { type: integer }
nextCursor:
type: string
nullable: true
description: Opaque cursor for the next page. `null` when there are no more results.
totalCount:
type: integer
minimum: 0
description: Total number of surveys matching the current filters across all pages.
"400":
description: Bad Request
content:
application/problem+json:
schema:
$ref: "#/components/schemas/Problem"
"401":
description: Not authenticated (no valid session or API key)
content:
application/problem+json:
schema:
$ref: "#/components/schemas/Problem"
"403":
description: Forbidden — no access, or workspace does not exist (404 not used; avoids existence leak)
content:
application/problem+json:
schema:
$ref: "#/components/schemas/Problem"
"429":
description: Rate limit exceeded
headers:
Retry-After:
schema: { type: integer }
description: Seconds until the current rate-limit window resets
content:
application/problem+json:
schema:
$ref: "#/components/schemas/Problem"
"500":
description: Internal Server Error
content:
application/problem+json:
schema:
$ref: "#/components/schemas/Problem"
security:
- sessionAuth: []
- apiKeyAuth: []
/api/v3/surveys/{surveyId}:
delete:
operationId: deleteSurveyV3
summary: Delete a survey
description: Deletes a survey by id. Session cookie or x-api-key.
tags:
- V3 Surveys
parameters:
- in: path
name: surveyId
required: true
schema:
type: string
format: cuid2
description: Survey identifier.
responses:
"200":
description: Survey deleted 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:
$ref: "#/components/schemas/SurveyDeleteResponse"
"400":
description: Bad Request
content:
application/problem+json:
schema:
$ref: "#/components/schemas/Problem"
"401":
description: Not authenticated (no valid session or API key)
content:
application/problem+json:
schema:
$ref: "#/components/schemas/Problem"
"403":
description: Forbidden — no access, or survey does not exist (404 not used; avoids existence leak)
content:
application/problem+json:
schema:
$ref: "#/components/schemas/Problem"
"429":
description: Rate limit exceeded
headers:
Retry-After:
schema: { type: integer }
description: Seconds until the current rate-limit window resets
content:
application/problem+json:
schema:
$ref: "#/components/schemas/Problem"
"500":
description: Internal Server Error
content:
application/problem+json:
schema:
$ref: "#/components/schemas/Problem"
security:
- sessionAuth: []
- apiKeyAuth: []
components:
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.
schemas:
SurveyListItem:
type: object
description: |
Shape returned by `GET /api/v3/surveys`. Serialized dates are ISO 8601 strings.
The v3 overview contract intentionally omits deprecated `environmentId` and internal fields such as `_count`.
Legacy DB rows may include survey **type** values `website` or `web` (see Prisma); filter **type** only accepts `link` | `app`.
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 }
responseCount: { type: integer }
creator: { type: object, nullable: true, properties: { name: { type: string } } }
singleUse:
type: object
nullable: true
properties:
enabled: { type: boolean }
isEncrypted: { type: boolean }
SurveyDeleteResponse:
type: object
required: [data]
properties:
data:
type: object
required: [id]
properties:
id: { type: string }
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: [bad_request, not_authenticated, forbidden, internal_server_error, too_many_requests]
requestId: { type: string }
details: { type: object }
invalid_params:
type: array
items:
type: object
properties:
name: { type: string }
reason: { type: string }