Skip to content
Dashboard

Feedback

FeedbackBoards

Boards (post categories) organize feedback into distinct containers with their own settings.

List all boards
GET/v2/boards
Get a board by ID
GET/v2/boards/{id}
ModelsExpand Collapse
Board object { id, access, createdAt, 7 more }
id: string

Unique identifier

access: object { adminOnly, allowedRoles, deniedRoles, segments }
adminOnly: boolean

If true, only admins can see this board

allowedRoles: array of string

Role IDs allowed access (empty = all)

deniedRoles: array of string

Role IDs explicitly denied access

segments: array of string

Segment IDs that can access (empty = all)

createdAt: string

ISO 8601 timestamp when created

customFields: array of string

Custom field IDs attached to this board

features: object { commentsEnabled, createdDatesVisible, postingEnabled }
commentsEnabled: boolean

Whether users can comment on posts

createdDatesVisible: boolean

Whether creation dates are visible on posts

postingEnabled: boolean

Whether users can create new posts

icon: object { type, value } or object { type, value } or object { type, value }

The board’s icon. Can be one of three types:

  • emoji: A single emoji character (e.g., 💡, 🚀, ⭐)
  • icon: A predefined icon from the built-in library (e.g., lightbulb, bug, star)
  • url: A custom image URL (HTTPS required)

Can be null if no icon is set.

One of the following:
EmojiIcon object { type, value }

An emoji character as the board icon

type: "emoji"

Emoji icon type

value: string

A single emoji character

PredefinedIcon object { type, value }

A predefined icon from the built-in icon library

type: "icon"

Predefined icon type

value: string

Icon name from the predefined icon set (e.g., lightbulb, bug, star, rocket, flag, heart, check, question, megaphone, gift)

URLIcon object { type, value }

A custom icon loaded from an external URL

type: "url"

External URL icon type

value: string

HTTPS URL to a custom icon image (PNG, SVG, or WebP recommended)

formaturi
localization: object { description, formPlaceholder, heroDescription, 3 more }
description: map[string]

Localized description

formPlaceholder: map[string]

Placeholder text in the post creation form

heroDescription: map[string]

Hero description/subtitle

heroTitle: map[string]

Hero title shown on the board page

name: map[string]

Localized board name (language code → text)

submitButtonText: map[string]

Submit button text

name: string

Display name in organization’s default locale

object: "board"

Object type identifier

postDefaults: object { visibility }
visibility: "public" or "authorOnly" or "companyOnly"

Default visibility for new posts

One of the following:
"public"
"authorOnly"
"companyOnly"
BoardListResponse = array of Board { id, access, createdAt, 7 more }
id: string

Unique identifier

access: object { adminOnly, allowedRoles, deniedRoles, segments }
adminOnly: boolean

If true, only admins can see this board

allowedRoles: array of string

Role IDs allowed access (empty = all)

deniedRoles: array of string

Role IDs explicitly denied access

segments: array of string

Segment IDs that can access (empty = all)

createdAt: string

ISO 8601 timestamp when created

customFields: array of string

Custom field IDs attached to this board

features: object { commentsEnabled, createdDatesVisible, postingEnabled }
commentsEnabled: boolean

Whether users can comment on posts

createdDatesVisible: boolean

Whether creation dates are visible on posts

postingEnabled: boolean

Whether users can create new posts

icon: object { type, value } or object { type, value } or object { type, value }

The board’s icon. Can be one of three types:

  • emoji: A single emoji character (e.g., 💡, 🚀, ⭐)
  • icon: A predefined icon from the built-in library (e.g., lightbulb, bug, star)
  • url: A custom image URL (HTTPS required)

Can be null if no icon is set.

One of the following:
EmojiIcon object { type, value }

An emoji character as the board icon

type: "emoji"

Emoji icon type

value: string

A single emoji character

PredefinedIcon object { type, value }

A predefined icon from the built-in icon library

type: "icon"

Predefined icon type

value: string

Icon name from the predefined icon set (e.g., lightbulb, bug, star, rocket, flag, heart, check, question, megaphone, gift)

URLIcon object { type, value }

A custom icon loaded from an external URL

type: "url"

External URL icon type

value: string

HTTPS URL to a custom icon image (PNG, SVG, or WebP recommended)

formaturi
localization: object { description, formPlaceholder, heroDescription, 3 more }
description: map[string]

Localized description

formPlaceholder: map[string]

Placeholder text in the post creation form

heroDescription: map[string]

Hero description/subtitle

heroTitle: map[string]

Hero title shown on the board page

name: map[string]

Localized board name (language code → text)

submitButtonText: map[string]

Submit button text

name: string

Display name in organization’s default locale

object: "board"

Object type identifier

postDefaults: object { visibility }
visibility: "public" or "authorOnly" or "companyOnly"

Default visibility for new posts

One of the following:
"public"
"authorOnly"
"companyOnly"

FeedbackPosts

User-submitted feedback and feature requests. Posts belong to boards and can be upvoted, commented on, and tracked through statuses.

List all posts
GET/v2/posts
Create a new post
POST/v2/posts
Get a post by ID
GET/v2/posts/{id}
Update a post
PATCH/v2/posts/{id}
Delete a post
DELETE/v2/posts/{id}
Merge two posts
POST/v2/posts/merge
Unmerge a post
POST/v2/posts/unmerge
Search posts
POST/v2/posts/search
ModelsExpand Collapse
Post object { id, access, anchor, 30 more }
id: string

Unique identifier

access: PostAccess { companyExternalIds, userIds }
companyExternalIds: array of string

External company IDs explicitly granted access to this post. Empty array means no company-level restrictions. Non-empty means only users belonging to these companies can see the post.

userIds: array of string

User IDs explicitly granted access to this post. Empty array means no user-level restrictions (post uses board/org visibility). Non-empty means only these users (plus admins) can see the post.

anchor: object { commentId, conversationId, messageId, 3 more }

When kind is ‘insight’, where exactly the insight points back into its origin: an insight source record with character ranges into its fullText, or the native conversation/message/comment/post ids.

commentId: optional string
conversationId: optional string
messageId: optional string
ranges: optional array of object { end, start }
end: number
start: number
sourceRecordId: optional string
submissionId: optional string
assigneeId: string

ID of the admin assigned to this post, null if unassigned

author: PostAuthor { id, email, name, 2 more }
id: string

Author unique identifier

email: string

Author email (if available)

name: string

Author display name

profilePicture: string

Author profile picture URL

type: "admin" or "customer" or "guest" or 3 more

Type of user who authored the post

One of the following:
"admin"
"customer"
"guest"
"integration"
"bot"
"lead"
boardId: string

Board (category) ID this post belongs to

commentCount: number

Total number of comments

content: string

Post content in HTML format

createdAt: string

ISO 8601 timestamp when created

customFields: map[unknown]

Custom field values keyed by field ID

eta: string

Estimated completion time as ISO 8601 timestamp, null if not set

features: PostFeatures { commentsEnabled }
commentsEnabled: boolean

Whether comments are allowed on this post

groupKey: string

When kind is ‘insight’, the triage grouping key (source record id, conversation id, origin post id, or the insight’s own id for singletons). Legacy insights may be null and group as singletons.

inReview: boolean

Whether the post is pending moderation review

insightSource: object { channel, capturedBy, conversationId, 3 more }

Provenance of an insight: which channel it came from and how it was captured.

channel: "slack" or "discord" or "email" or 5 more
One of the following:
"slack"
"discord"
"email"
"support"
"widget"
"call"
"api"
"feedback"
capturedBy: optional "manual" or "auto" or "ai" or "api"
One of the following:
"manual"
"auto"
"ai"
"api"
conversationId: optional string
externalId: optional string
sourceLabel: optional string
url: optional string
integrations: object { clickup, devops, github, 4 more }

Third-party integration links associated with this post

clickup: array of object { id, title, url }
id: string

ClickUp task ID

title: string

ClickUp task title

url: string

URL to the ClickUp task

devops: array of object { id, projectId, projectName, 2 more }
id: number

Azure DevOps work item ID

projectId: string

Azure DevOps project ID

projectName: string

Azure DevOps project name

title: string

Work item title

url: string

URL to the work item

github: array of object { id, number, repositoryFullName, 3 more }
id: string

GitHub issue ID

number: string

GitHub issue number

repositoryFullName: string

Full repository name (owner/repo)

repositoryName: string

Repository name

title: string

GitHub issue title

url: string

URL to the GitHub issue

hubspot: array of object { dealAmount, dealClosed, objectId, type }
dealAmount: number

Deal amount (for DEAL type)

dealClosed: boolean

Whether the deal is closed (for DEAL type)

objectId: number

HubSpot object ID

type: "TICKET" or "DEAL" or "CONTACT"

HubSpot object type

One of the following:
"TICKET"
"DEAL"
"CONTACT"
jira: array of object { issueId, issueUrl }
issueId: string

Jira issue ID

issueUrl: string

URL to the Jira issue

linear: array of object { issueId, issueUrl }
issueId: string

Linear issue ID

issueUrl: string

URL to the Linear issue

salesforce: array of object { amount, isClosed, objectId, objectType }
amount: number

Opportunity amount (for Opportunity type)

isClosed: boolean

Whether the opportunity is closed (for Opportunity type)

objectId: string

Salesforce record ID

objectType: "Opportunity" or "Case"

Salesforce object type

One of the following:
"Opportunity"
"Case"
isPinned: boolean

Whether the post is pinned to the top

kind: "issue" or "insight" or "record"

Discriminates an actionable work item (‘issue’) from a customer submission whose claims were extracted into insights (‘record’ — not a work item). Defaults to ‘issue’ for all pre-existing posts. Default list responses return issues only; pass kind=‘record’ to opt in. Raw signal (‘insight’) is never returned by the posts resource — insights are served by /v2/insights.

One of the following:
"issue"
"insight"
"record"
linkedInsightCount: number

Number of insights linked to this issue as supporting evidence. Only meaningful when kind is ‘issue’.

linkedIssueId: string

When kind is ‘insight’, the ID of the issue this insight supports. Null when the insight is unlinked or when kind is ‘issue’.

object: "post"

Object type identifier

opportunityAmount: number

Total opportunity amount from linked HubSpot deals and Salesforce opportunities

portalHidden: boolean

True when the issue is hidden from portal/public surfaces. Missing stored values are returned as false.

postUrl: string

Full URL to view the post

slug: string

URL-friendly slug

status: PostStatus { id, color, isDefault, 3 more }
id: string

Unique identifier

color: string

Color for UI display

isDefault: boolean

Whether this is the default status for new posts

name: string

Display name

object: "post_status"

Object type identifier

type: "reviewing" or "unstarted" or "active" or 2 more

The workflow stage this status represents

One of the following:
"reviewing"
"unstarted"
"active"
"completed"
"canceled"
tags: array of PostTag { id, color, name }

Tags attached to this post

id: string

Tag unique identifier

color: string

Tag color hex code

name: string

Tag name

title: string

Post title

updatedAt: string

ISO 8601 timestamp when last modified

upvotes: number

Total number of upvotes

deduped: optional true

Present and true only on POST /v2/posts, when the request carried a source.externalId that already had a post. The existing post is returned unchanged with HTTP 200; a newly created post returns HTTP 201 without this field.

intakeMode: optional "request" or "feedback"

Present only on POST /v2/posts: the intakeMode the post was processed under (‘request’ when the request named none). On an idempotent replay (deduped: true) this is the mode the post was ORIGINALLY created with.

One of the following:
"request"
"feedback"
processing: optional PostProcessing { status, reason, results }

On POST /v2/posts — queued: a processing run (claim extraction or the Organize rewrite) was enqueued and its result lands asynchronously on the post. skipped: nothing was enqueued; reason says which gate decided (‘request_mode’ for every intakeMode: ‘request’ create). existing: the create was an idempotent replay and the post was not processed again. On GET /v2/posts/{id} this field is present only for posts created with intakeMode: 'feedback' and reports how far that processing has got (‘queued’, ‘processing’, ‘complete’, ‘needs_review’, or ‘skipped’ with the same reason the create returned), with results listing what was made of the submission once the run has finished.

status: "queued" or "processing" or "complete" or 3 more

‘queued’ = an AI run was enqueued and has not landed yet; ‘processing’ = the run is working on the submission; ‘complete’ = it finished and results lists what it produced; ‘needs_review’ = it finished and a teammate still has to decide something; ‘skipped’ = nothing was enqueued (see reason); ‘existing’ = idempotent replay, not processed again.

One of the following:
"queued"
"processing"
"complete"
"needs_review"
"skipped"
"existing"
reason: optional "request_mode" or "autopilot_off" or "insights_disabled" or 11 more

Why processing was skipped. ‘request_mode’ = intakeMode was ‘request’ (the default); ‘autopilot_off’ = the workspace’s Autopilot dial is off or its plan does not include it; ‘pipeline_paused’ = the platform paused AI processing (it resumes automatically); ‘support_board’ / ‘spam_held’ / ‘staff_authored’ / ‘no_content’ = the post did not qualify; ‘source_too_long’ = the text was over the length limit when the run read it (a create that long is normally refused with a 400 first); ‘extraction_failed’ = the AI could not read the text after repeated attempts; nothing was written, and opening the post in Triage runs the analysis again.

One of the following:
"request_mode"
"autopilot_off"
"insights_disabled"
"pipeline_paused"
"support_board"
"spam_held"
"staff_authored"
"not_a_post"
"no_board"
"no_id"
"no_content"
"source_too_long"
"extraction_failed"
"hook_error"
results: optional array of object { outcome, title, postId, 2 more }

What the workspace’s “Organize submissions” lane made of this submission, one entry per ask it found, in the order they appear in the text. Present on GET /v2/posts/{id} for posts created with intakeMode: 'feedback' once the run has finished. Absent while the run is still working, and absent for workspaces that do not organize submissions — there the post itself is the request.

outcome: "attached" or "request" or "question" or "held"

‘request’ = the ask became a request of its own; ‘attached’ = it was added as evidence to a request that already existed; ‘held’ = it became a request that is waiting for a teammate to publish it; ‘question’ = it asked something and no request was created, so a teammate answers it.

One of the following:
"attached"
"request"
"question"
"held"
title: string

The ask, as it was written up.

postId: optional string

The request this ask became, or was attached to. Absent for a question, and absent when the request is not readable through this API (for example a ticket board).

postTitle: optional string

Title of postId.

reason: optional "too_many_topics" or "uncertain" or "custom_fields" or "processing_failed"

Why a ‘held’ result is waiting for a teammate. Only on a ‘held’ result, and only one of these codes: ‘too_many_topics’ = the submission held more separate asks than one submission can become requests, so this one request lists the asks that did not fit, one line each in the customer’s words; ‘uncertain’ = the ask was too vague to file without a person; ‘custom_fields’ = the board requires custom fields this submission did not carry; ‘processing_failed’ = the AI could not rewrite the text (the request then holds the original words) or could not check this ask against existing requests, so a teammate confirms it is not a duplicate.

One of the following:
"too_many_topics"
"uncertain"
"custom_fields"
"processing_failed"
PostAccess object { companyExternalIds, userIds }
companyExternalIds: array of string

External company IDs explicitly granted access to this post. Empty array means no company-level restrictions. Non-empty means only users belonging to these companies can see the post.

userIds: array of string

User IDs explicitly granted access to this post. Empty array means no user-level restrictions (post uses board/org visibility). Non-empty means only these users (plus admins) can see the post.

PostAuthor object { id, email, name, 2 more }
id: string

Author unique identifier

email: string

Author email (if available)

name: string

Author display name

profilePicture: string

Author profile picture URL

type: "admin" or "customer" or "guest" or 3 more

Type of user who authored the post

One of the following:
"admin"
"customer"
"guest"
"integration"
"bot"
"lead"
PostFeatures object { commentsEnabled }
commentsEnabled: boolean

Whether comments are allowed on this post

PostList object { data, nextCursor, object, pagination }
data: array of Post { id, access, anchor, 30 more }

Array of posts

id: string

Unique identifier

access: PostAccess { companyExternalIds, userIds }
companyExternalIds: array of string

External company IDs explicitly granted access to this post. Empty array means no company-level restrictions. Non-empty means only users belonging to these companies can see the post.

userIds: array of string

User IDs explicitly granted access to this post. Empty array means no user-level restrictions (post uses board/org visibility). Non-empty means only these users (plus admins) can see the post.

anchor: object { commentId, conversationId, messageId, 3 more }

When kind is ‘insight’, where exactly the insight points back into its origin: an insight source record with character ranges into its fullText, or the native conversation/message/comment/post ids.

commentId: optional string
conversationId: optional string
messageId: optional string
ranges: optional array of object { end, start }
end: number
start: number
sourceRecordId: optional string
submissionId: optional string
assigneeId: string

ID of the admin assigned to this post, null if unassigned

author: PostAuthor { id, email, name, 2 more }
id: string

Author unique identifier

email: string

Author email (if available)

name: string

Author display name

profilePicture: string

Author profile picture URL

type: "admin" or "customer" or "guest" or 3 more

Type of user who authored the post

One of the following:
"admin"
"customer"
"guest"
"integration"
"bot"
"lead"
boardId: string

Board (category) ID this post belongs to

commentCount: number

Total number of comments

content: string

Post content in HTML format

createdAt: string

ISO 8601 timestamp when created

customFields: map[unknown]

Custom field values keyed by field ID

eta: string

Estimated completion time as ISO 8601 timestamp, null if not set

features: PostFeatures { commentsEnabled }
commentsEnabled: boolean

Whether comments are allowed on this post

groupKey: string

When kind is ‘insight’, the triage grouping key (source record id, conversation id, origin post id, or the insight’s own id for singletons). Legacy insights may be null and group as singletons.

inReview: boolean

Whether the post is pending moderation review

insightSource: object { channel, capturedBy, conversationId, 3 more }

Provenance of an insight: which channel it came from and how it was captured.

channel: "slack" or "discord" or "email" or 5 more
One of the following:
"slack"
"discord"
"email"
"support"
"widget"
"call"
"api"
"feedback"
capturedBy: optional "manual" or "auto" or "ai" or "api"
One of the following:
"manual"
"auto"
"ai"
"api"
conversationId: optional string
externalId: optional string
sourceLabel: optional string
url: optional string
integrations: object { clickup, devops, github, 4 more }

Third-party integration links associated with this post

clickup: array of object { id, title, url }
id: string

ClickUp task ID

title: string

ClickUp task title

url: string

URL to the ClickUp task

devops: array of object { id, projectId, projectName, 2 more }
id: number

Azure DevOps work item ID

projectId: string

Azure DevOps project ID

projectName: string

Azure DevOps project name

title: string

Work item title

url: string

URL to the work item

github: array of object { id, number, repositoryFullName, 3 more }
id: string

GitHub issue ID

number: string

GitHub issue number

repositoryFullName: string

Full repository name (owner/repo)

repositoryName: string

Repository name

title: string

GitHub issue title

url: string

URL to the GitHub issue

hubspot: array of object { dealAmount, dealClosed, objectId, type }
dealAmount: number

Deal amount (for DEAL type)

dealClosed: boolean

Whether the deal is closed (for DEAL type)

objectId: number

HubSpot object ID

type: "TICKET" or "DEAL" or "CONTACT"

HubSpot object type

One of the following:
"TICKET"
"DEAL"
"CONTACT"
jira: array of object { issueId, issueUrl }
issueId: string

Jira issue ID

issueUrl: string

URL to the Jira issue

linear: array of object { issueId, issueUrl }
issueId: string

Linear issue ID

issueUrl: string

URL to the Linear issue

salesforce: array of object { amount, isClosed, objectId, objectType }
amount: number

Opportunity amount (for Opportunity type)

isClosed: boolean

Whether the opportunity is closed (for Opportunity type)

objectId: string

Salesforce record ID

objectType: "Opportunity" or "Case"

Salesforce object type

One of the following:
"Opportunity"
"Case"
isPinned: boolean

Whether the post is pinned to the top

kind: "issue" or "insight" or "record"

Discriminates an actionable work item (‘issue’) from a customer submission whose claims were extracted into insights (‘record’ — not a work item). Defaults to ‘issue’ for all pre-existing posts. Default list responses return issues only; pass kind=‘record’ to opt in. Raw signal (‘insight’) is never returned by the posts resource — insights are served by /v2/insights.

One of the following:
"issue"
"insight"
"record"
linkedInsightCount: number

Number of insights linked to this issue as supporting evidence. Only meaningful when kind is ‘issue’.

linkedIssueId: string

When kind is ‘insight’, the ID of the issue this insight supports. Null when the insight is unlinked or when kind is ‘issue’.

object: "post"

Object type identifier

opportunityAmount: number

Total opportunity amount from linked HubSpot deals and Salesforce opportunities

portalHidden: boolean

True when the issue is hidden from portal/public surfaces. Missing stored values are returned as false.

postUrl: string

Full URL to view the post

slug: string

URL-friendly slug

status: PostStatus { id, color, isDefault, 3 more }
id: string

Unique identifier

color: string

Color for UI display

isDefault: boolean

Whether this is the default status for new posts

name: string

Display name

object: "post_status"

Object type identifier

type: "reviewing" or "unstarted" or "active" or 2 more

The workflow stage this status represents

One of the following:
"reviewing"
"unstarted"
"active"
"completed"
"canceled"
tags: array of PostTag { id, color, name }

Tags attached to this post

id: string

Tag unique identifier

color: string

Tag color hex code

name: string

Tag name

title: string

Post title

updatedAt: string

ISO 8601 timestamp when last modified

upvotes: number

Total number of upvotes

deduped: optional true

Present and true only on POST /v2/posts, when the request carried a source.externalId that already had a post. The existing post is returned unchanged with HTTP 200; a newly created post returns HTTP 201 without this field.

intakeMode: optional "request" or "feedback"

Present only on POST /v2/posts: the intakeMode the post was processed under (‘request’ when the request named none). On an idempotent replay (deduped: true) this is the mode the post was ORIGINALLY created with.

One of the following:
"request"
"feedback"
processing: optional PostProcessing { status, reason, results }

On POST /v2/posts — queued: a processing run (claim extraction or the Organize rewrite) was enqueued and its result lands asynchronously on the post. skipped: nothing was enqueued; reason says which gate decided (‘request_mode’ for every intakeMode: ‘request’ create). existing: the create was an idempotent replay and the post was not processed again. On GET /v2/posts/{id} this field is present only for posts created with intakeMode: 'feedback' and reports how far that processing has got (‘queued’, ‘processing’, ‘complete’, ‘needs_review’, or ‘skipped’ with the same reason the create returned), with results listing what was made of the submission once the run has finished.

status: "queued" or "processing" or "complete" or 3 more

‘queued’ = an AI run was enqueued and has not landed yet; ‘processing’ = the run is working on the submission; ‘complete’ = it finished and results lists what it produced; ‘needs_review’ = it finished and a teammate still has to decide something; ‘skipped’ = nothing was enqueued (see reason); ‘existing’ = idempotent replay, not processed again.

One of the following:
"queued"
"processing"
"complete"
"needs_review"
"skipped"
"existing"
reason: optional "request_mode" or "autopilot_off" or "insights_disabled" or 11 more

Why processing was skipped. ‘request_mode’ = intakeMode was ‘request’ (the default); ‘autopilot_off’ = the workspace’s Autopilot dial is off or its plan does not include it; ‘pipeline_paused’ = the platform paused AI processing (it resumes automatically); ‘support_board’ / ‘spam_held’ / ‘staff_authored’ / ‘no_content’ = the post did not qualify; ‘source_too_long’ = the text was over the length limit when the run read it (a create that long is normally refused with a 400 first); ‘extraction_failed’ = the AI could not read the text after repeated attempts; nothing was written, and opening the post in Triage runs the analysis again.

One of the following:
"request_mode"
"autopilot_off"
"insights_disabled"
"pipeline_paused"
"support_board"
"spam_held"
"staff_authored"
"not_a_post"
"no_board"
"no_id"
"no_content"
"source_too_long"
"extraction_failed"
"hook_error"
results: optional array of object { outcome, title, postId, 2 more }

What the workspace’s “Organize submissions” lane made of this submission, one entry per ask it found, in the order they appear in the text. Present on GET /v2/posts/{id} for posts created with intakeMode: 'feedback' once the run has finished. Absent while the run is still working, and absent for workspaces that do not organize submissions — there the post itself is the request.

outcome: "attached" or "request" or "question" or "held"

‘request’ = the ask became a request of its own; ‘attached’ = it was added as evidence to a request that already existed; ‘held’ = it became a request that is waiting for a teammate to publish it; ‘question’ = it asked something and no request was created, so a teammate answers it.

One of the following:
"attached"
"request"
"question"
"held"
title: string

The ask, as it was written up.

postId: optional string

The request this ask became, or was attached to. Absent for a question, and absent when the request is not readable through this API (for example a ticket board).

postTitle: optional string

Title of postId.

reason: optional "too_many_topics" or "uncertain" or "custom_fields" or "processing_failed"

Why a ‘held’ result is waiting for a teammate. Only on a ‘held’ result, and only one of these codes: ‘too_many_topics’ = the submission held more separate asks than one submission can become requests, so this one request lists the asks that did not fit, one line each in the customer’s words; ‘uncertain’ = the ask was too vague to file without a person; ‘custom_fields’ = the board requires custom fields this submission did not carry; ‘processing_failed’ = the AI could not rewrite the text (the request then holds the original words) or could not check this ask against existing requests, so a teammate confirms it is not a duplicate.

One of the following:
"too_many_topics"
"uncertain"
"custom_fields"
"processing_failed"
nextCursor: string

Cursor for fetching the next page (cursor-based pagination)

maxLength512
object: "list"

Object type identifier

PostProcessing object { status, reason, results }

On POST /v2/posts — queued: a processing run (claim extraction or the Organize rewrite) was enqueued and its result lands asynchronously on the post. skipped: nothing was enqueued; reason says which gate decided (‘request_mode’ for every intakeMode: ‘request’ create). existing: the create was an idempotent replay and the post was not processed again. On GET /v2/posts/{id} this field is present only for posts created with intakeMode: 'feedback' and reports how far that processing has got (‘queued’, ‘processing’, ‘complete’, ‘needs_review’, or ‘skipped’ with the same reason the create returned), with results listing what was made of the submission once the run has finished.

status: "queued" or "processing" or "complete" or 3 more

‘queued’ = an AI run was enqueued and has not landed yet; ‘processing’ = the run is working on the submission; ‘complete’ = it finished and results lists what it produced; ‘needs_review’ = it finished and a teammate still has to decide something; ‘skipped’ = nothing was enqueued (see reason); ‘existing’ = idempotent replay, not processed again.

One of the following:
"queued"
"processing"
"complete"
"needs_review"
"skipped"
"existing"
reason: optional "request_mode" or "autopilot_off" or "insights_disabled" or 11 more

Why processing was skipped. ‘request_mode’ = intakeMode was ‘request’ (the default); ‘autopilot_off’ = the workspace’s Autopilot dial is off or its plan does not include it; ‘pipeline_paused’ = the platform paused AI processing (it resumes automatically); ‘support_board’ / ‘spam_held’ / ‘staff_authored’ / ‘no_content’ = the post did not qualify; ‘source_too_long’ = the text was over the length limit when the run read it (a create that long is normally refused with a 400 first); ‘extraction_failed’ = the AI could not read the text after repeated attempts; nothing was written, and opening the post in Triage runs the analysis again.

One of the following:
"request_mode"
"autopilot_off"
"insights_disabled"
"pipeline_paused"
"support_board"
"spam_held"
"staff_authored"
"not_a_post"
"no_board"
"no_id"
"no_content"
"source_too_long"
"extraction_failed"
"hook_error"
results: optional array of object { outcome, title, postId, 2 more }

What the workspace’s “Organize submissions” lane made of this submission, one entry per ask it found, in the order they appear in the text. Present on GET /v2/posts/{id} for posts created with intakeMode: 'feedback' once the run has finished. Absent while the run is still working, and absent for workspaces that do not organize submissions — there the post itself is the request.

outcome: "attached" or "request" or "question" or "held"

‘request’ = the ask became a request of its own; ‘attached’ = it was added as evidence to a request that already existed; ‘held’ = it became a request that is waiting for a teammate to publish it; ‘question’ = it asked something and no request was created, so a teammate answers it.

One of the following:
"attached"
"request"
"question"
"held"
title: string

The ask, as it was written up.

postId: optional string

The request this ask became, or was attached to. Absent for a question, and absent when the request is not readable through this API (for example a ticket board).

postTitle: optional string

Title of postId.

reason: optional "too_many_topics" or "uncertain" or "custom_fields" or "processing_failed"

Why a ‘held’ result is waiting for a teammate. Only on a ‘held’ result, and only one of these codes: ‘too_many_topics’ = the submission held more separate asks than one submission can become requests, so this one request lists the asks that did not fit, one line each in the customer’s words; ‘uncertain’ = the ask was too vague to file without a person; ‘custom_fields’ = the board requires custom fields this submission did not carry; ‘processing_failed’ = the AI could not rewrite the text (the request then holds the original words) or could not check this ask against existing requests, so a teammate confirms it is not a duplicate.

One of the following:
"too_many_topics"
"uncertain"
"custom_fields"
"processing_failed"
PostTag object { id, color, name }
id: string

Tag unique identifier

color: string

Tag color hex code

name: string

Tag name

SearchFilter object { field, operator, value }
field: string

Field name to filter on (e.g. state, tag_ids, created_at)

minLength1
maxLength64
operator: "=" or "!=" or "IN" or 9 more

Comparison operator

One of the following:
"="
"!="
"IN"
"NIN"
">"
"<"
">="
"<="
"~"
"!~"
"^"
"$"
value: string or number or boolean or array of string or number

Value to compare against (primitive or array of primitives)

One of the following:
string
number
boolean
array of string or number
One of the following:
string
number
SearchFilterGroup object { operator, value }
operator: "AND" or "OR"

Group operator: AND (all match) or OR (any match)

One of the following:
"AND"
"OR"
value: array of SearchFilter { field, operator, value }

Array of filter clauses (1-15 entries)

field: string

Field name to filter on (e.g. state, tag_ids, created_at)

minLength1
maxLength64
operator: "=" or "!=" or "IN" or 9 more

Comparison operator

One of the following:
"="
"!="
"IN"
"NIN"
">"
"<"
">="
"<="
"~"
"!~"
"^"
"$"
value: string or number or boolean or array of string or number

Value to compare against (primitive or array of primitives)

One of the following:
string
number
boolean
array of string or number
One of the following:
string
number
PostDeleteResponse object { id, deleted, object }
id: string

Unique identifier of the deleted post

deleted: true

Indicates the resource was deleted

object: "post"

Object type identifier

PostSearchResponse object { data, nextCursor, object, 2 more }
data: array of object { id, access, anchor, 30 more }

Array of search results

id: string

Unique identifier

access: PostAccess { companyExternalIds, userIds }
companyExternalIds: array of string

External company IDs explicitly granted access to this post. Empty array means no company-level restrictions. Non-empty means only users belonging to these companies can see the post.

userIds: array of string

User IDs explicitly granted access to this post. Empty array means no user-level restrictions (post uses board/org visibility). Non-empty means only these users (plus admins) can see the post.

anchor: object { commentId, conversationId, messageId, 3 more }

When kind is ‘insight’, where exactly the insight points back into its origin: an insight source record with character ranges into its fullText, or the native conversation/message/comment/post ids.

commentId: optional string
conversationId: optional string
messageId: optional string
ranges: optional array of object { end, start }
end: number
start: number
sourceRecordId: optional string
submissionId: optional string
assigneeId: string

ID of the admin assigned to this post, null if unassigned

author: PostAuthor { id, email, name, 2 more }
id: string

Author unique identifier

email: string

Author email (if available)

name: string

Author display name

profilePicture: string

Author profile picture URL

type: "admin" or "customer" or "guest" or 3 more

Type of user who authored the post

One of the following:
"admin"
"customer"
"guest"
"integration"
"bot"
"lead"
boardId: string

Board (category) ID this post belongs to

commentCount: number

Total number of comments

content: string

Post content in HTML format

createdAt: string

ISO 8601 timestamp when created

customFields: map[unknown]

Custom field values keyed by field ID

eta: string

Estimated completion time as ISO 8601 timestamp, null if not set

features: PostFeatures { commentsEnabled }
commentsEnabled: boolean

Whether comments are allowed on this post

groupKey: string

When kind is ‘insight’, the triage grouping key (source record id, conversation id, origin post id, or the insight’s own id for singletons). Legacy insights may be null and group as singletons.

inReview: boolean

Whether the post is pending moderation review

insightSource: object { channel, capturedBy, conversationId, 3 more }

Provenance of an insight: which channel it came from and how it was captured.

channel: "slack" or "discord" or "email" or 5 more
One of the following:
"slack"
"discord"
"email"
"support"
"widget"
"call"
"api"
"feedback"
capturedBy: optional "manual" or "auto" or "ai" or "api"
One of the following:
"manual"
"auto"
"ai"
"api"
conversationId: optional string
externalId: optional string
sourceLabel: optional string
url: optional string
integrations: object { clickup, devops, github, 4 more }

Third-party integration links associated with this post

clickup: array of object { id, title, url }
id: string

ClickUp task ID

title: string

ClickUp task title

url: string

URL to the ClickUp task

devops: array of object { id, projectId, projectName, 2 more }
id: number

Azure DevOps work item ID

projectId: string

Azure DevOps project ID

projectName: string

Azure DevOps project name

title: string

Work item title

url: string

URL to the work item

github: array of object { id, number, repositoryFullName, 3 more }
id: string

GitHub issue ID

number: string

GitHub issue number

repositoryFullName: string

Full repository name (owner/repo)

repositoryName: string

Repository name

title: string

GitHub issue title

url: string

URL to the GitHub issue

hubspot: array of object { dealAmount, dealClosed, objectId, type }
dealAmount: number

Deal amount (for DEAL type)

dealClosed: boolean

Whether the deal is closed (for DEAL type)

objectId: number

HubSpot object ID

type: "TICKET" or "DEAL" or "CONTACT"

HubSpot object type

One of the following:
"TICKET"
"DEAL"
"CONTACT"
jira: array of object { issueId, issueUrl }
issueId: string

Jira issue ID

issueUrl: string

URL to the Jira issue

linear: array of object { issueId, issueUrl }
issueId: string

Linear issue ID

issueUrl: string

URL to the Linear issue

salesforce: array of object { amount, isClosed, objectId, objectType }
amount: number

Opportunity amount (for Opportunity type)

isClosed: boolean

Whether the opportunity is closed (for Opportunity type)

objectId: string

Salesforce record ID

objectType: "Opportunity" or "Case"

Salesforce object type

One of the following:
"Opportunity"
"Case"
isPinned: boolean

Whether the post is pinned to the top

kind: "issue" or "insight" or "record"

Discriminates an actionable work item (‘issue’) from a customer submission whose claims were extracted into insights (‘record’ — not a work item). Defaults to ‘issue’ for all pre-existing posts. Default list responses return issues only; pass kind=‘record’ to opt in. Raw signal (‘insight’) is never returned by the posts resource — insights are served by /v2/insights.

One of the following:
"issue"
"insight"
"record"
linkedInsightCount: number

Number of insights linked to this issue as supporting evidence. Only meaningful when kind is ‘issue’.

linkedIssueId: string

When kind is ‘insight’, the ID of the issue this insight supports. Null when the insight is unlinked or when kind is ‘issue’.

object: "post"

Object type identifier

opportunityAmount: number

Total opportunity amount from linked HubSpot deals and Salesforce opportunities

portalHidden: boolean

True when the issue is hidden from portal/public surfaces. Missing stored values are returned as false.

postUrl: string

Full URL to view the post

slug: string

URL-friendly slug

status: PostStatus { id, color, isDefault, 3 more }
id: string

Unique identifier

color: string

Color for UI display

isDefault: boolean

Whether this is the default status for new posts

name: string

Display name

object: "post_status"

Object type identifier

type: "reviewing" or "unstarted" or "active" or 2 more

The workflow stage this status represents

One of the following:
"reviewing"
"unstarted"
"active"
"completed"
"canceled"
tags: array of PostTag { id, color, name }

Tags attached to this post

id: string

Tag unique identifier

color: string

Tag color hex code

name: string

Tag name

title: string

Post title

updatedAt: string

ISO 8601 timestamp when last modified

upvotes: number

Total number of upvotes

deduped: optional true

Present and true only on POST /v2/posts, when the request carried a source.externalId that already had a post. The existing post is returned unchanged with HTTP 200; a newly created post returns HTTP 201 without this field.

intakeMode: optional "request" or "feedback"

Present only on POST /v2/posts: the intakeMode the post was processed under (‘request’ when the request named none). On an idempotent replay (deduped: true) this is the mode the post was ORIGINALLY created with.

One of the following:
"request"
"feedback"
processing: optional PostProcessing { status, reason, results }

On POST /v2/posts — queued: a processing run (claim extraction or the Organize rewrite) was enqueued and its result lands asynchronously on the post. skipped: nothing was enqueued; reason says which gate decided (‘request_mode’ for every intakeMode: ‘request’ create). existing: the create was an idempotent replay and the post was not processed again. On GET /v2/posts/{id} this field is present only for posts created with intakeMode: 'feedback' and reports how far that processing has got (‘queued’, ‘processing’, ‘complete’, ‘needs_review’, or ‘skipped’ with the same reason the create returned), with results listing what was made of the submission once the run has finished.

status: "queued" or "processing" or "complete" or 3 more

‘queued’ = an AI run was enqueued and has not landed yet; ‘processing’ = the run is working on the submission; ‘complete’ = it finished and results lists what it produced; ‘needs_review’ = it finished and a teammate still has to decide something; ‘skipped’ = nothing was enqueued (see reason); ‘existing’ = idempotent replay, not processed again.

One of the following:
"queued"
"processing"
"complete"
"needs_review"
"skipped"
"existing"
reason: optional "request_mode" or "autopilot_off" or "insights_disabled" or 11 more

Why processing was skipped. ‘request_mode’ = intakeMode was ‘request’ (the default); ‘autopilot_off’ = the workspace’s Autopilot dial is off or its plan does not include it; ‘pipeline_paused’ = the platform paused AI processing (it resumes automatically); ‘support_board’ / ‘spam_held’ / ‘staff_authored’ / ‘no_content’ = the post did not qualify; ‘source_too_long’ = the text was over the length limit when the run read it (a create that long is normally refused with a 400 first); ‘extraction_failed’ = the AI could not read the text after repeated attempts; nothing was written, and opening the post in Triage runs the analysis again.

One of the following:
"request_mode"
"autopilot_off"
"insights_disabled"
"pipeline_paused"
"support_board"
"spam_held"
"staff_authored"
"not_a_post"
"no_board"
"no_id"
"no_content"
"source_too_long"
"extraction_failed"
"hook_error"
results: optional array of object { outcome, title, postId, 2 more }

What the workspace’s “Organize submissions” lane made of this submission, one entry per ask it found, in the order they appear in the text. Present on GET /v2/posts/{id} for posts created with intakeMode: 'feedback' once the run has finished. Absent while the run is still working, and absent for workspaces that do not organize submissions — there the post itself is the request.

outcome: "attached" or "request" or "question" or "held"

‘request’ = the ask became a request of its own; ‘attached’ = it was added as evidence to a request that already existed; ‘held’ = it became a request that is waiting for a teammate to publish it; ‘question’ = it asked something and no request was created, so a teammate answers it.

One of the following:
"attached"
"request"
"question"
"held"
title: string

The ask, as it was written up.

postId: optional string

The request this ask became, or was attached to. Absent for a question, and absent when the request is not readable through this API (for example a ticket board).

postTitle: optional string

Title of postId.

reason: optional "too_many_topics" or "uncertain" or "custom_fields" or "processing_failed"

Why a ‘held’ result is waiting for a teammate. Only on a ‘held’ result, and only one of these codes: ‘too_many_topics’ = the submission held more separate asks than one submission can become requests, so this one request lists the asks that did not fit, one line each in the customer’s words; ‘uncertain’ = the ask was too vague to file without a person; ‘custom_fields’ = the board requires custom fields this submission did not carry; ‘processing_failed’ = the AI could not rewrite the text (the request then holds the original words) or could not check this ask against existing requests, so a teammate confirms it is not a duplicate.

One of the following:
"too_many_topics"
"uncertain"
"custom_fields"
"processing_failed"
nextCursor: string

Cursor for fetching the next page (null if no more results)

object: "list"

Object type identifier

totalCount: optional number

Total number of posts matching the query, capped at 5000. When the actual total is at or above the cap, totalCountCapped is true and the value is exactly the cap.

totalCountCapped: optional boolean

True when totalCount is exactly the cap and the real count may be higher. UI can render as e.g. “5000+”.

FeedbackPostsVoters

User-submitted feedback and feature requests. Posts belong to boards and can be upvoted, commented on, and tracked through statuses.

List voters on a post
GET/v2/posts/{id}/voters
Add a voter to a post
POST/v2/posts/{id}/voters
Remove a voter from a post
DELETE/v2/posts/{id}/voters
ModelsExpand Collapse
VoterListResponse object { id, name, object, 16 more }
id: string

Unique identifier

name: string

User display name

object: "contact"

Object type identifier

type: "admin" or "customer" or "guest" or 3 more

Type of user

One of the following:
"admin"
"customer"
"guest"
"integration"
"bot"
"lead"
commentsCreated: optional number

Number of comments created

companies: optional array of Company { id, companyId, companySize, 11 more }

Companies the user belongs to

id: string

Featurebase internal ID

companyId: string

External company ID from your system

companySize: number

Company employee headcount

createdAt: string

ISO date when company was created

industry: string

Industry

lastActivity: string

ISO date of last activity

linkedUsers: number

Number of users linked to this company

monthlySpend: number

Monthly spend

name: string

Company name

object: "company"

Object type identifier

plan: string

Plan or tier name

updatedAt: string

ISO date when company was last updated

website: string

Company website URL

customFields: optional map[unknown]

Custom field values

customFields: optional map[unknown]

Custom field values on the user

description: optional string

User description/bio

email: optional string

User email

lastActivity: optional string

Last activity ISO timestamp

locale: optional string

User locale

manuallyOptedOutFromChangelog: optional boolean

Whether manually opted out from changelog

organizationId: optional string

Organization ID the user belongs to

postsCreated: optional number

Number of posts created

profilePicture: optional string

Profile picture URL

roles: optional array of string

User roles

subscribedToChangelog: optional boolean

Whether subscribed to changelog

userId: optional string

External user ID from SSO

verified: optional boolean

Whether email is verified

VoterAddResponse object { id, added, object, postId }
id: string

Voter unique identifier

added: true

Indicates the voter was added

object: "voter"

Object type identifier

postId: string

Post ID the voter was added to

VoterRemoveResponse object { id, object, postId, removed }
id: string

Voter unique identifier

object: "voter"

Object type identifier

postId: string

Post ID the voter was removed from

removed: true

Indicates the voter was removed

FeedbackPostsInsights

User-submitted feedback and feature requests. Posts belong to boards and can be upvoted, commented on, and tracked through statuses.

List the insights filed under a request
GET/v2/posts/{id}/insights
File a post as an insight under a request
POST/v2/posts/{id}/link-insight
Detach an insight from its request
POST/v2/posts/{id}/unlink-insight
Promote an insight into a request
POST/v2/posts/{id}/convert-to-issue

FeedbackPost Statuses

Post statuses define the workflow stages for posts (e.g., In Review, Active, Completed).

List all post statuses
GET/v2/post_statuses
Get a post status by ID
GET/v2/post_statuses/{id}
ModelsExpand Collapse
PostStatus object { id, color, isDefault, 3 more }
id: string

Unique identifier

color: string

Color for UI display

isDefault: boolean

Whether this is the default status for new posts

name: string

Display name

object: "post_status"

Object type identifier

type: "reviewing" or "unstarted" or "active" or 2 more

The workflow stage this status represents

One of the following:
"reviewing"
"unstarted"
"active"
"completed"
"canceled"
PostStatusListResponse = array of PostStatus { id, color, isDefault, 3 more }
id: string

Unique identifier

color: string

Color for UI display

isDefault: boolean

Whether this is the default status for new posts

name: string

Display name

object: "post_status"

Object type identifier

type: "reviewing" or "unstarted" or "active" or 2 more

The workflow stage this status represents

One of the following:
"reviewing"
"unstarted"
"active"
"completed"
"canceled"

FeedbackComments

Threaded discussions on posts and changelogs. Comments support voting, moderation, and privacy controls.

List comments
GET/v2/comments
Create a new comment
POST/v2/comments
Get a comment by ID
GET/v2/comments/{id}
Update a comment
PATCH/v2/comments/{id}
Delete a comment
DELETE/v2/comments/{id}
Delete a comment
DELETE/v2/comment
Search comments
POST/v2/comments/search
ModelsExpand Collapse
Comment object { id, author, changelogId, 14 more }
id: string

Unique identifier

author: CommentAuthor { id, name, profilePicture, type }
id: string

Author unique identifier

name: string

Author display name

profilePicture: string

Author profile picture URL

type: "admin" or "customer" or "guest" or 3 more

Type of user who authored the comment

One of the following:
"admin"
"customer"
"guest"
"integration"
"bot"
"lead"
changelogId: string

Changelog ID this comment belongs to

content: string

Comment content in HTML format

createdAt: string

ISO 8601 timestamp when created

downvotes: number

Number of downvotes

inReview: boolean

Whether the comment is in review

isDeleted: boolean

Whether the comment is deleted

isPinned: boolean

Whether the comment is pinned

isPrivate: boolean

Whether the comment is private

isSpam: boolean

Whether the comment is spam

object: "comment"

Object type identifier

parentCommentId: string

Parent comment ID for replies, null for root comments

postId: string

Post ID this comment belongs to

score: number

Net score (upvotes - downvotes)

updatedAt: string

ISO 8601 timestamp when updated

upvotes: number

Number of upvotes

CommentAuthor object { id, name, profilePicture, type }
id: string

Author unique identifier

name: string

Author display name

profilePicture: string

Author profile picture URL

type: "admin" or "customer" or "guest" or 3 more

Type of user who authored the comment

One of the following:
"admin"
"customer"
"guest"
"integration"
"bot"
"lead"
CommentDelete0Response object { id, deleted, object }
id: string

Unique identifier of the deleted comment

deleted: true

Indicates the resource was deleted

object: "comment"

Object type identifier

CommentDelete1Response object { success }
success: boolean
CommentSearchResponse object { data, nextCursor, object, 2 more }
data: array of object { id, author, changelogId, 14 more }

Array of search results

id: string

Unique identifier

author: CommentAuthor { id, name, profilePicture, type }
id: string

Author unique identifier

name: string

Author display name

profilePicture: string

Author profile picture URL

type: "admin" or "customer" or "guest" or 3 more

Type of user who authored the comment

One of the following:
"admin"
"customer"
"guest"
"integration"
"bot"
"lead"
changelogId: string

Changelog ID this comment belongs to

content: string

Comment content in HTML format

createdAt: string

ISO 8601 timestamp when created

downvotes: number

Number of downvotes

inReview: boolean

Whether the comment is in review

isDeleted: boolean

Whether the comment is deleted

isPinned: boolean

Whether the comment is pinned

isPrivate: boolean

Whether the comment is private

isSpam: boolean

Whether the comment is spam

object: "comment"

Object type identifier

parentCommentId: string

Parent comment ID for replies, null for root comments

postId: string

Post ID this comment belongs to

score: number

Net score (upvotes - downvotes)

updatedAt: string

ISO 8601 timestamp when updated

upvotes: number

Number of upvotes

nextCursor: string

Cursor for fetching the next page (null if no more results)

object: "list"

Object type identifier

totalCount: optional number

Total number of comments matching the query, capped at 5000 in filter mode and 200 (the Turbopuffer top-K) in search mode. When at the cap, totalCountCapped is true and the value is exactly the cap.

totalCountCapped: optional boolean

True when totalCount is exactly the cap and the real count may be higher. UI can render as e.g. “5000+”.

FeedbackCustom Fields

Configurable input fields for posts in your Featurebase organization. Custom fields allow you to collect additional structured data when users create posts.

List custom fields
GET/v2/custom_fields
Get a custom field by ID
GET/v2/custom_fields/{id}
ModelsExpand Collapse
CustomField object { id, label, object, 9 more }
id: string

Unique identifier

label: string

Field label displayed to users

object: "custom_field"

Object type identifier

type: "text" or "number" or "select" or 4 more

Field type

One of the following:
"text"
"number"
"select"
"multi-select"
"checkbox"
"date"
"file"
allowMultiple: optional boolean

Whether multiple files can be uploaded (file fields only)

createdAt: optional string

ISO timestamp when created

internal: optional boolean

Whether the field is for internal use only

options: optional array of object { id, label, description, 2 more }

Options for select/multi-select fields

id: string

Option unique identifier

label: string

Option display label

description: optional string

Option description

group: optional string

Option group label

icon: optional string

Option icon

placeholder: optional string

Placeholder text

public: optional boolean

Whether the field value is publicly visible

required: optional boolean

Whether the field is required

updatedAt: optional string

ISO timestamp when last updated

CustomFieldList object { data, nextCursor, object, pagination }
data: array of CustomField { id, label, object, 9 more }

Array of custom fields

id: string

Unique identifier

label: string

Field label displayed to users

object: "custom_field"

Object type identifier

type: "text" or "number" or "select" or 4 more

Field type

One of the following:
"text"
"number"
"select"
"multi-select"
"checkbox"
"date"
"file"
allowMultiple: optional boolean

Whether multiple files can be uploaded (file fields only)

createdAt: optional string

ISO timestamp when created

internal: optional boolean

Whether the field is for internal use only

options: optional array of object { id, label, description, 2 more }

Options for select/multi-select fields

id: string

Option unique identifier

label: string

Option display label

description: optional string

Option description

group: optional string

Option group label

icon: optional string

Option icon

placeholder: optional string

Placeholder text

public: optional boolean

Whether the field value is publicly visible

required: optional boolean

Whether the field is required

updatedAt: optional string

ISO timestamp when last updated

nextCursor: string

Cursor for fetching the next page (cursor-based pagination)

object: "list"

Object type identifier