Skip to content
Dashboard

Get a post by ID

client.feedback.posts.retrieve(stringid, PostRetrieveParams { featurebaseVersion } params?, RequestOptionsoptions?): Post { id, access, anchor, 30 more }
GET/v2/posts/{id}

Retrieves a single post by its unique identifier.

Returns the full post object including:

  • Author information
  • Current status
  • Tags
  • Voting stats
  • Engagement metrics
  • Custom field values

Feedback processing

For a post created with intakeMode: 'feedback', the response also carries processing, which says how far Featurebase has got with the text and what it made of it. It is absent on every other post, and on the list endpoint.

processing.status:

  • queued - the text was accepted and no result has landed yet
  • processing - Featurebase is organizing the submission right now
  • complete - it is done; results lists what came out of it
  • needs_review - it is done, and a teammate still has to decide something
  • skipped - nothing ran, and nothing will; reason says why (the same reason the create returned, for example autopilot_off). The post is stored exactly as it was sent.

processing.results is present in workspaces that organize submissions, once the run has finished: one entry per ask found in the text, in the order they appear. Each entry has the ask’s title and its outcome:

  • 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 waiting for a teammate to publish it
  • question - it asked something; no request was created and a teammate answers it

postId and postTitle name the request the ask became or was attached to. They are absent for a question, and absent when that request is not readable through this API.

held results carry a reason code saying what the teammate still has to decide: too_many_topics (the submission held more separate asks than one submission can become requests, so this request lists the ones that did not fit), uncertain, custom_fields or processing_failed (the AI could not rewrite the text, or could not check the ask against existing requests, so a teammate confirms it). No other outcome carries it.

Workspaces that do not organize submissions report status only: there the post you sent IS the request, so there is no separate result to list.

ParametersExpand Collapse
id: string

Post unique identifier

params: PostRetrieveParams { featurebaseVersion }
featurebaseVersion?: "2026-08-19.orbit" | "2026-01-01.nova" | "2025-12-12.clover"

API version for this request. Defaults to your organization’s configured API version if not specified.

One of the following:
"2026-08-19.orbit"
"2026-01-01.nova"
"2025-12-12.clover"
ReturnsExpand Collapse
Post { id, access, anchor, 30 more }
id: string

Unique identifier

access: PostAccess { companyExternalIds, userIds }
companyExternalIds: Array<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<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: Anchor | null

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?: string | null
conversationId?: string | null
messageId?: string | null
ranges?: Array<Range> | null
end: number
start: number
sourceRecordId?: string | null
submissionId?: string | null
assigneeId: string | null

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

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

Author unique identifier

email: string | null

Author email (if available)

name: string

Author display name

profilePicture: string | null

Author profile picture URL

type: "admin" | "customer" | "guest" | 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: Record<string, unknown>

Custom field values keyed by field ID

eta: string | null

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 | null

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: InsightSource | null

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

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

Third-party integration links associated with this post

clickup: Array<Clickup>
id: string

ClickUp task ID

title: string

ClickUp task title

url: string

URL to the ClickUp task

devops: Array<Devop>
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<GitHub>
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<Hubspot>
dealAmount: number | null

Deal amount (for DEAL type)

dealClosed: boolean | null

Whether the deal is closed (for DEAL type)

objectId: number

HubSpot object ID

type: "TICKET" | "DEAL" | "CONTACT"

HubSpot object type

One of the following:
"TICKET"
"DEAL"
"CONTACT"
jira: Array<Jira>
issueId: string

Jira issue ID

issueUrl: string | null

URL to the Jira issue

linear: Array<Linear>
issueId: string

Linear issue ID

issueUrl: string | null

URL to the Linear issue

salesforce: Array<Salesforce>
amount: number | null

Opportunity amount (for Opportunity type)

isClosed: boolean | null

Whether the opportunity is closed (for Opportunity type)

objectId: string

Salesforce record ID

objectType: "Opportunity" | "Case"

Salesforce object type

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

Whether the post is pinned to the top

kind: "issue" | "insight" | "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 | null

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 | null

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" | "unstarted" | "active" | 2 more

The workflow stage this status represents

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

Tags attached to this post

id: string

Tag unique identifier

color: string | null

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?: 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?: "request" | "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?: 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" | "processing" | "complete" | 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?: "request_mode" | "autopilot_off" | "insights_disabled" | 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?: Array<Result>

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" | "request" | "question" | "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?: 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?: string

Title of postId.

reason?: "too_many_topics" | "uncertain" | "custom_fields" | "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"

Get a post by ID

import Featurebase from 'featurebase-node';

const client = new Featurebase({
  apiKey: process.env['FEATUREBASE_API_KEY'], // This is the default and can be omitted
});

const post = await client.feedback.posts.retrieve('507f1f77bcf86cd799439011');

console.log(post.id);
{
  "id": "507f1f77bcf86cd799439011",
  "access": {
    "companyExternalIds": [
      "string"
    ],
    "userIds": [
      "string"
    ]
  },
  "anchor": {
    "commentId": "commentId",
    "conversationId": "conversationId",
    "messageId": "messageId",
    "ranges": [
      {
        "end": 0,
        "start": 0
      }
    ],
    "sourceRecordId": "sourceRecordId",
    "submissionId": "submissionId"
  },
  "assigneeId": "507f1f77bcf86cd799439013",
  "author": {
    "id": "507f1f77bcf86cd799439011",
    "email": "john@example.com",
    "name": "John Doe",
    "profilePicture": "https://cdn.example.com/avatars/john.png",
    "type": "customer"
  },
  "boardId": "507f1f77bcf86cd799439011",
  "commentCount": 5,
  "content": "<p>It would be great to have a dark mode option for the dashboard.</p>",
  "createdAt": "2023-12-12T00:00:00.000Z",
  "customFields": {
    "cf_priority": "bar",
    "cf_effort": "bar"
  },
  "eta": "2025-01-01T00:00:00.000Z",
  "features": {
    "commentsEnabled": true
  },
  "groupKey": null,
  "inReview": false,
  "insightSource": {
    "channel": "slack",
    "capturedBy": "manual",
    "conversationId": "conversationId",
    "externalId": "externalId",
    "sourceLabel": "sourceLabel",
    "url": "url"
  },
  "integrations": {
    "clickup": [
      {
        "id": "86a1b2c3d",
        "title": "Add dark mode support",
        "url": "https://app.clickup.com/t/86a1b2c3d"
      }
    ],
    "devops": [
      {
        "id": 1234,
        "projectId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "projectName": "My Project",
        "title": "Add dark mode support",
        "url": "https://dev.azure.com/org/project/_workitems/edit/1234"
      }
    ],
    "github": [
      {
        "id": "1234567890",
        "number": "42",
        "repositoryFullName": "acme/backend",
        "repositoryName": "backend",
        "title": "Add dark mode support",
        "url": "https://github.com/acme/backend/issues/42"
      }
    ],
    "hubspot": [
      {
        "dealAmount": 5000,
        "dealClosed": false,
        "objectId": 123456789,
        "type": "TICKET"
      }
    ],
    "jira": [
      {
        "issueId": "10042",
        "issueUrl": "https://myteam.atlassian.net/browse/PROJ-123"
      }
    ],
    "linear": [
      {
        "issueId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "issueUrl": "https://linear.app/team/issue/ENG-123"
      }
    ],
    "salesforce": [
      {
        "amount": 25000,
        "isClosed": false,
        "objectId": "006Dn00000Abcdef",
        "objectType": "Opportunity"
      }
    ]
  },
  "isPinned": false,
  "kind": "issue",
  "linkedInsightCount": 0,
  "linkedIssueId": null,
  "object": "post",
  "opportunityAmount": 30000,
  "portalHidden": false,
  "postUrl": "https://feedback.example.com/p/add-dark-mode-support",
  "slug": "add-dark-mode-support",
  "status": {
    "id": "507f1f77bcf86cd799439011",
    "color": "Blue",
    "isDefault": false,
    "name": "In Progress",
    "object": "post_status",
    "type": "active"
  },
  "tags": [
    {
      "id": "507f1f77bcf86cd799439011",
      "color": "#FF5722",
      "name": "bug"
    }
  ],
  "title": "Add dark mode support",
  "updatedAt": "2023-12-13T00:00:00.000Z",
  "upvotes": 42,
  "deduped": true,
  "intakeMode": "request",
  "processing": {
    "status": "queued",
    "reason": "request_mode",
    "results": [
      {
        "outcome": "request",
        "title": "Export the board to CSV",
        "postId": "507f1f77bcf86cd799439011",
        "postTitle": "CSV export",
        "reason": "too_many_topics"
      }
    ]
  }
}
Returns Examples
{
  "id": "507f1f77bcf86cd799439011",
  "access": {
    "companyExternalIds": [
      "string"
    ],
    "userIds": [
      "string"
    ]
  },
  "anchor": {
    "commentId": "commentId",
    "conversationId": "conversationId",
    "messageId": "messageId",
    "ranges": [
      {
        "end": 0,
        "start": 0
      }
    ],
    "sourceRecordId": "sourceRecordId",
    "submissionId": "submissionId"
  },
  "assigneeId": "507f1f77bcf86cd799439013",
  "author": {
    "id": "507f1f77bcf86cd799439011",
    "email": "john@example.com",
    "name": "John Doe",
    "profilePicture": "https://cdn.example.com/avatars/john.png",
    "type": "customer"
  },
  "boardId": "507f1f77bcf86cd799439011",
  "commentCount": 5,
  "content": "<p>It would be great to have a dark mode option for the dashboard.</p>",
  "createdAt": "2023-12-12T00:00:00.000Z",
  "customFields": {
    "cf_priority": "bar",
    "cf_effort": "bar"
  },
  "eta": "2025-01-01T00:00:00.000Z",
  "features": {
    "commentsEnabled": true
  },
  "groupKey": null,
  "inReview": false,
  "insightSource": {
    "channel": "slack",
    "capturedBy": "manual",
    "conversationId": "conversationId",
    "externalId": "externalId",
    "sourceLabel": "sourceLabel",
    "url": "url"
  },
  "integrations": {
    "clickup": [
      {
        "id": "86a1b2c3d",
        "title": "Add dark mode support",
        "url": "https://app.clickup.com/t/86a1b2c3d"
      }
    ],
    "devops": [
      {
        "id": 1234,
        "projectId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "projectName": "My Project",
        "title": "Add dark mode support",
        "url": "https://dev.azure.com/org/project/_workitems/edit/1234"
      }
    ],
    "github": [
      {
        "id": "1234567890",
        "number": "42",
        "repositoryFullName": "acme/backend",
        "repositoryName": "backend",
        "title": "Add dark mode support",
        "url": "https://github.com/acme/backend/issues/42"
      }
    ],
    "hubspot": [
      {
        "dealAmount": 5000,
        "dealClosed": false,
        "objectId": 123456789,
        "type": "TICKET"
      }
    ],
    "jira": [
      {
        "issueId": "10042",
        "issueUrl": "https://myteam.atlassian.net/browse/PROJ-123"
      }
    ],
    "linear": [
      {
        "issueId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "issueUrl": "https://linear.app/team/issue/ENG-123"
      }
    ],
    "salesforce": [
      {
        "amount": 25000,
        "isClosed": false,
        "objectId": "006Dn00000Abcdef",
        "objectType": "Opportunity"
      }
    ]
  },
  "isPinned": false,
  "kind": "issue",
  "linkedInsightCount": 0,
  "linkedIssueId": null,
  "object": "post",
  "opportunityAmount": 30000,
  "portalHidden": false,
  "postUrl": "https://feedback.example.com/p/add-dark-mode-support",
  "slug": "add-dark-mode-support",
  "status": {
    "id": "507f1f77bcf86cd799439011",
    "color": "Blue",
    "isDefault": false,
    "name": "In Progress",
    "object": "post_status",
    "type": "active"
  },
  "tags": [
    {
      "id": "507f1f77bcf86cd799439011",
      "color": "#FF5722",
      "name": "bug"
    }
  ],
  "title": "Add dark mode support",
  "updatedAt": "2023-12-13T00:00:00.000Z",
  "upvotes": 42,
  "deduped": true,
  "intakeMode": "request",
  "processing": {
    "status": "queued",
    "reason": "request_mode",
    "results": [
      {
        "outcome": "request",
        "title": "Export the board to CSV",
        "postId": "507f1f77bcf86cd799439011",
        "postTitle": "CSV export",
        "reason": "too_many_topics"
      }
    ]
  }
}