upload-post 2.15.0 → 2.17.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.
Files changed (4) hide show
  1. package/README.md +11 -0
  2. package/index.d.ts +55 -12
  3. package/index.js +28 -1
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -191,6 +191,14 @@ await client.editScheduled('job-id', {
191
191
  },
192
192
  });
193
193
 
194
+ // Change any other option by its API form name (null removes it), schedule the
195
+ // video natively on YouTube, or replace the Reels cover
196
+ await client.editScheduled('job-id', {
197
+ fields: { privacyStatus: 'unlisted', disable_comment: true, instagram_first_comment: 'Link in bio' },
198
+ youtubePublishAt: '2026-11-01T09:00:00Z',
199
+ coverImageBase64: 'data:image/jpeg;base64,...', // or removeCover: true
200
+ });
201
+
194
202
  // Cancel a scheduled post
195
203
  await client.cancelScheduled('job-id');
196
204
  ```
@@ -622,6 +630,8 @@ for those options.
622
630
  - `instagramLocationId` - Location ID
623
631
  - `instagramThumbOffset` - Thumbnail offset
624
632
  - `instagramAltText` - Alt text on photos (string or list, ≤1000 chars each)
633
+ - `instagramShareMode` - Trial Reels: `TRIAL_REELS_SHARE_TO_FOLLOWERS_IF_LIKED` or `TRIAL_REELS_DONT_SHARE_TO_FOLLOWERS` (videos only)
634
+ - `instagramIsAiGenerated` - AI-generated content label (sent as the generic `is_ai_generated`, which other platforms in the same post also read)
625
635
 
626
636
  ### YouTube
627
637
  - `youtubeTags` - Array or comma-separated tags
@@ -659,6 +669,7 @@ for those options.
659
669
  - `thumbnailUrl` - URL for custom video cover (generic field, also read by X, YouTube and LinkedIn; on Facebook only when facebookMediaType is VIDEO)
660
670
  - `facebookLinkUrl` - URL for text posts
661
671
  - `facebookAltText` - Alt text per photo
672
+ - `facebookMediaType` on photos - POSTS (feed, default) or STORIES (Page photo story)
662
673
  - `facebookPlaceId` - Place ID
663
674
  - `facebookTargeting` / `facebookFeedTargeting` - Audience JSON
664
675
  - `facebookCallToAction` / `facebookChildAttachments` / `facebookMultiShareEndCard` - Link posts
package/index.d.ts CHANGED
@@ -36,6 +36,8 @@ declare module 'upload-post' {
36
36
 
37
37
  /** Facebook media type */
38
38
  export type FacebookMediaType = 'REELS' | 'STORIES' | 'VIDEO';
39
+ export type FacebookPhotoMediaType = 'POSTS' | 'STORIES';
40
+ export type InstagramShareMode = 'TRIAL_REELS_SHARE_TO_FOLLOWERS_IF_LIKED' | 'TRIAL_REELS_DONT_SHARE_TO_FOLLOWERS';
39
41
 
40
42
  /** X (Twitter) reply settings */
41
43
  export type XReplySettings = 'everyone' | 'following' | 'mentionedUsers' | 'subscribers' | 'verified';
@@ -274,6 +276,10 @@ declare module 'upload-post' {
274
276
  instagramLocationId?: string;
275
277
  /** Thumbnail offset */
276
278
  instagramThumbOffset?: string;
279
+ /** Trial Reel: shown to non-followers first. Sent as share_mode */
280
+ instagramShareMode?: InstagramShareMode;
281
+ /** AI-generated content label. Sent as the generic is_ai_generated field */
282
+ instagramIsAiGenerated?: boolean;
277
283
  }
278
284
 
279
285
  export interface InstagramPhotoOptions {
@@ -287,6 +293,8 @@ declare module 'upload-post' {
287
293
  instagramLocationId?: string;
288
294
  /** Alt text: string (applied to every item) or list of strings, ≤1000 chars each */
289
295
  instagramAltText?: string | string[];
296
+ /** AI-generated content label. Sent as the generic is_ai_generated field */
297
+ instagramIsAiGenerated?: boolean;
290
298
  }
291
299
 
292
300
  // ==================== YouTube Options ====================
@@ -414,6 +422,8 @@ declare module 'upload-post' {
414
422
  facebookPageId?: string;
415
423
  /** Alt text per photo */
416
424
  facebookAltText?: string | string[];
425
+ /** POSTS (default) publishes to the feed, STORIES as a Page story */
426
+ facebookMediaType?: FacebookPhotoMediaType;
417
427
  /** Facebook Place ID */
418
428
  facebookPlaceId?: string;
419
429
  /** Geographic/age/language targeting */
@@ -968,6 +978,50 @@ declare module 'upload-post' {
968
978
  [key: string]: any;
969
979
  }
970
980
 
981
+ /** Body of PATCH /api/uploadposts/schedule/<job_id>. Every key is optional. */
982
+ export interface EditScheduledOptions {
983
+ /** New publish date (ISO 8601); must be in the future and within 1 year */
984
+ scheduledDate?: string;
985
+ /** IANA timezone `scheduledDate` is read in (default UTC) */
986
+ timezone?: string;
987
+ /** New title for every platform */
988
+ title?: string;
989
+ /** New caption/description for every platform */
990
+ caption?: string;
991
+ /** Alias of caption */
992
+ description?: string;
993
+ /** Per-platform text; only the platforms listed are touched */
994
+ platformContent?: Record<string, { title?: string; caption?: string }>;
995
+ /**
996
+ * Any other editable upload option, keyed by its API form name
997
+ * (e.g. `privacyStatus`, `tags`, `disable_comment`, `instagram_first_comment`,
998
+ * `gbp_cta_url`). `null` removes the option; unknown or internal keys are ignored.
999
+ */
1000
+ fields?: Record<string, string | number | boolean | string[] | null>;
1001
+ /** Native YouTube scheduling (RFC3339), sent as `fields.youtube_publish_at`; `null` removes it */
1002
+ youtubePublishAt?: string | null;
1003
+ /** New cover image (Instagram / Facebook Reels) as base64 or a data URL */
1004
+ coverImageBase64?: string;
1005
+ /** File name of the new cover (default `cover.jpg`) */
1006
+ coverFilename?: string;
1007
+ /** MIME type of the new cover (default `image/jpeg`) */
1008
+ coverContentType?: string;
1009
+ /** Remove the custom cover */
1010
+ removeCover?: boolean;
1011
+ }
1012
+
1013
+ export interface EditScheduledResponse {
1014
+ success: boolean;
1015
+ job_id?: string;
1016
+ scheduled_date?: string;
1017
+ title?: string;
1018
+ caption?: string;
1019
+ platform_content?: Record<string, { title: string; caption: string }>;
1020
+ error?: string;
1021
+ error_code?: string;
1022
+ [key: string]: any;
1023
+ }
1024
+
971
1025
  export interface UserProfile {
972
1026
  username: string;
973
1027
  social_accounts?: Record<string, any>;
@@ -1412,18 +1466,7 @@ declare module 'upload-post' {
1412
1466
  * @param jobId - Scheduled job ID
1413
1467
  * @param options - Edit options
1414
1468
  */
1415
- editScheduled(jobId: string, options: {
1416
- scheduledDate?: string;
1417
- timezone?: string;
1418
- /** New title for every platform */
1419
- title?: string;
1420
- /** New caption/description for every platform */
1421
- caption?: string;
1422
- /** Alias of caption */
1423
- description?: string;
1424
- /** Per-platform text; only the platforms listed are touched */
1425
- platformContent?: Record<string, { title?: string; caption?: string }>;
1426
- }): Promise<{ success: boolean }>;
1469
+ editScheduled(jobId: string, options?: EditScheduledOptions): Promise<EditScheduledResponse>;
1427
1470
 
1428
1471
  // User Management
1429
1472
  /**
package/index.js CHANGED
@@ -300,9 +300,14 @@ export class UploadPost {
300
300
  if (options.instagramCollaborators) form.append('collaborators', options.instagramCollaborators);
301
301
  if (options.instagramUserTags) form.append('user_tags', options.instagramUserTags);
302
302
  if (options.instagramLocationId) form.append('location_id', options.instagramLocationId);
303
+ // AI-content label. The API field is the generic is_ai_generated, which other
304
+ // platforms in the same post also read (it is the same media).
305
+ this._appendField(form, 'is_ai_generated', this._pick(options, 'instagramIsAiGenerated', 'isAiGenerated', 'is_ai_generated'));
303
306
 
304
307
  if (isVideo) {
305
308
  if (options.instagramShareToFeed !== undefined) form.append('share_to_feed', String(options.instagramShareToFeed));
309
+ // Trial Reels: TRIAL_REELS_SHARE_TO_FOLLOWERS_IF_LIKED or TRIAL_REELS_DONT_SHARE_TO_FOLLOWERS.
310
+ this._appendField(form, 'share_mode', this._pick(options, 'instagramShareMode', 'share_mode'));
306
311
  if (options.instagramCoverUrl) {
307
312
  const coverVal = options.instagramCoverUrl;
308
313
  if (typeof coverVal === 'string' && (coverVal.toLowerCase().startsWith('http://') || coverVal.toLowerCase().startsWith('https://'))) {
@@ -426,6 +431,8 @@ export class UploadPost {
426
431
 
427
432
  if (!isVideo && !isText) {
428
433
  this._appendField(form, 'facebook_alt_text', this._pick(options, 'facebookAltText', 'facebook_alt_text'));
434
+ // POSTS (default) or STORIES for a Page photo story.
435
+ this._appendField(form, 'facebook_media_type', this._pick(options, 'facebookMediaType', 'facebook_media_type'));
429
436
  }
430
437
 
431
438
  if (isText && options.facebookLinkUrl) {
@@ -1425,9 +1432,20 @@ export class UploadPost {
1425
1432
  * @param {Object} [options.platformContent] - Per-platform text, e.g.
1426
1433
  * { instagram: { caption: '...' }, tiktok: { caption: '...' }, youtube: { title: '...' } }.
1427
1434
  * Only the platforms listed are touched.
1435
+ * @param {Object} [options.fields] - Any other editable upload option, keyed by the
1436
+ * API form name (e.g. { privacyStatus: 'unlisted', tags: '["a","b"]', disable_comment: true,
1437
+ * instagram_first_comment: '...' }). `null` removes an option. Unknown or internal keys
1438
+ * are ignored by the API.
1439
+ * @param {string|null} [options.youtubePublishAt] - Native YouTube scheduling (RFC3339),
1440
+ * sent as fields.youtube_publish_at; `null` removes it. Wins over the same key in `fields`.
1441
+ * @param {string} [options.coverImageBase64] - New cover image (Instagram / Facebook Reels),
1442
+ * base64 or a data URL
1443
+ * @param {string} [options.coverFilename] - File name of the new cover (default cover.jpg)
1444
+ * @param {string} [options.coverContentType] - MIME type of the new cover (default image/jpeg)
1445
+ * @param {boolean} [options.removeCover] - Remove the custom cover
1428
1446
  * @returns {Promise<Object>} Edit result
1429
1447
  */
1430
- async editScheduled(jobId, options) {
1448
+ async editScheduled(jobId, options = {}) {
1431
1449
  const body = {};
1432
1450
  if (options.scheduledDate) body.scheduled_date = options.scheduledDate;
1433
1451
  if (options.timezone) body.timezone = options.timezone;
@@ -1435,6 +1453,15 @@ export class UploadPost {
1435
1453
  if (options.caption !== undefined) body.caption = options.caption;
1436
1454
  else if (options.description !== undefined) body.description = options.description;
1437
1455
  if (options.platformContent) body.platform_content = options.platformContent;
1456
+ const fields = { ...(options.fields || {}) };
1457
+ if (options.youtubePublishAt !== undefined) fields.youtube_publish_at = options.youtubePublishAt;
1458
+ if (Object.keys(fields).length) body.fields = fields;
1459
+ if (options.removeCover === true) body.remove_cover = true;
1460
+ else if (options.coverImageBase64) {
1461
+ body.cover_image_base64 = options.coverImageBase64;
1462
+ if (options.coverFilename) body.cover_filename = options.coverFilename;
1463
+ if (options.coverContentType) body.cover_content_type = options.coverContentType;
1464
+ }
1438
1465
  return this._request(`/uploadposts/schedule/${jobId}`, 'PATCH', body);
1439
1466
  }
1440
1467
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "upload-post",
3
- "version": "2.15.0",
3
+ "version": "2.17.0",
4
4
  "description": "Official client library for Upload-Post API - Cross-platform social media upload for TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, and X (Twitter)",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",