featurebase-node 0.15.0 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/client.d.mts +8 -2
  3. package/client.d.mts.map +1 -1
  4. package/client.d.ts +8 -2
  5. package/client.d.ts.map +1 -1
  6. package/client.js +6 -0
  7. package/client.js.map +1 -1
  8. package/client.mjs +6 -0
  9. package/client.mjs.map +1 -1
  10. package/package.json +1 -1
  11. package/resources/audit-logs.d.mts +2 -2
  12. package/resources/audit-logs.d.mts.map +1 -1
  13. package/resources/audit-logs.d.ts +2 -2
  14. package/resources/audit-logs.d.ts.map +1 -1
  15. package/resources/changelogs/changelogs.d.mts +1166 -6
  16. package/resources/changelogs/changelogs.d.mts.map +1 -1
  17. package/resources/changelogs/changelogs.d.ts +1166 -6
  18. package/resources/changelogs/changelogs.d.ts.map +1 -1
  19. package/resources/changelogs/changelogs.js +53 -3
  20. package/resources/changelogs/changelogs.js.map +1 -1
  21. package/resources/changelogs/changelogs.mjs +53 -3
  22. package/resources/changelogs/changelogs.mjs.map +1 -1
  23. package/resources/changelogs/index.d.mts +1 -1
  24. package/resources/changelogs/index.d.mts.map +1 -1
  25. package/resources/changelogs/index.d.ts +1 -1
  26. package/resources/changelogs/index.d.ts.map +1 -1
  27. package/resources/changelogs/index.js.map +1 -1
  28. package/resources/changelogs/index.mjs.map +1 -1
  29. package/resources/conversation-attributes.d.mts +5 -0
  30. package/resources/conversation-attributes.d.mts.map +1 -1
  31. package/resources/conversation-attributes.d.ts +5 -0
  32. package/resources/conversation-attributes.d.ts.map +1 -1
  33. package/resources/feedback/comments.d.mts +4 -2
  34. package/resources/feedback/comments.d.mts.map +1 -1
  35. package/resources/feedback/comments.d.ts +4 -2
  36. package/resources/feedback/comments.d.ts.map +1 -1
  37. package/resources/feedback/custom-fields.d.mts +12 -0
  38. package/resources/feedback/custom-fields.d.mts.map +1 -1
  39. package/resources/feedback/custom-fields.d.ts +12 -0
  40. package/resources/feedback/custom-fields.d.ts.map +1 -1
  41. package/resources/feedback/posts/index.d.mts +1 -0
  42. package/resources/feedback/posts/index.d.mts.map +1 -1
  43. package/resources/feedback/posts/index.d.ts +1 -0
  44. package/resources/feedback/posts/index.d.ts.map +1 -1
  45. package/resources/feedback/posts/index.js +3 -1
  46. package/resources/feedback/posts/index.js.map +1 -1
  47. package/resources/feedback/posts/index.mjs +1 -0
  48. package/resources/feedback/posts/index.mjs.map +1 -1
  49. package/resources/feedback/posts/insights.d.mts +254 -0
  50. package/resources/feedback/posts/insights.d.mts.map +1 -0
  51. package/resources/feedback/posts/insights.d.ts +254 -0
  52. package/resources/feedback/posts/insights.d.ts.map +1 -0
  53. package/resources/feedback/posts/insights.js +269 -0
  54. package/resources/feedback/posts/insights.js.map +1 -0
  55. package/resources/feedback/posts/insights.mjs +265 -0
  56. package/resources/feedback/posts/insights.mjs.map +1 -0
  57. package/resources/feedback/posts/posts.d.mts +647 -20
  58. package/resources/feedback/posts/posts.d.mts.map +1 -1
  59. package/resources/feedback/posts/posts.d.ts +647 -20
  60. package/resources/feedback/posts/posts.d.ts.map +1 -1
  61. package/resources/feedback/posts/posts.js +199 -7
  62. package/resources/feedback/posts/posts.js.map +1 -1
  63. package/resources/feedback/posts/posts.mjs +199 -7
  64. package/resources/feedback/posts/posts.mjs.map +1 -1
  65. package/resources/index.d.mts +2 -1
  66. package/resources/index.d.mts.map +1 -1
  67. package/resources/index.d.ts +2 -1
  68. package/resources/index.d.ts.map +1 -1
  69. package/resources/index.js +3 -1
  70. package/resources/index.js.map +1 -1
  71. package/resources/index.mjs +1 -0
  72. package/resources/index.mjs.map +1 -1
  73. package/resources/reports.d.mts +101 -19
  74. package/resources/reports.d.mts.map +1 -1
  75. package/resources/reports.d.ts +101 -19
  76. package/resources/reports.d.ts.map +1 -1
  77. package/resources/reports.js +15 -8
  78. package/resources/reports.js.map +1 -1
  79. package/resources/reports.mjs +15 -8
  80. package/resources/reports.mjs.map +1 -1
  81. package/resources/support/conversations/conversations.d.mts +22 -1
  82. package/resources/support/conversations/conversations.d.mts.map +1 -1
  83. package/resources/support/conversations/conversations.d.ts +22 -1
  84. package/resources/support/conversations/conversations.d.ts.map +1 -1
  85. package/resources/support/conversations/conversations.js.map +1 -1
  86. package/resources/support/conversations/conversations.mjs.map +1 -1
  87. package/resources/support/tickets/tickets.d.mts +9 -5
  88. package/resources/support/tickets/tickets.d.mts.map +1 -1
  89. package/resources/support/tickets/tickets.d.ts +9 -5
  90. package/resources/support/tickets/tickets.d.ts.map +1 -1
  91. package/resources/support/tickets/tickets.js.map +1 -1
  92. package/resources/support/tickets/tickets.mjs.map +1 -1
  93. package/resources/training-data/files.d.mts +292 -0
  94. package/resources/training-data/files.d.mts.map +1 -0
  95. package/resources/training-data/files.d.ts +292 -0
  96. package/resources/training-data/files.d.ts.map +1 -0
  97. package/resources/training-data/files.js +171 -0
  98. package/resources/training-data/files.js.map +1 -0
  99. package/resources/training-data/files.mjs +167 -0
  100. package/resources/training-data/files.mjs.map +1 -0
  101. package/resources/training-data/index.d.mts +4 -0
  102. package/resources/training-data/index.d.mts.map +1 -0
  103. package/resources/training-data/index.d.ts +4 -0
  104. package/resources/training-data/index.d.ts.map +1 -0
  105. package/resources/training-data/index.js +11 -0
  106. package/resources/training-data/index.js.map +1 -0
  107. package/resources/training-data/index.mjs +5 -0
  108. package/resources/training-data/index.mjs.map +1 -0
  109. package/resources/training-data/qna.d.mts +370 -0
  110. package/resources/training-data/qna.d.mts.map +1 -0
  111. package/resources/training-data/qna.d.ts +370 -0
  112. package/resources/training-data/qna.d.ts.map +1 -0
  113. package/resources/training-data/qna.js +177 -0
  114. package/resources/training-data/qna.js.map +1 -0
  115. package/resources/training-data/qna.mjs +173 -0
  116. package/resources/training-data/qna.mjs.map +1 -0
  117. package/resources/training-data/training-data.d.mts +442 -0
  118. package/resources/training-data/training-data.d.mts.map +1 -0
  119. package/resources/training-data/training-data.d.ts +442 -0
  120. package/resources/training-data/training-data.d.ts.map +1 -0
  121. package/resources/training-data/training-data.js +105 -0
  122. package/resources/training-data/training-data.js.map +1 -0
  123. package/resources/training-data/training-data.mjs +100 -0
  124. package/resources/training-data/training-data.mjs.map +1 -0
  125. package/resources/training-data.d.mts +2 -0
  126. package/resources/training-data.d.mts.map +1 -0
  127. package/resources/training-data.d.ts +2 -0
  128. package/resources/training-data.d.ts.map +1 -0
  129. package/resources/training-data.js +6 -0
  130. package/resources/training-data.js.map +1 -0
  131. package/resources/training-data.mjs +3 -0
  132. package/resources/training-data.mjs.map +1 -0
  133. package/src/client.ts +22 -0
  134. package/src/resources/audit-logs.ts +12 -0
  135. package/src/resources/changelogs/changelogs.ts +2003 -51
  136. package/src/resources/changelogs/index.ts +1 -0
  137. package/src/resources/conversation-attributes.ts +6 -0
  138. package/src/resources/feedback/comments.ts +4 -2
  139. package/src/resources/feedback/custom-fields.ts +15 -0
  140. package/src/resources/feedback/posts/index.ts +7 -0
  141. package/src/resources/feedback/posts/insights.ts +340 -0
  142. package/src/resources/feedback/posts/posts.ts +764 -20
  143. package/src/resources/index.ts +8 -0
  144. package/src/resources/reports.ts +160 -19
  145. package/src/resources/support/conversations/conversations.ts +27 -1
  146. package/src/resources/support/tickets/tickets.ts +9 -5
  147. package/src/resources/training-data/files.ts +431 -0
  148. package/src/resources/training-data/index.ts +31 -0
  149. package/src/resources/training-data/qna.ts +514 -0
  150. package/src/resources/training-data/training-data.ts +603 -0
  151. package/src/resources/training-data.ts +3 -0
  152. package/src/version.ts +1 -1
  153. package/version.d.mts +1 -1
  154. package/version.d.ts +1 -1
  155. package/version.js +1 -1
  156. package/version.mjs +1 -1
@@ -3,6 +3,14 @@
3
3
  import { APIResource } from '../../../core/resource';
4
4
  import * as PostsAPI from './posts';
5
5
  import * as PostStatusesAPI from '../post-statuses';
6
+ import * as InsightsAPI from './insights';
7
+ import {
8
+ InsightConvertToPostParams,
9
+ InsightLinkParams,
10
+ InsightListParams,
11
+ InsightUnlinkParams,
12
+ Insights,
13
+ } from './insights';
6
14
  import * as VotersAPI from './voters';
7
15
  import {
8
16
  VoterAddParams,
@@ -26,17 +34,29 @@ import { path } from '../../../internal/utils/path';
26
34
  */
27
35
  export class Posts extends APIResource {
28
36
  voters: VotersAPI.Voters = new VotersAPI.Voters(this._client);
37
+ insights: InsightsAPI.Insights = new InsightsAPI.Insights(this._client);
29
38
 
30
39
  /**
31
40
  * Creates a new post (feedback submission) in the specified board.
32
41
  *
33
42
  * ### Required Fields
34
43
  *
35
- * - `title` - Post title (minimum 2 characters)
36
- * - `boardId` - Board ID to create the post in
44
+ * - `title` - Post title (minimum 2 characters). Required unless `intakeMode` is
45
+ * `feedback`; see "Intake mode" below.
37
46
  *
38
47
  * ### Optional Fields
39
48
  *
49
+ * - `boardId` - Board ID to create the post in. Omit to use the organization's
50
+ * default board.
51
+ * - `intakeMode` - What the text is: `request` (default) or `feedback`. See
52
+ * "Intake mode" below.
53
+ * - `source` - Where the request came from:
54
+ * `{ channel, externalId, url?, conversationId?, label? }`. See "Provenance and
55
+ * idempotency" below.
56
+ * - `attachTo` - Id of an existing open request this post belongs with. The new
57
+ * post stays a post. See "attachTo, or link-insight?" below.
58
+ * - `internal` - Store this request on the hidden internal board (never
59
+ * portal-visible). Cannot be combined with `boardId`.
40
60
  * - `content` - Post content in HTML format
41
61
  * - `tags` - Array of tag names to attach
42
62
  * - `statusId` - Status ID to set (defaults to board's default status)
@@ -62,20 +82,155 @@ export class Posts extends APIResource {
62
82
  *
63
83
  * Resolution priority: `id` > `userId` > `email` > authenticated user
64
84
  *
85
+ * ### Intake mode
86
+ *
87
+ * `intakeMode` is the caller's declared intent, and the ONE field that selects how
88
+ * the post is processed. `author` and `source` only describe where the text came
89
+ * from; they never select the path.
90
+ *
91
+ * - `request` (default, and the behaviour of every call that does not send the
92
+ * field): a finished request. Stored exactly as supplied — no AI claim
93
+ * extraction, no rewrite — whoever the author is. Featurebase may still link
94
+ * existing customer evidence TO the new request in the background; that never
95
+ * modifies it.
96
+ * - `feedback`: raw customer feedback (a Slack message, a call note, a survey
97
+ * answer). Processed exactly like a post written on the portal: organized into a
98
+ * request when the workspace's "Organize submissions" lane is on, otherwise its
99
+ * claims are extracted and matched against existing requests. The workspace's
100
+ * Autopilot dial, plan, AI budget, moderation and spam settings all apply, and
101
+ * the post is processed even when it is filed under the API key's own user.
102
+ * `statusId` and `eta` are rejected in this mode (400): raw feedback carries no
103
+ * decision yet. Feedback creates are rate-limited per workspace more strictly
104
+ * than requests (429 with `Retry-After`).
105
+ *
106
+ * At most 5 requests organized from one submission notify the team (admin
107
+ * notifications, mentions, Slack, Discord and tracker pushes); the rest are
108
+ * created silently — still revealed, published and returned in
109
+ * `processing.results` — and still fire the `post.created` webhook.
110
+ *
111
+ * In `feedback` mode `title` is optional: raw text rarely has one. Omit it (or
112
+ * send it blank) and the first line of `content` becomes the title, cut to 120
113
+ * characters. `content` is then required — an empty title and an empty content
114
+ * together are rejected with a 400 on `content`. In `request` mode `title` stays
115
+ * required, minimum 2 characters.
116
+ *
117
+ * #### How much text a feedback create may carry
118
+ *
119
+ * Feedback text is read whole or not at all: up to the limit of its
120
+ * `source.channel` it is read in full, from the first line to the last; anything
121
+ * longer is rejected (400 on `content`) instead of being trimmed, and the error
122
+ * message states the limit and the length that was sent.
123
+ *
124
+ * | `source.channel` | Limit (characters) | Sized for |
125
+ * | ------------------ | ------------------ | -------------------------- |
126
+ * | `call` | 120,000 | a two-hour call transcript |
127
+ * | `email` | 60,000 | a long email thread |
128
+ * | `api` | 60,000 | a document or an export |
129
+ * | `slack`, `discord` | 30,000 | a thread or channel export |
130
+ *
131
+ * Text sent without a `source.channel` gets the `api` limit. The limits are the
132
+ * same for every workspace.
133
+ *
134
+ * The count is of plain text: the title and the content with its HTML markup
135
+ * removed. Workspaces that do not process feedback at all (the AI is switched off)
136
+ * have no limit — the text is stored exactly as sent. To send more, split it and
137
+ * give each part its own `source.externalId`.
138
+ *
139
+ * Long text takes longer. A short message is processed in seconds; a long one is
140
+ * read in parts, one after the other, and every ask it produces is then matched
141
+ * and filed in turn — allow tens of minutes for a two-hour transcript. An ask that
142
+ * comes up more than once in the same text is reported once. One submission yields
143
+ * at most 30 requests: when the text holds more separate asks than that, the 29
144
+ * strongest become requests as usual and the rest are listed — one line each, the
145
+ * ask and the customer's own words — in a single request held for a teammate.
146
+ * Where the workspace does not organize submissions, at most 50 asks are captured
147
+ * from one submission; the rest of a longer text is not read.
148
+ *
149
+ * Long feedback is rate-limited on its own: at most 20 submissions per workspace
150
+ * per hour may be longer than a single reading pass (about 16,000 characters where
151
+ * the workspace organizes submissions, otherwise 6,000 — 24,000 for a `call`).
152
+ * Past that the create answers 429 with `Retry-After`; shorter feedback is not
153
+ * affected.
154
+ *
155
+ * The response reports what happened in `intakeMode` and `processing`:
156
+ * `processing.status` is `queued` when an AI run was enqueued (its result lands on
157
+ * the post asynchronously), `skipped` with a `reason` when nothing was enqueued
158
+ * (`request_mode`, `autopilot_off`, `pipeline_paused`, `support_board`,
159
+ * `spam_held`, `staff_authored`, …), or `existing` on an idempotent replay. To
160
+ * read where the feedback ended up afterwards, fetch the post with
161
+ * `GET /v2/posts/{id}`.
162
+ *
163
+ * ### Provenance and idempotency
164
+ *
165
+ * `source` records where a request came from and makes the create idempotent:
166
+ *
167
+ * - `source.externalId` is the stable id of the message or record in your system.
168
+ * Sending the same `(channel, externalId)` twice returns the post created the
169
+ * first time, unchanged, with `deduped: true` — the first delivery always wins.
170
+ * - Ids are stored namespaced as `api:<externalId>`, so they can never collide
171
+ * with ids Featurebase writes itself.
172
+ * - `feedback`, `widget` and `support` are reserved for posts authored on the
173
+ * portal, the widget and the inbox, and are rejected with a 400.
174
+ * - With `source` and no `author`, the post is attributed to a guest named after
175
+ * `source.label` (or the channel), because a relayed request belongs to the
176
+ * customer who said it — not to the API key that carried it.
177
+ *
178
+ * ### attachTo, or link-insight?
179
+ *
180
+ * Both attach something to a request that already exists, and they do different
181
+ * things.
182
+ *
183
+ * `attachTo` records that the NEW post belongs with an existing request. **The
184
+ * post stays a post**: it keeps `kind: 'issue'`, it is still returned by
185
+ * `GET /v2/posts` and `GET /v2/posts/{id}`, it keeps its own votes, and its author
186
+ * does **not** become a supporter of the target — nothing about the target changes
187
+ * except that it now counts this post among the posts linked to it. The link is
188
+ * silent: no notification, no ack to the author. The post does **not** appear in
189
+ * `GET /v2/posts/{targetId}/insights` — that endpoint lists insights, and this is
190
+ * a post.
191
+ *
192
+ * `POST /v2/posts/{id}/link-insight` does the opposite: it files the post as a
193
+ * **quote** under the request. The post becomes an insight, leaves the posts
194
+ * resource (`GET /v2/posts/{id}` answers 404 afterwards), appears in
195
+ * `GET /v2/posts/{targetId}/insights`, and its author is rolled up as a supporter
196
+ * of the request.
197
+ *
198
+ * So: two customers asking for the same thing, and you want both to keep their own
199
+ * request row → `attachTo`. A sentence a customer said that is evidence for a
200
+ * request you already track → create the post, then call `link-insight` on it.
201
+ * Both take the same kind of target: an open request in this workspace.
202
+ *
203
+ * `attachTo` cannot be combined with `intakeMode: 'feedback'` (400
204
+ * `invalid_parameter` on `attachTo`): `attachTo` says where the text belongs, and
205
+ * `feedback` mode asks Featurebase to decide that for every ask the text contains.
206
+ * Pick one.
207
+ *
208
+ * The link is made AFTER the post is written, so an unusable target is a **422
209
+ * that says the post was created** — it is not rolled back. The target is refused
210
+ * when it does not exist in this workspace, is not a request (an insight or a
211
+ * record), was merged into another request, is a processed submission, is still
212
+ * awaiting moderation, is held as spam, or is the new post itself.
213
+ *
65
214
  * ### Backdating (Imports)
66
215
  *
67
216
  * - `createdAt` - Override creation date for importing historical data
68
217
  *
69
218
  * ### Response
70
219
  *
71
- * Returns the created post object with all fields populated.
220
+ * - **201** - the post was created. Returns the post object with all fields
221
+ * populated, plus `intakeMode` and `processing`.
222
+ * - **200** - a post already existed for this
223
+ * `(source.channel, source.externalId)`. Returns that post unchanged, with
224
+ * `deduped: true` and `processing.status: 'existing'`. A replay is answered from
225
+ * the existing post and never links anything a second time, whatever `attachTo`
226
+ * says.
227
+ * - **422** - `attachTo` named a request that cannot carry evidence (missing,
228
+ * merged, atomized, held for moderation or spam, not a request, or the post
229
+ * itself). **The post was still created**; the error message says so.
72
230
  *
73
231
  * @example
74
232
  * ```ts
75
- * const post = await client.feedback.posts.create({
76
- * boardId: '507f1f77bcf86cd799439011',
77
- * title: 'Add dark mode support',
78
- * });
233
+ * const post = await client.feedback.posts.create();
79
234
  * ```
80
235
  */
81
236
  create(params: PostCreateParams, options?: RequestOptions): APIPromise<Post> {
@@ -106,6 +261,46 @@ export class Posts extends APIResource {
106
261
  * - Engagement metrics
107
262
  * - Custom field values
108
263
  *
264
+ * ### Feedback processing
265
+ *
266
+ * For a post created with `intakeMode: 'feedback'`, the response also carries
267
+ * `processing`, which says how far Featurebase has got with the text and what it
268
+ * made of it. It is absent on every other post, and on the list endpoint.
269
+ *
270
+ * `processing.status`:
271
+ *
272
+ * - `queued` - the text was accepted and no result has landed yet
273
+ * - `processing` - Featurebase is organizing the submission right now
274
+ * - `complete` - it is done; `results` lists what came out of it
275
+ * - `needs_review` - it is done, and a teammate still has to decide something
276
+ * - `skipped` - nothing ran, and nothing will; `reason` says why (the same reason
277
+ * the create returned, for example `autopilot_off`). The post is stored exactly
278
+ * as it was sent.
279
+ *
280
+ * `processing.results` is present in workspaces that organize submissions, once
281
+ * the run has finished: one entry per ask found in the text, in the order they
282
+ * appear. Each entry has the ask's `title` and its `outcome`:
283
+ *
284
+ * - `request` - the ask became a request of its own
285
+ * - `attached` - it was added as evidence to a request that already existed
286
+ * - `held` - it became a request waiting for a teammate to publish it
287
+ * - `question` - it asked something; no request was created and a teammate answers
288
+ * it
289
+ *
290
+ * `postId` and `postTitle` name the request the ask became or was attached to.
291
+ * They are absent for a `question`, and absent when that request is not readable
292
+ * through this API.
293
+ *
294
+ * `held` results carry a `reason` code saying what the teammate still has to
295
+ * decide: `too_many_topics` (the submission held more separate asks than one
296
+ * submission can become requests, so this request lists the ones that did not
297
+ * fit), `uncertain`, `custom_fields` or `processing_failed` (the AI could not
298
+ * rewrite the text, or could not check the ask against existing requests, so a
299
+ * teammate confirms it). No other outcome carries it.
300
+ *
301
+ * Workspaces that do not organize submissions report `status` only: there the post
302
+ * you sent IS the request, so there is no separate result to list.
303
+ *
109
304
  * @example
110
305
  * ```ts
111
306
  * const post = await client.feedback.posts.retrieve(
@@ -228,6 +423,8 @@ export class Posts extends APIResource {
228
423
  * - `tags` - Filter by tag names (can be comma-separated or repeated)
229
424
  * - `q` - Search query for title/content
230
425
  * - `inReview` - Include posts pending moderation
426
+ * - `kind` - `issue` (default: work items) or `record`; insights are never served
427
+ * by this resource — use `/v2/insights`
231
428
  *
232
429
  * ### Sorting
233
430
  *
@@ -579,6 +776,13 @@ export interface Post {
579
776
 
580
777
  access: PostAccess;
581
778
 
779
+ /**
780
+ * When kind is 'insight', where exactly the insight points back into its origin:
781
+ * an insight source record with character ranges into its fullText, or the native
782
+ * conversation/message/comment/post ids.
783
+ */
784
+ anchor: Post.Anchor | null;
785
+
582
786
  /**
583
787
  * ID of the admin assigned to this post, null if unassigned
584
788
  */
@@ -618,11 +822,23 @@ export interface Post {
618
822
 
619
823
  features: PostFeatures;
620
824
 
825
+ /**
826
+ * When kind is 'insight', the triage grouping key (source record id, conversation
827
+ * id, origin post id, or the insight's own id for singletons). Legacy insights may
828
+ * be null and group as singletons.
829
+ */
830
+ groupKey: string | null;
831
+
621
832
  /**
622
833
  * Whether the post is pending moderation review
623
834
  */
624
835
  inReview: boolean;
625
836
 
837
+ /**
838
+ * Provenance of an insight: which channel it came from and how it was captured.
839
+ */
840
+ insightSource: Post.InsightSource | null;
841
+
626
842
  /**
627
843
  * Third-party integration links associated with this post
628
844
  */
@@ -633,6 +849,27 @@ export interface Post {
633
849
  */
634
850
  isPinned: boolean;
635
851
 
852
+ /**
853
+ * Discriminates an actionable work item ('issue') from a customer submission whose
854
+ * claims were extracted into insights ('record' — not a work item). Defaults to
855
+ * 'issue' for all pre-existing posts. Default list responses return issues only;
856
+ * pass kind='record' to opt in. Raw signal ('insight') is never returned by the
857
+ * posts resource — insights are served by /v2/insights.
858
+ */
859
+ kind: 'issue' | 'insight' | 'record';
860
+
861
+ /**
862
+ * Number of insights linked to this issue as supporting evidence. Only meaningful
863
+ * when kind is 'issue'.
864
+ */
865
+ linkedInsightCount: number;
866
+
867
+ /**
868
+ * When kind is 'insight', the ID of the issue this insight supports. Null when the
869
+ * insight is unlinked or when kind is 'issue'.
870
+ */
871
+ linkedIssueId: string | null;
872
+
636
873
  /**
637
874
  * Object type identifier
638
875
  */
@@ -643,6 +880,12 @@ export interface Post {
643
880
  */
644
881
  opportunityAmount: number | null;
645
882
 
883
+ /**
884
+ * True when the issue is hidden from portal/public surfaces. Missing stored values
885
+ * are returned as false.
886
+ */
887
+ portalHidden: boolean;
888
+
646
889
  /**
647
890
  * Full URL to view the post
648
891
  */
@@ -674,9 +917,82 @@ export interface Post {
674
917
  * Total number of upvotes
675
918
  */
676
919
  upvotes: number;
920
+
921
+ /**
922
+ * Present and true only on POST /v2/posts, when the request carried a
923
+ * `source.externalId` that already had a post. The existing post is returned
924
+ * unchanged with HTTP 200; a newly created post returns HTTP 201 without this
925
+ * field.
926
+ */
927
+ deduped?: true;
928
+
929
+ /**
930
+ * Present only on POST /v2/posts: the `intakeMode` the post was processed under
931
+ * ('request' when the request named none). On an idempotent replay
932
+ * (`deduped: true`) this is the mode the post was ORIGINALLY created with.
933
+ */
934
+ intakeMode?: 'request' | 'feedback';
935
+
936
+ /**
937
+ * On POST /v2/posts — `queued`: a processing run (claim extraction or the Organize
938
+ * rewrite) was enqueued and its result lands asynchronously on the post.
939
+ * `skipped`: nothing was enqueued; `reason` says which gate decided
940
+ * ('request_mode' for every intakeMode: 'request' create). `existing`: the create
941
+ * was an idempotent replay and the post was not processed again. On GET
942
+ * /v2/posts/{id} this field is present only for posts created with
943
+ * `intakeMode: 'feedback'` and reports how far that processing has got ('queued',
944
+ * 'processing', 'complete', 'needs_review', or 'skipped' with the same `reason`
945
+ * the create returned), with `results` listing what was made of the submission
946
+ * once the run has finished.
947
+ */
948
+ processing?: Post.Processing;
677
949
  }
678
950
 
679
951
  export namespace Post {
952
+ /**
953
+ * When kind is 'insight', where exactly the insight points back into its origin:
954
+ * an insight source record with character ranges into its fullText, or the native
955
+ * conversation/message/comment/post ids.
956
+ */
957
+ export interface Anchor {
958
+ commentId?: string | null;
959
+
960
+ conversationId?: string | null;
961
+
962
+ messageId?: string | null;
963
+
964
+ ranges?: Array<Anchor.Range> | null;
965
+
966
+ sourceRecordId?: string | null;
967
+
968
+ submissionId?: string | null;
969
+ }
970
+
971
+ export namespace Anchor {
972
+ export interface Range {
973
+ end: number;
974
+
975
+ start: number;
976
+ }
977
+ }
978
+
979
+ /**
980
+ * Provenance of an insight: which channel it came from and how it was captured.
981
+ */
982
+ export interface InsightSource {
983
+ channel: 'slack' | 'discord' | 'email' | 'support' | 'widget' | 'call' | 'api' | 'feedback';
984
+
985
+ capturedBy?: 'manual' | 'auto' | 'ai' | 'api' | null;
986
+
987
+ conversationId?: string | null;
988
+
989
+ externalId?: string | null;
990
+
991
+ sourceLabel?: string | null;
992
+
993
+ url?: string | null;
994
+ }
995
+
680
996
  /**
681
997
  * Third-party integration links associated with this post
682
998
  */
@@ -841,6 +1157,107 @@ export namespace Post {
841
1157
  objectType: 'Opportunity' | 'Case';
842
1158
  }
843
1159
  }
1160
+
1161
+ /**
1162
+ * On POST /v2/posts — `queued`: a processing run (claim extraction or the Organize
1163
+ * rewrite) was enqueued and its result lands asynchronously on the post.
1164
+ * `skipped`: nothing was enqueued; `reason` says which gate decided
1165
+ * ('request_mode' for every intakeMode: 'request' create). `existing`: the create
1166
+ * was an idempotent replay and the post was not processed again. On GET
1167
+ * /v2/posts/{id} this field is present only for posts created with
1168
+ * `intakeMode: 'feedback'` and reports how far that processing has got ('queued',
1169
+ * 'processing', 'complete', 'needs_review', or 'skipped' with the same `reason`
1170
+ * the create returned), with `results` listing what was made of the submission
1171
+ * once the run has finished.
1172
+ */
1173
+ export interface Processing {
1174
+ /**
1175
+ * 'queued' = an AI run was enqueued and has not landed yet; 'processing' = the run
1176
+ * is working on the submission; 'complete' = it finished and `results` lists what
1177
+ * it produced; 'needs_review' = it finished and a teammate still has to decide
1178
+ * something; 'skipped' = nothing was enqueued (see `reason`); 'existing' =
1179
+ * idempotent replay, not processed again.
1180
+ */
1181
+ status: 'queued' | 'processing' | 'complete' | 'needs_review' | 'skipped' | 'existing';
1182
+
1183
+ /**
1184
+ * Why processing was skipped. 'request_mode' = intakeMode was 'request' (the
1185
+ * default); 'autopilot_off' = the workspace's Autopilot dial is off or its plan
1186
+ * does not include it; 'pipeline_paused' = the platform paused AI processing (it
1187
+ * resumes automatically); 'support_board' / 'spam_held' / 'staff_authored' /
1188
+ * 'no_content' = the post did not qualify; 'source_too_long' = the text was over
1189
+ * the length limit when the run read it (a create that long is normally refused
1190
+ * with a 400 first); 'extraction_failed' = the AI could not read the text after
1191
+ * repeated attempts; nothing was written, and opening the post in Triage runs the
1192
+ * analysis again.
1193
+ */
1194
+ reason?:
1195
+ | 'request_mode'
1196
+ | 'autopilot_off'
1197
+ | 'insights_disabled'
1198
+ | 'pipeline_paused'
1199
+ | 'support_board'
1200
+ | 'spam_held'
1201
+ | 'staff_authored'
1202
+ | 'not_a_post'
1203
+ | 'no_board'
1204
+ | 'no_id'
1205
+ | 'no_content'
1206
+ | 'source_too_long'
1207
+ | 'extraction_failed'
1208
+ | 'hook_error';
1209
+
1210
+ /**
1211
+ * What the workspace's "Organize submissions" lane made of this submission, one
1212
+ * entry per ask it found, in the order they appear in the text. Present on GET
1213
+ * /v2/posts/{id} for posts created with `intakeMode: 'feedback'` once the run has
1214
+ * finished. Absent while the run is still working, and absent for workspaces that
1215
+ * do not organize submissions — there the post itself is the request.
1216
+ */
1217
+ results?: Array<Processing.Result>;
1218
+ }
1219
+
1220
+ export namespace Processing {
1221
+ export interface Result {
1222
+ /**
1223
+ * 'request' = the ask became a request of its own; 'attached' = it was added as
1224
+ * evidence to a request that already existed; 'held' = it became a request that is
1225
+ * waiting for a teammate to publish it; 'question' = it asked something and no
1226
+ * request was created, so a teammate answers it.
1227
+ */
1228
+ outcome: 'attached' | 'request' | 'question' | 'held';
1229
+
1230
+ /**
1231
+ * The ask, as it was written up.
1232
+ */
1233
+ title: string;
1234
+
1235
+ /**
1236
+ * The request this ask became, or was attached to. Absent for a question, and
1237
+ * absent when the request is not readable through this API (for example a ticket
1238
+ * board).
1239
+ */
1240
+ postId?: string;
1241
+
1242
+ /**
1243
+ * Title of `postId`.
1244
+ */
1245
+ postTitle?: string;
1246
+
1247
+ /**
1248
+ * Why a 'held' result is waiting for a teammate. Only on a 'held' result, and only
1249
+ * one of these codes: 'too_many_topics' = the submission held more separate asks
1250
+ * than one submission can become requests, so this one request lists the asks that
1251
+ * did not fit, one line each in the customer's words; 'uncertain' = the ask was
1252
+ * too vague to file without a person; 'custom_fields' = the board requires custom
1253
+ * fields this submission did not carry; 'processing_failed' = the AI could not
1254
+ * rewrite the text (the request then holds the original words) or could not check
1255
+ * this ask against existing requests, so a teammate confirms it is not a
1256
+ * duplicate.
1257
+ */
1258
+ reason?: 'too_many_topics' | 'uncertain' | 'custom_fields' | 'processing_failed';
1259
+ }
1260
+ }
844
1261
  }
845
1262
 
846
1263
  export interface PostAccess {
@@ -995,6 +1412,13 @@ export namespace PostSearchResponse {
995
1412
 
996
1413
  access: PostsAPI.PostAccess;
997
1414
 
1415
+ /**
1416
+ * When kind is 'insight', where exactly the insight points back into its origin:
1417
+ * an insight source record with character ranges into its fullText, or the native
1418
+ * conversation/message/comment/post ids.
1419
+ */
1420
+ anchor: Data.Anchor | null;
1421
+
998
1422
  /**
999
1423
  * ID of the admin assigned to this post, null if unassigned
1000
1424
  */
@@ -1034,11 +1458,23 @@ export namespace PostSearchResponse {
1034
1458
 
1035
1459
  features: PostsAPI.PostFeatures;
1036
1460
 
1461
+ /**
1462
+ * When kind is 'insight', the triage grouping key (source record id, conversation
1463
+ * id, origin post id, or the insight's own id for singletons). Legacy insights may
1464
+ * be null and group as singletons.
1465
+ */
1466
+ groupKey: string | null;
1467
+
1037
1468
  /**
1038
1469
  * Whether the post is pending moderation review
1039
1470
  */
1040
1471
  inReview: boolean;
1041
1472
 
1473
+ /**
1474
+ * Provenance of an insight: which channel it came from and how it was captured.
1475
+ */
1476
+ insightSource: Data.InsightSource | null;
1477
+
1042
1478
  /**
1043
1479
  * Third-party integration links associated with this post
1044
1480
  */
@@ -1049,6 +1485,27 @@ export namespace PostSearchResponse {
1049
1485
  */
1050
1486
  isPinned: boolean;
1051
1487
 
1488
+ /**
1489
+ * Discriminates an actionable work item ('issue') from a customer submission whose
1490
+ * claims were extracted into insights ('record' — not a work item). Defaults to
1491
+ * 'issue' for all pre-existing posts. Default list responses return issues only;
1492
+ * pass kind='record' to opt in. Raw signal ('insight') is never returned by the
1493
+ * posts resource — insights are served by /v2/insights.
1494
+ */
1495
+ kind: 'issue' | 'insight' | 'record';
1496
+
1497
+ /**
1498
+ * Number of insights linked to this issue as supporting evidence. Only meaningful
1499
+ * when kind is 'issue'.
1500
+ */
1501
+ linkedInsightCount: number;
1502
+
1503
+ /**
1504
+ * When kind is 'insight', the ID of the issue this insight supports. Null when the
1505
+ * insight is unlinked or when kind is 'issue'.
1506
+ */
1507
+ linkedIssueId: string | null;
1508
+
1052
1509
  /**
1053
1510
  * Object type identifier
1054
1511
  */
@@ -1059,6 +1516,12 @@ export namespace PostSearchResponse {
1059
1516
  */
1060
1517
  opportunityAmount: number | null;
1061
1518
 
1519
+ /**
1520
+ * True when the issue is hidden from portal/public surfaces. Missing stored values
1521
+ * are returned as false.
1522
+ */
1523
+ portalHidden: boolean;
1524
+
1062
1525
  /**
1063
1526
  * Full URL to view the post
1064
1527
  */
@@ -1090,9 +1553,82 @@ export namespace PostSearchResponse {
1090
1553
  * Total number of upvotes
1091
1554
  */
1092
1555
  upvotes: number;
1556
+
1557
+ /**
1558
+ * Present and true only on POST /v2/posts, when the request carried a
1559
+ * `source.externalId` that already had a post. The existing post is returned
1560
+ * unchanged with HTTP 200; a newly created post returns HTTP 201 without this
1561
+ * field.
1562
+ */
1563
+ deduped?: true;
1564
+
1565
+ /**
1566
+ * Present only on POST /v2/posts: the `intakeMode` the post was processed under
1567
+ * ('request' when the request named none). On an idempotent replay
1568
+ * (`deduped: true`) this is the mode the post was ORIGINALLY created with.
1569
+ */
1570
+ intakeMode?: 'request' | 'feedback';
1571
+
1572
+ /**
1573
+ * On POST /v2/posts — `queued`: a processing run (claim extraction or the Organize
1574
+ * rewrite) was enqueued and its result lands asynchronously on the post.
1575
+ * `skipped`: nothing was enqueued; `reason` says which gate decided
1576
+ * ('request_mode' for every intakeMode: 'request' create). `existing`: the create
1577
+ * was an idempotent replay and the post was not processed again. On GET
1578
+ * /v2/posts/{id} this field is present only for posts created with
1579
+ * `intakeMode: 'feedback'` and reports how far that processing has got ('queued',
1580
+ * 'processing', 'complete', 'needs_review', or 'skipped' with the same `reason`
1581
+ * the create returned), with `results` listing what was made of the submission
1582
+ * once the run has finished.
1583
+ */
1584
+ processing?: Data.Processing;
1093
1585
  }
1094
1586
 
1095
1587
  export namespace Data {
1588
+ /**
1589
+ * When kind is 'insight', where exactly the insight points back into its origin:
1590
+ * an insight source record with character ranges into its fullText, or the native
1591
+ * conversation/message/comment/post ids.
1592
+ */
1593
+ export interface Anchor {
1594
+ commentId?: string | null;
1595
+
1596
+ conversationId?: string | null;
1597
+
1598
+ messageId?: string | null;
1599
+
1600
+ ranges?: Array<Anchor.Range> | null;
1601
+
1602
+ sourceRecordId?: string | null;
1603
+
1604
+ submissionId?: string | null;
1605
+ }
1606
+
1607
+ export namespace Anchor {
1608
+ export interface Range {
1609
+ end: number;
1610
+
1611
+ start: number;
1612
+ }
1613
+ }
1614
+
1615
+ /**
1616
+ * Provenance of an insight: which channel it came from and how it was captured.
1617
+ */
1618
+ export interface InsightSource {
1619
+ channel: 'slack' | 'discord' | 'email' | 'support' | 'widget' | 'call' | 'api' | 'feedback';
1620
+
1621
+ capturedBy?: 'manual' | 'auto' | 'ai' | 'api' | null;
1622
+
1623
+ conversationId?: string | null;
1624
+
1625
+ externalId?: string | null;
1626
+
1627
+ sourceLabel?: string | null;
1628
+
1629
+ url?: string | null;
1630
+ }
1631
+
1096
1632
  /**
1097
1633
  * Third-party integration links associated with this post
1098
1634
  */
@@ -1257,31 +1793,136 @@ export namespace PostSearchResponse {
1257
1793
  objectType: 'Opportunity' | 'Case';
1258
1794
  }
1259
1795
  }
1796
+
1797
+ /**
1798
+ * On POST /v2/posts — `queued`: a processing run (claim extraction or the Organize
1799
+ * rewrite) was enqueued and its result lands asynchronously on the post.
1800
+ * `skipped`: nothing was enqueued; `reason` says which gate decided
1801
+ * ('request_mode' for every intakeMode: 'request' create). `existing`: the create
1802
+ * was an idempotent replay and the post was not processed again. On GET
1803
+ * /v2/posts/{id} this field is present only for posts created with
1804
+ * `intakeMode: 'feedback'` and reports how far that processing has got ('queued',
1805
+ * 'processing', 'complete', 'needs_review', or 'skipped' with the same `reason`
1806
+ * the create returned), with `results` listing what was made of the submission
1807
+ * once the run has finished.
1808
+ */
1809
+ export interface Processing {
1810
+ /**
1811
+ * 'queued' = an AI run was enqueued and has not landed yet; 'processing' = the run
1812
+ * is working on the submission; 'complete' = it finished and `results` lists what
1813
+ * it produced; 'needs_review' = it finished and a teammate still has to decide
1814
+ * something; 'skipped' = nothing was enqueued (see `reason`); 'existing' =
1815
+ * idempotent replay, not processed again.
1816
+ */
1817
+ status: 'queued' | 'processing' | 'complete' | 'needs_review' | 'skipped' | 'existing';
1818
+
1819
+ /**
1820
+ * Why processing was skipped. 'request_mode' = intakeMode was 'request' (the
1821
+ * default); 'autopilot_off' = the workspace's Autopilot dial is off or its plan
1822
+ * does not include it; 'pipeline_paused' = the platform paused AI processing (it
1823
+ * resumes automatically); 'support_board' / 'spam_held' / 'staff_authored' /
1824
+ * 'no_content' = the post did not qualify; 'source_too_long' = the text was over
1825
+ * the length limit when the run read it (a create that long is normally refused
1826
+ * with a 400 first); 'extraction_failed' = the AI could not read the text after
1827
+ * repeated attempts; nothing was written, and opening the post in Triage runs the
1828
+ * analysis again.
1829
+ */
1830
+ reason?:
1831
+ | 'request_mode'
1832
+ | 'autopilot_off'
1833
+ | 'insights_disabled'
1834
+ | 'pipeline_paused'
1835
+ | 'support_board'
1836
+ | 'spam_held'
1837
+ | 'staff_authored'
1838
+ | 'not_a_post'
1839
+ | 'no_board'
1840
+ | 'no_id'
1841
+ | 'no_content'
1842
+ | 'source_too_long'
1843
+ | 'extraction_failed'
1844
+ | 'hook_error';
1845
+
1846
+ /**
1847
+ * What the workspace's "Organize submissions" lane made of this submission, one
1848
+ * entry per ask it found, in the order they appear in the text. Present on GET
1849
+ * /v2/posts/{id} for posts created with `intakeMode: 'feedback'` once the run has
1850
+ * finished. Absent while the run is still working, and absent for workspaces that
1851
+ * do not organize submissions — there the post itself is the request.
1852
+ */
1853
+ results?: Array<Processing.Result>;
1854
+ }
1855
+
1856
+ export namespace Processing {
1857
+ export interface Result {
1858
+ /**
1859
+ * 'request' = the ask became a request of its own; 'attached' = it was added as
1860
+ * evidence to a request that already existed; 'held' = it became a request that is
1861
+ * waiting for a teammate to publish it; 'question' = it asked something and no
1862
+ * request was created, so a teammate answers it.
1863
+ */
1864
+ outcome: 'attached' | 'request' | 'question' | 'held';
1865
+
1866
+ /**
1867
+ * The ask, as it was written up.
1868
+ */
1869
+ title: string;
1870
+
1871
+ /**
1872
+ * The request this ask became, or was attached to. Absent for a question, and
1873
+ * absent when the request is not readable through this API (for example a ticket
1874
+ * board).
1875
+ */
1876
+ postId?: string;
1877
+
1878
+ /**
1879
+ * Title of `postId`.
1880
+ */
1881
+ postTitle?: string;
1882
+
1883
+ /**
1884
+ * Why a 'held' result is waiting for a teammate. Only on a 'held' result, and only
1885
+ * one of these codes: 'too_many_topics' = the submission held more separate asks
1886
+ * than one submission can become requests, so this one request lists the asks that
1887
+ * did not fit, one line each in the customer's words; 'uncertain' = the ask was
1888
+ * too vague to file without a person; 'custom_fields' = the board requires custom
1889
+ * fields this submission did not carry; 'processing_failed' = the AI could not
1890
+ * rewrite the text (the request then holds the original words) or could not check
1891
+ * this ask against existing requests, so a teammate confirms it is not a
1892
+ * duplicate.
1893
+ */
1894
+ reason?: 'too_many_topics' | 'uncertain' | 'custom_fields' | 'processing_failed';
1895
+ }
1896
+ }
1260
1897
  }
1261
1898
  }
1262
1899
 
1263
1900
  export interface PostCreateParams {
1264
1901
  /**
1265
- * Body param: Board ID to create post in
1902
+ * Body param: Admin ID to assign this post to
1266
1903
  */
1267
- boardId: string;
1904
+ assigneeId?: string;
1268
1905
 
1269
1906
  /**
1270
- * Body param: Post title
1907
+ * Body param: Id of an existing open request this post is evidence for. The post
1908
+ * is created, then linked.
1271
1909
  */
1272
- title: string;
1910
+ attachTo?: string;
1273
1911
 
1274
1912
  /**
1275
- * Body param: Admin ID to assign this post to
1913
+ * Body param: Author to attribute the post to. If not provided, uses the
1914
+ * authenticated user — unless `source` is given, in which case a guest author is
1915
+ * synthesised from `source.label` (or the channel name), because a relayed request
1916
+ * belongs to the customer who said it. Supports multiple identification methods:
1917
+ * id (Featurebase ID), userId (external SSO ID), or email.
1276
1918
  */
1277
- assigneeId?: string;
1919
+ author?: TicketsAPI.AuthorInput;
1278
1920
 
1279
1921
  /**
1280
- * Body param: Author to attribute the post to. If not provided, uses the
1281
- * authenticated user. Supports multiple identification methods: id (Featurebase
1282
- * ID), userId (external SSO ID), or email.
1922
+ * Body param: Board ID to create the post in. Omit to use the default board of the
1923
+ * organization.
1283
1924
  */
1284
- author?: TicketsAPI.AuthorInput;
1925
+ boardId?: string;
1285
1926
 
1286
1927
  /**
1287
1928
  * Body param: Whether comments are enabled on this post
@@ -1289,7 +1930,9 @@ export interface PostCreateParams {
1289
1930
  commentsEnabled?: boolean | null;
1290
1931
 
1291
1932
  /**
1292
- * Body param: Post content (HTML)
1933
+ * Body param: Post content (HTML). Required when `intakeMode` is 'feedback' and no
1934
+ * title is given. In 'feedback' mode its plain text is limited by `source.channel`
1935
+ * (see `intakeMode`); longer content is rejected with a 400.
1293
1936
  */
1294
1937
  content?: string;
1295
1938
 
@@ -1315,6 +1958,23 @@ export interface PostCreateParams {
1315
1958
  */
1316
1959
  inReview?: boolean | null;
1317
1960
 
1961
+ /**
1962
+ * Body param: What the text is. 'request' (default): a finished request — stored
1963
+ * exactly as supplied, no AI claim extraction, regardless of author. 'feedback':
1964
+ * raw customer feedback — processed like a portal post (organized into a request,
1965
+ * or its claims extracted and matched against existing requests) under the
1966
+ * workspace's Autopilot dial, plan, AI budget and moderation settings. In
1967
+ * 'feedback' mode `title` is optional (the first line of `content` becomes it),
1968
+ * `statusId` and `eta` are rejected (400) because raw feedback has no decision
1969
+ * yet, and text is read in full up to the limit of its `source.channel` — 120,000
1970
+ * characters of plain text for 'call' (a two-hour transcript), 60,000 for 'email'
1971
+ * and 'api', 30,000 for 'slack' and 'discord'; without a `source.channel` the
1972
+ * 'api' limit applies — while anything longer is rejected (400) rather than
1973
+ * trimmed. Long text takes tens of minutes rather than seconds to process, and one
1974
+ * submission yields at most 30 requests.
1975
+ */
1976
+ intakeMode?: 'request' | 'feedback';
1977
+
1318
1978
  /**
1319
1979
  * Body param: Push the created post to third-party integrations configured on your
1320
1980
  * organization. Each integration must be explicitly set to true to trigger;
@@ -1322,6 +1982,12 @@ export interface PostCreateParams {
1322
1982
  */
1323
1983
  integrations?: PostCreateParams.Integrations;
1324
1984
 
1985
+ /**
1986
+ * Body param: Create this request on the internal board. Never visible on the
1987
+ * portal.
1988
+ */
1989
+ internal?: boolean;
1990
+
1325
1991
  /**
1326
1992
  * Body param: Whether to send email notifications to admins when this post is
1327
1993
  * created. When true, admins will receive the same email notifications as when a
@@ -1329,6 +1995,20 @@ export interface PostCreateParams {
1329
1995
  */
1330
1996
  notifyAdmins?: boolean;
1331
1997
 
1998
+ /**
1999
+ * Body param: When true, hides the issue from portal/public surfaces. Omitted or
2000
+ * false is visible.
2001
+ */
2002
+ portalHidden?: boolean;
2003
+
2004
+ /**
2005
+ * Body param: Provenance of this request, and the idempotency key for the create.
2006
+ * With `source` and no `author`, the post is attributed to a guest named after
2007
+ * `label` (or the channel) so it belongs to the customer who said it, not to the
2008
+ * API key.
2009
+ */
2010
+ source?: PostCreateParams.Source;
2011
+
1332
2012
  /**
1333
2013
  * Body param: Status ID to set
1334
2014
  */
@@ -1339,6 +2019,13 @@ export interface PostCreateParams {
1339
2019
  */
1340
2020
  tags?: string | Array<string>;
1341
2021
 
2022
+ /**
2023
+ * Body param: Post title. Required unless `intakeMode` is 'feedback', where raw
2024
+ * customer text rarely has one: omit it (or send it blank) and the first line of
2025
+ * `content` becomes the title, cut to 120 characters.
2026
+ */
2027
+ title?: string;
2028
+
1342
2029
  /**
1343
2030
  * Body param: Initial upvotes count. Defaults to 1 (post author is automatically
1344
2031
  * added as voter). Use 0 to create a post without any votes.
@@ -1396,6 +2083,45 @@ export namespace PostCreateParams {
1396
2083
  */
1397
2084
  slack?: boolean;
1398
2085
  }
2086
+
2087
+ /**
2088
+ * Provenance of this request, and the idempotency key for the create. With
2089
+ * `source` and no `author`, the post is attributed to a guest named after `label`
2090
+ * (or the channel) so it belongs to the customer who said it, not to the API key.
2091
+ */
2092
+ export interface Source {
2093
+ /**
2094
+ * Channel the request arrived on. 'feedback', 'widget' and 'support' are reserved
2095
+ * for posts authored on the portal, the widget and the inbox, and are rejected
2096
+ * with a 400. With `intakeMode: 'feedback'` the channel also sets how much text is
2097
+ * read: 'call' 120,000 characters, 'email' and 'api' 60,000, 'slack' and 'discord'
2098
+ * 30,000.
2099
+ */
2100
+ channel: 'slack' | 'discord' | 'email' | 'support' | 'widget' | 'call' | 'api' | 'feedback';
2101
+
2102
+ /**
2103
+ * Stable id of the message/record in the source system. Sending the same (channel,
2104
+ * externalId) twice returns the post created the first time instead of a duplicate
2105
+ * (HTTP 200 with `deduped: true`). Stored namespaced as `api:<externalId>`.
2106
+ */
2107
+ externalId: string;
2108
+
2109
+ /**
2110
+ * Id of the support conversation this request came from
2111
+ */
2112
+ conversationId?: string;
2113
+
2114
+ /**
2115
+ * Display name of the specific source. Also names the guest author when no
2116
+ * `author` is given.
2117
+ */
2118
+ label?: string;
2119
+
2120
+ /**
2121
+ * Deep link back to the original message/record
2122
+ */
2123
+ url?: string;
2124
+ }
1399
2125
  }
1400
2126
 
1401
2127
  export interface PostRetrieveParams {
@@ -1414,8 +2140,10 @@ export interface PostUpdateParams {
1414
2140
 
1415
2141
  /**
1416
2142
  * Body param: Author to attribute the post to. If not provided, uses the
1417
- * authenticated user. Supports multiple identification methods: id (Featurebase
1418
- * ID), userId (external SSO ID), or email.
2143
+ * authenticated user unless `source` is given, in which case a guest author is
2144
+ * synthesised from `source.label` (or the channel name), because a relayed request
2145
+ * belongs to the customer who said it. Supports multiple identification methods:
2146
+ * id (Featurebase ID), userId (external SSO ID), or email.
1419
2147
  */
1420
2148
  author?: TicketsAPI.AuthorInput;
1421
2149
 
@@ -1507,6 +2235,13 @@ export interface PostListParams extends CursorPageParams {
1507
2235
  */
1508
2236
  inReview?: boolean | null;
1509
2237
 
2238
+ /**
2239
+ * Query param: Filter by submission kind. Omitted or 'issue' returns work items
2240
+ * only. Pass 'record' for customer submissions whose claims were extracted into
2241
+ * insights. Insights are never served by this resource — use /v2/insights.
2242
+ */
2243
+ kind?: 'issue' | 'record';
2244
+
1510
2245
  /**
1511
2246
  * Query param: Search query to filter posts by title/content
1512
2247
  */
@@ -1645,6 +2380,7 @@ export interface PostUnmergeParams {
1645
2380
  }
1646
2381
 
1647
2382
  Posts.Voters = Voters;
2383
+ Posts.Insights = Insights;
1648
2384
 
1649
2385
  export declare namespace Posts {
1650
2386
  export {
@@ -1678,4 +2414,12 @@ export declare namespace Posts {
1678
2414
  type VoterAddParams as VoterAddParams,
1679
2415
  type VoterRemoveParams as VoterRemoveParams,
1680
2416
  };
2417
+
2418
+ export {
2419
+ Insights as Insights,
2420
+ type InsightListParams as InsightListParams,
2421
+ type InsightConvertToPostParams as InsightConvertToPostParams,
2422
+ type InsightLinkParams as InsightLinkParams,
2423
+ type InsightUnlinkParams as InsightUnlinkParams,
2424
+ };
1681
2425
  }