mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-18 06:52:01 -05:00
330 lines
13 KiB
YAML
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 }
|