ugcinc 4.19.0 → 4.20.0

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.
package/README.md CHANGED
@@ -43,7 +43,7 @@ if (res.ok) {
43
43
 
44
44
  - `client.accounts`: list, create, update, troubleshoot, quarantine/release, and manage account lifecycle
45
45
  - `client.posts`: create video/slideshow posts, update them, retry failures, and preview schedule conflicts
46
- - `client.media`: upload media, create media records, manage tags/names, and work with social audio
46
+ - `client.media`: upload media, create media records, search profile-picture candidates, search images and videos by keyword (`media.imageSearch`, cursor-paged), manage tags/names, and work with social audio
47
47
  - `client.stats`: fetch account/post analytics, daily aggregates, top performers, and refresh stats
48
48
  - `client.org`: manage organizations, API keys, and integration keys
49
49
  - `client.billing`: inspect subscription state and handle account deactivation, replacements, and refunds
@@ -74,6 +74,20 @@ if (posts.ok) {
74
74
  }
75
75
  ```
76
76
 
77
+ ## Posting failure quick reference
78
+
79
+ `posts.getStatus()` returns `failure`; both `getPosts()` and `getPostsPage()` include it on each post. It is `null` when there is no current posting issue. A successful lookup still returns HTTP 200 even if the post failed.
80
+
81
+ ```typescript
82
+ const result = await client.posts.getStatus({ postId: "post_123" });
83
+ if (result.ok && result.data.failure) {
84
+ const { code, message, recovery, next_action } = result.data.failure;
85
+ console.log(code, message, recovery, next_action);
86
+ }
87
+ ```
88
+
89
+ See the posting failure reference below for recovery states and messages.
90
+
77
91
  ## Pagination
78
92
 
79
93
  `accounts.getAccounts()` and `posts.getPosts()` support keyset pagination via `limit`/`cursor`.
@@ -93,6 +107,28 @@ do {
93
107
  } while (cursor);
94
108
  ```
95
109
 
110
+ For interactive tables, `posts.getPostsPage()` keeps filtering and sorting on the server and
111
+ returns page-scoped latest stats plus `totalCount`, organization-wide `postTags`,
112
+ `anyHasTitle`, and `matchingAccountIds`. It supports caption/title/account search; platform,
113
+ post type, status, account, account-tag, post-tag, social-audio, and phone-type filters; and
114
+ sorting by account, status, title, caption, tag, audio presence, scheduled time, or engagement:
115
+
116
+ ```typescript
117
+ const page = await client.posts.getPostsPage({
118
+ search: "launch",
119
+ statuses: ["complete"],
120
+ postTags: ["fall-campaign"],
121
+ sortBy: "views",
122
+ sortDirection: "desc",
123
+ limit: 200,
124
+ });
125
+
126
+ if (page.ok) {
127
+ console.log(page.data.posts, page.data.stats, page.data.totalCount);
128
+ const next = page.data.nextCursor;
129
+ }
130
+ ```
131
+
96
132
  ## Caption Overlays
97
133
 
98
134
  Video posts can carry text overlays to display on the video. Pass `captionOverlays` to
@@ -144,6 +180,28 @@ await client.posts.createVideo({
144
180
  await client.posts.updatePost({ postId: "post_123", post_tag: "campaign-august" });
145
181
  ```
146
182
 
183
+ When a post fails, its category is appended to the tag as `fail reason: <fail_category>` (for
184
+ example `campaign-july fail reason: device_error`), so a post list shows why without a status call
185
+ per post. The suffix is removed again when the post is retried.
186
+
187
+ ## Backup Audio
188
+
189
+ A post can carry a second sound. If the platform refuses the primary sound at posting time, the
190
+ backup becomes the post's `social_audio_id`, the post is re-attempted once, ` switched to backup
191
+ audio` is appended to its `tag`, and `backup_audio_used_at` is set. With no backup left the post
192
+ fails as `audio_unavailable` and is not retried. Set it with `backupSocialAudioId` on
193
+ `posts.createVideo()`, `posts.createSlideshow()`, `posts.createDraft()`, and `posts.updatePost()`
194
+ (`null` clears it on update).
195
+
196
+ ```typescript
197
+ await client.posts.createSlideshow({
198
+ accountId: "acc_123",
199
+ imageUrls: ["https://example.com/img1.jpg"],
200
+ socialAudioId: "audio_primary",
201
+ backupSocialAudioId: "audio_fallback",
202
+ });
203
+ ```
204
+
147
205
  ## Useful Exports
148
206
 
149
207
  - `UGCClient` for API access
@@ -162,6 +220,33 @@ For the full endpoint reference, all method signatures, data structures, and wor
162
220
 
163
221
  MIT
164
222
 
223
+ ## Posting failure reference
224
+
225
+ The same `failure` object is returned by status and list responses. It contains a stable `code`, a fixed `message`, a `recovery` state, and `next_action`.
226
+
227
+ | Recovery | Meaning |
228
+ |---|---|
229
+ | `retry_pending` | The existing post is waiting for another attempt; no execution time is guaranteed. |
230
+ | `waiting` | The existing post is paused until a publishing issue is resolved. |
231
+ | `verifying_publication` | Publication is being checked before another attempt is allowed. |
232
+ | `stopped` | No automatic recovery is indicated. Follow `next_action`. |
233
+
234
+ `next_action` is `wait`, `change_audio`, `change_audio_or_contact_support`, or `contact_support`. When it is `wait`, do not submit another copy. Account and publishing operations are handled by the service; these messages never ask customers to manage them.
235
+
236
+ | Code | Message |
237
+ |---|---|
238
+ | `AUDIO_UNAVAILABLE` | The selected sound is unavailable. Choose another sound and retry. |
239
+ | `BACKUP_AUDIO_RETRY_PENDING` | The selected sound was unavailable. Your post is waiting to be retried with its backup sound. |
240
+ | `AUDIO_RETRY_PENDING` | The selected sound could not be loaded. Your post is waiting to be retried. |
241
+ | `AUDIO_PREPARATION_FAILED` | The selected sound could not be prepared for this post. Choose another sound or contact support before retrying. |
242
+ | `PUBLISHING_RETRY_PENDING` | Publishing was temporarily interrupted. Your post is waiting to be retried. No action is needed. |
243
+ | `PUBLISHING_BLOCKED` | A publishing issue prevented this post from proceeding. Contact support for help with this post. |
244
+ | `POST_PROCESSING_FAILED` | A processing error prevented this posting attempt from completing. Contact support before retrying. |
245
+ | `PUBLICATION_CHECK_IN_PROGRESS` | We’re checking whether your post published. Please wait before submitting it again to avoid a duplicate. |
246
+ | `PUBLICATION_UNCONFIRMED` | We couldn’t confirm whether your post published. Contact support before submitting it again to avoid a duplicate. |
247
+ | `POST_RETRY_PENDING` | The previous posting attempt did not complete. Your post is waiting to be retried. No action is needed. |
248
+ | `POST_FAILED` | The posting attempt failed, but we couldn’t determine the cause. Contact support before submitting it again. |
249
+
165
250
  ### Render a supplied Remotion composition
166
251
 
167
252
  `remotion-composition` accepts a TSX module whose default export is a React
@@ -196,11 +281,43 @@ status/logs to inspect failures and the returned video for preview.
196
281
  Server integrations can also call `submitRemotionCompositionRenderJob` and
197
282
  `getRenderJobStatus` with the configured render-worker origin.
198
283
 
199
- ### Remotion composition PNG output
284
+ ## Social reference acquisition
285
+
286
+ Quick reference:
287
+
288
+ ```ts
289
+ const page = await client.media.listProfilePosts({ url: profileUrl, limit: 20 });
290
+ const post = await client.media.downloadSocialPost({ url: postUrl });
291
+ ```
200
292
 
201
- The `remotion-composition` workflow node accepts `outputType: "image"` to render
202
- the first composition frame as a PNG. Its output port becomes `image`; video
203
- remains the default for existing workflows. Pass sound assets separately for
204
- photo posts because PNG files contain no audio. The public render helper
205
- `submitRemotionCompositionRenderJob({...composition, outputType: "image"})`
206
- supports the same option.
293
+ `media.listProfilePosts({url, limit?, cursor?})` calls `POST /media/social/list-profile-posts`.
294
+ Supported profiles are TikTok and Instagram. Limit defaults to 20 (1–50). Results include
295
+ `profile`, `posts`, `nextCursor`, `coverage` and `warnings`. Pass `nextCursor` back with
296
+ the same profile URL. Cursors expire after one hour and belong to the authenticated
297
+ organization. Short pages may still have a cursor. Instagram combines posts and reels;
298
+ ordering follows those feeds, not a guaranteed chronological snapshot. `exhausted`
299
+ means the available feeds ended, not that all historical posts are visible. Thumbnail
300
+ URLs are upstream references and may expire. Listing does not import media.
301
+
302
+ `media.downloadSocialPost({url})` calls `POST /media/social/download-post` until the
303
+ checkpointed import finishes. Inputs are TikTok video/photo or Instagram post/reel
304
+ URLs, including supported share redirects. The raw HTTP endpoint returns `processing`
305
+ while another continuation is needed; call it again with the same URL. The SDK/tool
306
+ handles these calls. Imports have `complete`, `partial` or `failed` status, ordered
307
+ `assets`, `failedAssets`, post metadata, sound attribution, provenance and an owned
308
+ JSON `manifestUrl`. Assets contain owned URLs, media IDs, SHA-256 hashes, probed
309
+ format/dimensions/duration and embedded audio presence. A successful API response can
310
+ contain a partial/failed import; check `data.status` and `data.failedAssets`.
311
+
312
+ Repeat a partial/failed import to retry missing assets. Successful imports are reused
313
+ within the organization while their records/files remain available; import state is
314
+ retained for 30 days. Original downloaded video bytes/audio are preserved. Photo
315
+ carousels stay ordered images: this does not generate a slideshow video or download
316
+ an attributed full music track. Absent metadata is null. Supported acquired formats
317
+ are JPEG/PNG/WebP images and MP4/WebM video, limited to 50 assets, 100 MB per asset
318
+ and 30 minutes per video. Limits are checked before accepting an asset. No guarantee
319
+ of original upload quality or watermark removal is made.
320
+
321
+ Both operations require an organization and `content:analyze` for scoped keys.
322
+ Errors may include `errorCode` and `retryable`. Existing scraper credit/rate safeguards
323
+ apply. Storage/provider errors do not imply that a post or profile was deleted.
@@ -96,6 +96,8 @@ export interface EditProfileInfo {
96
96
  }
97
97
  export interface GetAccountsParams {
98
98
  tag?: string;
99
+ /** Platform filter: only accounts of this type are returned. */
100
+ type?: 'tiktok' | 'instagram';
99
101
  org_group?: string;
100
102
  user_group?: string;
101
103
  status?: AccountStatus;
package/dist/base.js CHANGED
@@ -52,6 +52,8 @@ class BaseClient {
52
52
  ok: false,
53
53
  code: rawData.code,
54
54
  message: rawData.message ?? 'Request failed',
55
+ ...(typeof rawData.errorCode === 'string' ? { errorCode: rawData.errorCode } : {}),
56
+ ...(typeof rawData.retryable === 'boolean' ? { retryable: rawData.retryable } : {}),
55
57
  };
56
58
  }
57
59
  }
package/dist/index.d.ts CHANGED
@@ -30,10 +30,10 @@ export type { InputType } from './automations/nodes/types';
30
30
  export type { ClientConfig } from './base';
31
31
  export type { Account, AccountStatus, AccountStat, AccountTask, QuarantineAccountParams, ReleaseAccountParams, QuarantineAccountResponse, EditProfileInfo, GetAccountsParams, GetAccountStatsParams, GetAccountStatusParams, AccountInfoUpdate, UpdateAccountInfoParams, AccountInfoUpdateResult, UpdateAccountInfoResponse, AccountSocialUpdate, UpdateAccountSocialParams, AccountSocialUpdateResult, UpdateAccountSocialResponse, DeleteAccountPostsParams, DeleteAccountPostsResponse, ResetWarmupParams, ResetWarmupResponse, NicheSwitchUpdate, NicheSwitchParams, NicheSwitchResult, NicheSwitchResponse, CreateAccountInput, CreateAccountsParams, CreateAccountResult, CreateAccountsResponse, TroubleshootFailReason, TroubleshootAccount, TroubleshootParams, } from './accounts';
32
32
  export type { TaskType, Task, GetTasksParams } from './tasks';
33
- export type { PostType, PostStatus, Post, PostStat, CaptionOverlay, GetPostsParams, CreateDraftParams, CreateSlideshowParams, GetPostStatsParams, GetPostStatusParams, CreateVideoParams, UpdatePostParams, DeletePostsParams, DeletePostsResponse, RetryPostsParams, SetPostStatusParams, SetPostStatusResponse, PreviewScheduleEntry, PreviewScheduleParams, PreviewScheduleResult, } from './posts';
33
+ export type { PostType, PostStatus, Post, PostStat, CaptionOverlay, GetPostsParams, GetPostsPageParams, PostsPage, PostSortField, PostSortDirection, CreateDraftParams, CreateSlideshowParams, GetPostStatsParams, GetPostStatusParams, CreateVideoParams, UpdatePostParams, DeletePostsParams, DeletePostsResponse, RetryPostsParams, SetPostStatusParams, SetPostStatusResponse, PreviewScheduleEntry, PreviewScheduleParams, PreviewScheduleResult, } from './posts';
34
34
  export type { RefreshStatsParams, RefreshStatsError, RefreshStatsResponse, RefreshStatsProgressResponse, RefreshStartEvent, RefreshProgressEvent, RefreshDoneEvent, RefreshStreamEvent, DailyAggregatedStat, GetDailyAggregatedStatsParams, DailyAccountStat, GetDailyAccountStatsParams, DailyPostStat, GetDailyPostStatsParams, DashboardDailyStat, GetDashboardDailyStatsParams, TopAccount, GetTopAccountsParams, TopPost, GetTopPostsParams, } from './stats';
35
35
  export type { Org, ApiKey, DeleteApiKeyParams, EditApiKeyParams, IntegrationKey, IntegrationProvider, UpsertIntegrationKeyParams, DeleteIntegrationKeyParams } from './org';
36
- export type { UserMedia, MediaUse, SocialAudio, Media, GetMediaParams, GetSocialAudioParams, UploadMediaParams, UploadMediaResponse, MediaTagUpdate, UpdateMediaTagsParams, MediaTagUpdateResult, UpdateMediaTagsResponse, UpdateMediaTagParams, UpdateMediaNameParams, DeleteMediaParams, DeleteMediaResponse, CreateSocialAudioParams, ImportTextParams, ImportTextResponse, CreateMediaFromUrlParams, GetMediaUseParams, GetMediaUseResponse, FilterMediaParams, FilterMediaResponse, } from './media';
36
+ export type { UserMedia, MediaUse, SocialAudio, Media, GetMediaParams, GetSocialAudioParams, UploadMediaParams, UploadMediaResponse, MediaTagUpdate, UpdateMediaTagsParams, MediaTagUpdateResult, UpdateMediaTagsResponse, UpdateMediaTagParams, UpdateMediaNameParams, DeleteMediaParams, DeleteMediaResponse, CreateSocialAudioParams, ImportTextParams, ImportTextResponse, CreateMediaFromUrlParams, GetMediaUseParams, GetMediaUseResponse, FilterMediaParams, FilterMediaResponse, PfpSearchParams, PfpSearchResponse, PfpCandidate, ImageSearchParams, ImageSearchResult, ImageSearchResponse, ImageSearchVideoFormat, } from './media';
37
37
  export type { CommentStatus, Comment, CreateCommentParams, CreateCommentResponse, GetCommentsParams, } from './comments';
38
38
  export type { BillingInfo, PortalUrlResponse, CancelAccountParams, CancelAccountResponse, ReactivateAccountParams, ReactivateAccountResponse, CancelSubscriptionResponse, RequestReplacementParams, RequestReplacementResponse, RequestRefundParams, RequestRefundResponse, BillingRequestInfo, PortalParams, } from './billing';
39
39
  export type { RenderJobResponse, RenderJobStatus, SubmitImageRenderJobParams, SubmitVideoRenderJobParams, SubmitScreenshotAnimationRenderJobParams, SubmitAutoCaptionRenderJobParams, SubmitSceneSplitJobParams, SceneSplitDetectorConfig, SubmitInstagramDmRenderJobParams, SubmitIMessageDmRenderJobParams, IgDmMessage, ImDmMessage, RenderVideoEditorConfig, } from './render';
@@ -84,3 +84,5 @@ export type { NodeDefinition, NodeType, UserCreatableNodeType, NodeConfig, Workf
84
84
  export type { SelectionMode, ExhaustionBehavior, SelectionConfig, SelectionState, OutputMode, NodeCategory, TriggerIterationMode, IterationExhaustionBehavior, CollectionSelectionMode, TriggerCollectionConfig, MediaItemType, MediaNodeSelectionType, ResolvedPreview, PreviewSelectionState, PreviewContext, NullableOutputs, } from './automations/nodes/types';
85
85
  export type { SuccessResponse, ErrorResponse, ApiResponse, } from './types';
86
86
  export type { CropBoundary, CropAxisConfig, DynamicCropConfig, BorderRadiusConfig, VerticalAnchor, HorizontalAnchor, HorizontalSelfAnchor, VerticalSelfAnchor, RelativePositionConfigX, RelativePositionConfigY, } from './render/types';
87
+ export type { PublicPostFailureCode, PublicPostRecovery, PublicPostNextAction, PublicPostFailure } from './post-failure';
88
+ export type { ListProfilePostsParams, ListProfilePostsResult, DownloadSocialPostParams, DownloadSocialPostResult, SocialReferencePost, SocialReferenceAsset } from './social-acquisition';
package/dist/media.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { BaseClient } from './base';
2
+ import type { ListProfilePostsParams, ListProfilePostsResult, DownloadSocialPostParams, DownloadSocialPostResult } from './social-acquisition';
2
3
  import type { ApiResponse } from './types';
3
4
  export interface UserMedia {
4
5
  id: string;
@@ -131,6 +132,57 @@ export interface FilterMediaResponse {
131
132
  used_count: number;
132
133
  available_count: number;
133
134
  }
135
+ export interface PfpSearchParams {
136
+ /** What to search for, e.g. "black girl selfie" or "asian woman aesthetic". */
137
+ search_term: string;
138
+ /** How many pictures to return. Defaults to 12, capped at 40. */
139
+ count?: number;
140
+ }
141
+ export interface PfpCandidate {
142
+ /** Stable id for this picture at the source. */
143
+ source_id: string;
144
+ /** Full-resolution image — the one an account's avatar is set to. */
145
+ url: string;
146
+ /** Smaller variant, for rendering a grid of options. */
147
+ preview_url: string;
148
+ }
149
+ export interface PfpSearchResponse {
150
+ pictures: PfpCandidate[];
151
+ search_term: string;
152
+ count: number;
153
+ }
154
+ export interface ImageSearchParams {
155
+ /** What to search for. */
156
+ search_term: string;
157
+ /** How many results to return. Defaults to 24, min 1, capped at 50. */
158
+ count?: number;
159
+ /** `next_cursor` of a previous response for the same `search_term`. */
160
+ cursor?: string;
161
+ }
162
+ export type ImageSearchVideoFormat = 'mp4' | 'hls' | 'gif';
163
+ export interface ImageSearchResult {
164
+ /** Stable id for this result at the source. */
165
+ source_id: string;
166
+ title: string;
167
+ /** Full-resolution image; the cover image for a video result. */
168
+ url: string;
169
+ /** Smaller variant, for rendering a grid of results. */
170
+ preview_url: string;
171
+ /** Present only on video results. */
172
+ video_url?: string;
173
+ /**
174
+ * Present exactly when `video_url` is. `hls` is a playlist (`.m3u8`), not a
175
+ * file — convert it before storing it.
176
+ */
177
+ video_format?: ImageSearchVideoFormat;
178
+ }
179
+ export interface ImageSearchResponse {
180
+ results: ImageSearchResult[];
181
+ /** Pass as `cursor` to get the next page; null when there are no more. */
182
+ next_cursor: string | null;
183
+ search_term: string;
184
+ count: number;
185
+ }
134
186
  export interface OwnedUploadParams {
135
187
  filename: string;
136
188
  contentType: string;
@@ -147,6 +199,10 @@ export interface OwnedUploadResponse {
147
199
  * Client for managing media files
148
200
  */
149
201
  export declare class MediaClient extends BaseClient {
202
+ /** List a bounded page of TikTok/Instagram references; does not download media. */
203
+ listProfilePosts(params: ListProfilePostsParams): Promise<ApiResponse<ListProfilePostsResult>>;
204
+ /** Import actual post assets to owned media. Continuations are checkpointed server-side. */
205
+ downloadSocialPost(params: DownloadSocialPostParams): Promise<ApiResponse<DownloadSocialPostResult>>;
150
206
  /**
151
207
  * Get media for the organization
152
208
  * Returns both user_media and social_audio combined
@@ -213,4 +269,32 @@ export declare class MediaClient extends BaseClient {
213
269
  * @param params.name - Use case identifier (e.g., "profile_picture")
214
270
  */
215
271
  filter(params: FilterMediaParams): Promise<ApiResponse<FilterMediaResponse>>;
272
+ /**
273
+ * Search for profile-picture candidates for a search term.
274
+ *
275
+ * Pictures already in use as a profile picture are removed before the result
276
+ * comes back, so two accounts cannot end up wearing the same face. Sourcing
277
+ * and filtering are one call for that reason — a caller who forgot the second
278
+ * step would get duplicates across their accounts and find out much later.
279
+ *
280
+ * Requires the `media:search` scope on a restricted key.
281
+ *
282
+ * @param params.search_term - What to search for
283
+ * @param params.count - How many to return (default 12, max 40)
284
+ */
285
+ pfpSearch(params: PfpSearchParams): Promise<ApiResponse<PfpSearchResponse>>;
286
+ /**
287
+ * Search for images and videos by keyword, a page at a time.
288
+ *
289
+ * Pass the previous response's `next_cursor` as `cursor`, with the same
290
+ * `search_term`, to continue. Unlike `pfpSearch`, results already in use are
291
+ * not removed.
292
+ *
293
+ * Requires the `media:search` scope on a restricted key.
294
+ *
295
+ * @param params.search_term - What to search for
296
+ * @param params.count - How many to return (default 24, max 50)
297
+ * @param params.cursor - `next_cursor` from the previous page
298
+ */
299
+ imageSearch(params: ImageSearchParams): Promise<ApiResponse<ImageSearchResponse>>;
216
300
  }
package/dist/media.js CHANGED
@@ -6,6 +6,22 @@ const base_1 = require("./base");
6
6
  * Client for managing media files
7
7
  */
8
8
  class MediaClient extends base_1.BaseClient {
9
+ /** List a bounded page of TikTok/Instagram references; does not download media. */
10
+ async listProfilePosts(params) {
11
+ return this.post('/media/social/list-profile-posts', params);
12
+ }
13
+ /** Import actual post assets to owned media. Continuations are checkpointed server-side. */
14
+ async downloadSocialPost(params) {
15
+ for (let step = 0; step < 120; step++) {
16
+ const response = await this.post('/media/social/download-post', params);
17
+ if (!response.ok)
18
+ return response;
19
+ if (response.data.status !== 'processing')
20
+ return { ...response, data: response.data };
21
+ await new Promise(resolve => setTimeout(resolve, Math.min(5000, Math.max(100, response.data.status === 'processing' ? response.data.retryAfterMs ?? 250 : 250))));
22
+ }
23
+ return { ok: false, code: 504, message: 'Import is still pending. Repeat downloadSocialPost with the same URL to resume.', errorCode: 'IMPORT_PENDING', retryable: true };
24
+ }
9
25
  /**
10
26
  * Get media for the organization
11
27
  * Returns both user_media and social_audio combined
@@ -96,5 +112,37 @@ class MediaClient extends base_1.BaseClient {
96
112
  async filter(params) {
97
113
  return this.post('/media/filter', params);
98
114
  }
115
+ /**
116
+ * Search for profile-picture candidates for a search term.
117
+ *
118
+ * Pictures already in use as a profile picture are removed before the result
119
+ * comes back, so two accounts cannot end up wearing the same face. Sourcing
120
+ * and filtering are one call for that reason — a caller who forgot the second
121
+ * step would get duplicates across their accounts and find out much later.
122
+ *
123
+ * Requires the `media:search` scope on a restricted key.
124
+ *
125
+ * @param params.search_term - What to search for
126
+ * @param params.count - How many to return (default 12, max 40)
127
+ */
128
+ async pfpSearch(params) {
129
+ return this.post('/media/pfp-search', params);
130
+ }
131
+ /**
132
+ * Search for images and videos by keyword, a page at a time.
133
+ *
134
+ * Pass the previous response's `next_cursor` as `cursor`, with the same
135
+ * `search_term`, to continue. Unlike `pfpSearch`, results already in use are
136
+ * not removed.
137
+ *
138
+ * Requires the `media:search` scope on a restricted key.
139
+ *
140
+ * @param params.search_term - What to search for
141
+ * @param params.count - How many to return (default 24, max 50)
142
+ * @param params.cursor - `next_cursor` from the previous page
143
+ */
144
+ async imageSearch(params) {
145
+ return this.post('/media/image-search', params);
146
+ }
99
147
  }
100
148
  exports.MediaClient = MediaClient;
@@ -0,0 +1,10 @@
1
+ /** Customer-facing posting explanations. No operational diagnostics are exposed. */
2
+ export type PublicPostFailureCode = 'AUDIO_UNAVAILABLE' | 'BACKUP_AUDIO_RETRY_PENDING' | 'AUDIO_RETRY_PENDING' | 'AUDIO_PREPARATION_FAILED' | 'PUBLISHING_RETRY_PENDING' | 'PUBLISHING_BLOCKED' | 'POST_PROCESSING_FAILED' | 'PUBLICATION_CHECK_IN_PROGRESS' | 'PUBLICATION_UNCONFIRMED' | 'POST_RETRY_PENDING' | 'POST_FAILED';
3
+ export type PublicPostRecovery = 'retry_pending' | 'waiting' | 'verifying_publication' | 'stopped';
4
+ export type PublicPostNextAction = 'wait' | 'change_audio' | 'change_audio_or_contact_support' | 'contact_support';
5
+ export interface PublicPostFailure {
6
+ code: PublicPostFailureCode;
7
+ message: string;
8
+ recovery: PublicPostRecovery;
9
+ next_action: PublicPostNextAction;
10
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/posts.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { BaseClient } from './base';
2
2
  import type { ApiResponse } from './types';
3
+ import type { PublicPostFailure } from './post-failure';
3
4
  /**
4
5
  * Why a post failed, as a stable, client-safe category.
5
6
  *
@@ -10,9 +11,13 @@ import type { ApiResponse } from './types';
10
11
  * - `capacity_deferred` — no posting capacity was available; the post will be retried
11
12
  * - `platform_change` — the social app's interface changed and the post could not complete
12
13
  * - `unconfirmed` — posting finished but publication could not be confirmed
14
+ * - `audio_unavailable` — the chosen sound was not available on the platform and no backup audio was left
13
15
  * - `unknown` — the post failed for an unrecognized reason
16
+ *
17
+ * A failed post also carries its category on its `tag`, as a trailing
18
+ * `fail reason: <category>` (removed again when the post is retried).
14
19
  */
15
- export type PostFailCategory = 'device_error' | 'network_error' | 'upload_interrupted' | 'account_issue' | 'capacity_deferred' | 'platform_change' | 'unconfirmed' | 'unknown';
20
+ export type PostFailCategory = 'device_error' | 'network_error' | 'upload_interrupted' | 'account_issue' | 'capacity_deferred' | 'platform_change' | 'unconfirmed' | 'audio_unavailable' | 'unknown';
16
21
  export type PostType = 'video' | 'slideshow';
17
22
  export type PostStatus = 'draft' | 'scheduled' | 'pending' | 'complete' | 'failed' | 'retrying' | 'deleting' | 'deleted' | 'hidden' | 'require-approval';
18
23
  /**
@@ -27,6 +32,8 @@ export interface CaptionOverlay {
27
32
  fontSize: number;
28
33
  }
29
34
  export interface Post {
35
+ /** Present in list responses; null when there is no current posting issue. */
36
+ failure?: PublicPostFailure | null;
30
37
  id: string;
31
38
  account_id: string | null;
32
39
  type: PostType;
@@ -42,6 +49,10 @@ export interface Post {
42
49
  thumbnail_url: string | null;
43
50
  music_post_id: string | null;
44
51
  social_audio_id: string | null;
52
+ /** Fallback sound. Moved into `social_audio_id` (and cleared) if the platform refuses the primary sound. */
53
+ backup_social_audio_id: string | null;
54
+ /** When the backup sound was swapped in (ISO 8601), or null if it never was. */
55
+ backup_audio_used_at: string | null;
45
56
  scheduled_at: string | null;
46
57
  postUrl?: string;
47
58
  }
@@ -64,12 +75,45 @@ export interface GetPostsParams {
64
75
  limit?: number;
65
76
  /** Opaque cursor from a previous response's `nextCursor`, to fetch the next page. */
66
77
  cursor?: string;
78
+ /** Case-insensitive substring search across caption, title, and account username. */
79
+ search?: string;
80
+ platform?: 'tiktok' | 'instagram';
81
+ postType?: PostType;
82
+ statuses?: PostStatus[];
83
+ /** Match any comma-separated tag on the post's account. */
84
+ accountTags?: string[];
85
+ /** Match any comma-separated tag stored on the post. */
86
+ postTags?: string[];
87
+ socialAudioIds?: string[];
88
+ phoneTypes?: Array<'physical_iphone' | 'manual_iphone' | 'physical_android' | 'emulated_android' | 'social_api' | 'custom_provider' | 'tracking' | 'clipper'>;
89
+ sortBy?: PostSortField;
90
+ sortDirection?: PostSortDirection;
91
+ }
92
+ export type PostSortField = 'created' | 'account' | 'status' | 'title' | 'caption' | 'tag' | 'audio' | 'time' | 'views' | 'likes' | 'comments' | 'shares';
93
+ export type PostSortDirection = 'asc' | 'desc';
94
+ export interface GetPostsPageParams extends GetPostsParams {
95
+ /** Page size. Defaults to 200 and is capped at 500. */
96
+ limit?: number;
97
+ }
98
+ export interface PostsPage {
99
+ posts: Post[];
100
+ /** Latest stats only for posts in this page. */
101
+ stats: PostStat[];
102
+ totalCount: number;
103
+ /** Distinct comma-split post tags across the organization. */
104
+ postTags: string[];
105
+ anyHasTitle: boolean;
106
+ /** Accounts with a match when `accountIds` is ignored and all other filters remain active. */
107
+ matchingAccountIds: string[];
108
+ nextCursor: string | null;
67
109
  }
68
110
  export interface CreateSlideshowParams {
69
111
  accountId: string | null;
70
112
  caption?: string;
71
113
  title?: string;
72
114
  socialAudioId?: string;
115
+ /** Fallback sound, used once if the platform refuses the primary sound at posting time */
116
+ backupSocialAudioId?: string;
73
117
  postTime?: string;
74
118
  imageUrls: string[];
75
119
  strict?: boolean;
@@ -95,6 +139,8 @@ export interface CreateVideoParams {
95
139
  caption?: string;
96
140
  captionOverlays?: CaptionOverlay[];
97
141
  socialAudioId?: string;
142
+ /** Fallback sound, used once if the platform refuses the primary sound at posting time */
143
+ backupSocialAudioId?: string;
98
144
  postTime?: string;
99
145
  videoUrl: string;
100
146
  strict?: boolean;
@@ -127,6 +173,8 @@ export interface UpdatePostParams {
127
173
  caption?: string;
128
174
  title?: string;
129
175
  socialAudioId?: string;
176
+ /** Fallback sound, used once if the platform refuses the primary sound at posting time; `null` clears it */
177
+ backupSocialAudioId?: string | null;
130
178
  postTime?: string;
131
179
  accountId?: string;
132
180
  mediaUrls?: string[];
@@ -153,6 +201,8 @@ export interface CreateDraftParams {
153
201
  caption?: string;
154
202
  title?: string;
155
203
  socialAudioId?: string;
204
+ /** Fallback sound, used once if the platform refuses the primary sound at posting time */
205
+ backupSocialAudioId?: string;
156
206
  /** Tag stored on the created POST itself (returned as `tag` on the Post object) */
157
207
  post_tag?: string;
158
208
  }
@@ -179,6 +229,11 @@ export declare class PostsClient extends BaseClient {
179
229
  * passed back as `cursor` to fetch the next page, ordered newest-first.
180
230
  */
181
231
  getPosts(params?: GetPostsParams): Promise<ApiResponse<Post[]>>;
232
+ /**
233
+ * Get one server-filtered/sorted page plus table metadata and page-scoped stats.
234
+ * Unlike getPosts(), this method never falls back to an unbounded response.
235
+ */
236
+ getPostsPage(params?: GetPostsPageParams): Promise<ApiResponse<PostsPage>>;
182
237
  /**
183
238
  * Create a draft post without assigning it to an account
184
239
  *
@@ -199,6 +254,7 @@ export declare class PostsClient extends BaseClient {
199
254
  postUrl?: string;
200
255
  /** Set when status is "failed"; null otherwise. */
201
256
  fail_category: PostFailCategory | null;
257
+ failure: PublicPostFailure | null;
202
258
  }>>;
203
259
  /**
204
260
  * Create a video post
package/dist/posts.js CHANGED
@@ -15,6 +15,13 @@ class PostsClient extends base_1.BaseClient {
15
15
  async getPosts(params) {
16
16
  return this.post('/post', params ?? {});
17
17
  }
18
+ /**
19
+ * Get one server-filtered/sorted page plus table metadata and page-scoped stats.
20
+ * Unlike getPosts(), this method never falls back to an unbounded response.
21
+ */
22
+ async getPostsPage(params = {}) {
23
+ return this.post('/post', { ...params, includePageMeta: true });
24
+ }
18
25
  /**
19
26
  * Create a draft post without assigning it to an account
20
27
  *
@@ -399,10 +399,10 @@ export declare const iMessageDmPropsSchema: z.ZodObject<{
399
399
  dynamicCrop: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
400
400
  cropVerticalCenter: z.ZodOptional<z.ZodBoolean>;
401
401
  }, "strip", z.ZodTypeAny, {
402
+ time?: string | undefined;
402
403
  backgroundColor?: string | undefined;
403
404
  lightMode?: boolean | undefined;
404
405
  username?: string | undefined;
405
- time?: string | undefined;
406
406
  profilePicUrl?: string | undefined;
407
407
  messages?: {
408
408
  text: string;
@@ -582,10 +582,10 @@ export declare const iMessageDmPropsSchema: z.ZodObject<{
582
582
  readReceiptLeft?: number | undefined;
583
583
  readReceiptRight?: number | undefined;
584
584
  }, {
585
+ time?: string | undefined;
585
586
  backgroundColor?: string | undefined;
586
587
  lightMode?: boolean | undefined;
587
588
  username?: string | undefined;
588
- time?: string | undefined;
589
589
  profilePicUrl?: string | undefined;
590
590
  messages?: {
591
591
  text: string;
@@ -0,0 +1,85 @@
1
+ /** Reference acquisition; these operations use the organization's content:analyze permission. */
2
+ export interface ListProfilePostsParams {
3
+ url: string;
4
+ limit?: number;
5
+ cursor?: string;
6
+ }
7
+ export interface DownloadSocialPostParams {
8
+ url: string;
9
+ }
10
+ export interface SocialReferencePost {
11
+ id: string;
12
+ url: string;
13
+ type: 'video' | 'image' | 'carousel' | 'unknown';
14
+ caption: string | null;
15
+ publishedAt: string | null;
16
+ thumbnailUrl: string | null;
17
+ metrics: {
18
+ views: number | null;
19
+ likes: number | null;
20
+ comments: number | null;
21
+ shares: number | null;
22
+ };
23
+ }
24
+ export interface SocialReferenceWarning {
25
+ code: string;
26
+ message: string;
27
+ }
28
+ export interface ListProfilePostsResult {
29
+ profile: {
30
+ platform: 'tiktok' | 'instagram';
31
+ username: string;
32
+ url: string;
33
+ };
34
+ posts: SocialReferencePost[];
35
+ nextCursor: string | null;
36
+ coverage: 'partial' | 'exhausted';
37
+ warnings: SocialReferenceWarning[];
38
+ }
39
+ export interface SocialReferenceAsset {
40
+ index: number;
41
+ mediaId: string;
42
+ url: string;
43
+ type: 'image' | 'video';
44
+ mimeType: string;
45
+ width: number;
46
+ height: number;
47
+ durationMs: number | null;
48
+ audio: 'present' | 'absent' | 'not_applicable';
49
+ sha256: string;
50
+ }
51
+ export interface DownloadSocialPostResult {
52
+ status: 'complete' | 'partial' | 'failed';
53
+ post: SocialReferencePost & {
54
+ platform: 'tiktok' | 'instagram';
55
+ author: {
56
+ username: string | null;
57
+ profileUrl: string | null;
58
+ };
59
+ };
60
+ assets: SocialReferenceAsset[];
61
+ failedAssets: {
62
+ index: number;
63
+ code: string;
64
+ message: string;
65
+ retryable: boolean;
66
+ }[];
67
+ sound: {
68
+ id: string | null;
69
+ title: string | null;
70
+ artist: string | null;
71
+ sourceUrl: string | null;
72
+ } | null;
73
+ provenance: {
74
+ requestedUrl: string;
75
+ canonicalUrl: string;
76
+ retrievedAt: string;
77
+ };
78
+ manifestUrl: string | null;
79
+ warnings: SocialReferenceWarning[];
80
+ }
81
+ export interface SocialDownloadProgress {
82
+ status: 'processing';
83
+ retryAfterMs?: number;
84
+ }
85
+ export type SocialDownloadStep = DownloadSocialPostResult | SocialDownloadProgress;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -8,6 +8,7 @@ exports.accountTools = [
8
8
  description: 'List accounts with optional filters. Returns all accounts if no filters provided.',
9
9
  schema: zod_1.z.object({
10
10
  tag: zod_1.z.string().optional().describe('Filter by tag'),
11
+ type: zod_1.z.enum(['tiktok', 'instagram']).optional().describe('Filter by platform'),
11
12
  org_group: zod_1.z.string().optional().describe('Filter by org group'),
12
13
  user_group: zod_1.z.string().optional().describe('Filter by user group'),
13
14
  status: zod_1.z.enum(['uninitialized', 'pending', 'initialized', 'setup', 'warming', 'warmed', 'needs_replacement', 'replacing', 'failed', 'deleted']).optional().describe('Filter by account status'),
@@ -3,6 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.mediaTools = void 0;
4
4
  const zod_1 = require("zod");
5
5
  exports.mediaTools = [
6
+ {
7
+ name: 'list_profile_posts',
8
+ description: 'List a page of TikTok or Instagram profile posts, with metadata and an opaque continuation cursor. Does not download files; provider-visible history may be incomplete.',
9
+ schema: zod_1.z.object({ url: zod_1.z.string().url(), limit: zod_1.z.number().int().min(1).max(50).optional(), cursor: zod_1.z.string().uuid().optional() }).strict(),
10
+ execute: async (client, params) => client.media.listProfilePosts(params),
11
+ },
12
+ {
13
+ name: 'download_social_post',
14
+ description: 'Download a TikTok or Instagram post into owned UGC media, preserving ordered photo/video assets and embedded video audio. Returns partial failures explicitly. Repeat the same URL to resume failed assets; sound attribution is not a separately downloaded soundtrack.',
15
+ schema: zod_1.z.object({ url: zod_1.z.string().url() }).strict(),
16
+ execute: async (client, params) => client.media.downloadSocialPost(params),
17
+ },
6
18
  {
7
19
  name: 'list_media',
8
20
  description: 'List all media (user media + social audio). Can filter by IDs or tag.',
@@ -26,6 +26,7 @@ exports.postTools = [
26
26
  videoUrl: zod_1.z.string().describe('URL of the video file'),
27
27
  caption: zod_1.z.string().optional().describe('Post caption'),
28
28
  socialAudioId: zod_1.z.string().optional().describe('Social audio ID to use'),
29
+ backupSocialAudioId: zod_1.z.string().optional().describe('Fallback social audio ID, used once if the platform refuses the primary sound'),
29
30
  postTime: zod_1.z.string().optional().describe('Scheduled time (ISO 8601). Omit for next available slot.'),
30
31
  strict: zod_1.z.boolean().optional().describe('If true with auto-selection, must post at exact time or error'),
31
32
  mustPostBy: zod_1.z.string().optional().describe('Latest allowed scheduled time (ISO 8601). Errors if no slot is available before this deadline.'),
@@ -47,6 +48,7 @@ exports.postTools = [
47
48
  caption: zod_1.z.string().optional().describe('Post caption'),
48
49
  title: zod_1.z.string().optional().describe('Slideshow title'),
49
50
  socialAudioId: zod_1.z.string().optional().describe('Social audio ID to use'),
51
+ backupSocialAudioId: zod_1.z.string().optional().describe('Fallback social audio ID, used once if the platform refuses the primary sound'),
50
52
  postTime: zod_1.z.string().optional().describe('Scheduled time (ISO 8601)'),
51
53
  strict: zod_1.z.boolean().optional().describe('If true with auto-selection, must post at exact time or error'),
52
54
  mustPostBy: zod_1.z.string().optional().describe('Latest allowed scheduled time (ISO 8601). Errors if no slot is available before this deadline.'),
@@ -68,6 +70,7 @@ exports.postTools = [
68
70
  caption: zod_1.z.string().optional().describe('Post caption'),
69
71
  title: zod_1.z.string().optional().describe('Slideshow title'),
70
72
  socialAudioId: zod_1.z.string().optional().describe('Social audio ID'),
73
+ backupSocialAudioId: zod_1.z.string().optional().describe('Fallback social audio ID, used once if the platform refuses the primary sound'),
71
74
  post_tag: zod_1.z.string().optional().describe('Tag stored on the created post'),
72
75
  }),
73
76
  execute: async (client, params) => {
@@ -92,6 +95,7 @@ exports.postTools = [
92
95
  caption: zod_1.z.string().optional().describe('New caption'),
93
96
  title: zod_1.z.string().optional().describe('New title (slideshows only)'),
94
97
  socialAudioId: zod_1.z.string().optional().describe('New social audio ID'),
98
+ backupSocialAudioId: zod_1.z.string().nullable().optional().describe('Fallback social audio ID, used once if the platform refuses the primary sound; null clears it'),
95
99
  postTime: zod_1.z.string().optional().describe('New scheduled time (ISO 8601)'),
96
100
  accountId: zod_1.z.string().optional().describe('Move post to different account'),
97
101
  mediaUrls: zod_1.z.array(zod_1.z.string()).optional().describe('New media URLs'),
package/dist/types.d.ts CHANGED
@@ -14,5 +14,9 @@ export interface ErrorResponse {
14
14
  message: string;
15
15
  ok: false;
16
16
  data?: never;
17
+ errorCode?: string;
18
+ retryable?: boolean;
17
19
  }
18
20
  export type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;
21
+ export type { PublicPostFailureCode, PublicPostRecovery, PublicPostNextAction, PublicPostFailure } from './post-failure';
22
+ export type { ListProfilePostsParams, ListProfilePostsResult, DownloadSocialPostParams, DownloadSocialPostResult, SocialReferencePost, SocialReferenceAsset } from './social-acquisition';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc",
3
- "version": "4.19.0",
3
+ "version": "4.20.0",
4
4
  "description": "TypeScript/JavaScript client for the UGC Inc API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",