File a post as an insight under a request
An insight is a customer's own words kept as evidence under a request — a quote, not a request
of its own, and never returned by GET /v2/posts.
This endpoint turns the post {id} into an insight under the request issueId. Use it for the
sentence a customer actually said; use attachTo on POST /v2/posts when the thing you are
filing is a request in its own right and should stay one.
What changes
- The post becomes an insight:
kindisinsightandlinkedIssueIdis the request. - It leaves the posts resource.
GET /v2/posts/{id}answers404from then on, and it no longer appears inGET /v2/posts. Read it back throughGET /v2/posts/{issueId}/insights, which is where it now lives. - Its author counts as a supporter of the request: the insight's upvoters are rolled onto the
request (deduped by user, so a customer who had already voted is not counted twice) and the
insight's author is subscribed to the request's updates. The request's
upvotesgoes up accordingly. - The request's
linkedInsightCountis recounted from the live set of insights pointing at it.
Linking again to the same request is a no-op and returns the insight unchanged.
An insight that is already filed under a DIFFERENT request is MOVED — but only if nobody has
confirmed that first link by hand. The old link is dropped first, and the old request is remembered
as "not a match" so automatic matching never puts it back there. An insight whose link was already
confirmed by a person (every link made through this endpoint counts as confirmed) is refused with
400 Insight is already linked to another issue. Detach it first. — call
POST /v2/posts/{id}/unlink-insight first, then link it where you want it.
Which targets are refused
404— no post with{id}in this workspace, or one the API key cannot see (Insight not found.); no request withissueId, or one the API key cannot see (Issue not found.).400—issueIdequals{id}(a post cannot be linked to itself); the target is not a request (it is itself an insight, or a record — convert or restore it first); the target is a processed submission (atomizedAt— link to the requests that came out of it); the post being linked is a record (link the record's extracted insights instead); the post is already filed under another request by hand (unlink it first).400 invalid_id—{id}orissueIdis not a valid object id.
A merged, held-for-moderation, spam-held or closed target is not refused here: those guards apply to automatic (AI) attachment only, and a person linking by hand is trusted to mean it.
Response
The updated insight, in the standard post format (kind: 'insight', linkedIssueId set). The
request it was linked to is not returned — fetch it with GET /v2/posts/{issueId} if you need
its new upvotes and linkedInsightCount.
Body
issueId— the request to file this post under. Required.linkSource— deprecated and ignored; a link made through the API is always a manual link.
Errors
Failures raised by the insight service (404 / 400 / 409) are returned as
{ "code": <status>, "message": "..." } rather than in the Stripe-style envelope the rest of this
resource uses. The status code is the contract; do not parse the body shape.
Body
ID of the request this post becomes evidence for. Must be a request in this workspace that can still take evidence.
507f1f77bcf86cd799439011Response
Success
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.
ID of the admin assigned to this post, null if unassigned
507f1f77bcf86cd799439013Post content in HTML format
<p>It would be great to have a dark mode option for the dashboard.</p>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.
truetrueEstimated completion time as ISO 8601 timestamp, null if not set
2025-01-01T00:00:00.000ZWhen 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.
Provenance of an insight: which channel it came from and how it was captured.
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.
request, feedbackrequestDiscriminates 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.
issue, insight, recordissueNumber of insights linked to this issue as supporting evidence. Only meaningful when kind is 'issue'.
0When kind is 'insight', the ID of the issue this insight supports. Null when the insight is unlinked or when kind is 'issue'.
Total opportunity amount from linked HubSpot deals and Salesforce opportunities
30000True when the issue is hidden from portal/public surfaces. Missing stored values are returned as false.
falseFull URL to view the post
https://feedback.example.com/p/add-dark-mode-supportOn 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.