upload-post 2.4.0 → 2.5.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
@@ -321,6 +321,38 @@ These options work across all upload methods:
321
321
  | `autogenerateLanguage` | Force the AI output language (ISO code); omit to auto-detect from the media |
322
322
  | `idempotencyKey` | Collapses duplicate uploads within 24h. Reuse the same value when retrying. Alias: `requestId` |
323
323
 
324
+ ## Google Business Profile
325
+
326
+ Pass the target location on the upload itself. There is no separate "select a location" call — the API resolves the location per post.
327
+
328
+ ```javascript
329
+ const { locations } = await client.getGoogleBusinessLocations('myprofile');
330
+
331
+ await client.upload('video.mp4', {
332
+ user: 'myprofile',
333
+ platforms: ['google_business'],
334
+ title: 'Now open on Sundays',
335
+ gbpLocationId: locations[0].name, // "accounts/123/locations/456"
336
+ });
337
+ ```
338
+
339
+ `gbpLocationId` is **required when the account has more than one location** — the API only auto-selects when exactly one exists. Beyond a standard post you can publish an event or an offer:
340
+
341
+ ```javascript
342
+ await client.uploadText({
343
+ user: 'myprofile',
344
+ platforms: ['google_business'],
345
+ title: 'Summer sale',
346
+ gbpLocationId: locations[0].name,
347
+ gbpTopicType: 'OFFER',
348
+ gbpOfferCoupon: 'SUMMER25',
349
+ gbpOfferRedeemUrl: 'https://example.com/redeem',
350
+ gbpOfferTerms: 'One per customer',
351
+ });
352
+ ```
353
+
354
+ Also available: `gbpTopicType: 'EVENT'` with `gbpEventTitle` / `gbpEventStartDate` / `gbpEventStartTime` / `gbpEventEndDate` / `gbpEventEndTime`, a call-to-action via `gbpCtaType` + `gbpCtaUrl`, and `gbpMediaUrl` / `gbpMediaFormat`.
355
+
324
356
  ## Retrying an upload safely
325
357
 
326
358
  An upload that times out may still have been accepted by the API. Retrying it without an idempotency key publishes the post a second time.
package/index.d.ts CHANGED
@@ -349,6 +349,37 @@ declare module 'upload-post' {
349
349
 
350
350
  // ==================== Combined Upload Options ====================
351
351
 
352
+ export interface GoogleBusinessOptions {
353
+ /** Google Business location id, e.g. "accounts/123/locations/456". Required when the account has more than one location; the API auto-selects only for single-location accounts. List them with getGoogleBusinessLocations(). */
354
+ gbpLocationId?: string;
355
+ /** Post type. Defaults to STANDARD. */
356
+ gbpTopicType?: 'STANDARD' | 'EVENT' | 'OFFER';
357
+ /** Media URL attached to the post */
358
+ gbpMediaUrl?: string;
359
+ /** Media format */
360
+ gbpMediaFormat?: 'PHOTO' | 'VIDEO';
361
+ /** Call-to-action button */
362
+ gbpCtaType?: 'BOOK' | 'ORDER' | 'SHOP' | 'LEARN_MORE' | 'SIGN_UP' | 'CALL';
363
+ /** URL the call-to-action button opens */
364
+ gbpCtaUrl?: string;
365
+ /** Event title. Used when gbpTopicType is EVENT. */
366
+ gbpEventTitle?: string;
367
+ /** Event start date, YYYY-MM-DD */
368
+ gbpEventStartDate?: string;
369
+ /** Event start time, HH:MM */
370
+ gbpEventStartTime?: string;
371
+ /** Event end date, YYYY-MM-DD */
372
+ gbpEventEndDate?: string;
373
+ /** Event end time, HH:MM */
374
+ gbpEventEndTime?: string;
375
+ /** Offer coupon code. Used when gbpTopicType is OFFER. */
376
+ gbpOfferCoupon?: string;
377
+ /** URL to redeem the offer */
378
+ gbpOfferRedeemUrl?: string;
379
+ /** Offer terms and conditions */
380
+ gbpOfferTerms?: string;
381
+ }
382
+
352
383
  export interface UploadVideoOptions extends CommonUploadOptions,
353
384
  TikTokVideoOptions,
354
385
  InstagramVideoOptions,
@@ -357,7 +388,9 @@ declare module 'upload-post' {
357
388
  FacebookVideoOptions,
358
389
  PinterestVideoOptions,
359
390
  XMediaOptions,
360
- ThreadsOptions {
391
+ ThreadsOptions,
392
+ RedditOptions,
393
+ GoogleBusinessOptions {
361
394
  /** Target platforms */
362
395
  platforms: VideoPlatform[];
363
396
  }
@@ -370,7 +403,8 @@ declare module 'upload-post' {
370
403
  PinterestPhotoOptions,
371
404
  XMediaOptions,
372
405
  ThreadsOptions,
373
- RedditOptions {
406
+ RedditOptions,
407
+ GoogleBusinessOptions {
374
408
  /** Target platforms */
375
409
  platforms: PhotoPlatform[];
376
410
  }
@@ -390,7 +424,8 @@ declare module 'upload-post' {
390
424
  XTextOptions,
391
425
  ThreadsOptions,
392
426
  RedditOptions,
393
- LinkPreviewOptions {
427
+ LinkPreviewOptions,
428
+ GoogleBusinessOptions {
394
429
  /** Target platforms */
395
430
  platforms: TextPlatform[];
396
431
  }
@@ -721,7 +756,19 @@ declare module 'upload-post' {
721
756
  * Update notification configuration (including webhook settings)
722
757
  * @param options - Notification config options
723
758
  */
724
- updateNotificationConfig(options?: { webhookEvents?: string[]; webhookUrl?: string }): Promise<{ success: boolean; config?: { webhook_events?: string[]; webhook_url?: string; [key: string]: any } }>;
759
+ updateNotificationConfig(options?: {
760
+ webhookEvents?: string[];
761
+ webhookUrl?: string;
762
+ channels?: string[];
763
+ telegramChatId?: string;
764
+ slackWebhookUrl?: string;
765
+ whatsappToPhone?: string;
766
+ }): Promise<{ success: boolean; config?: { webhook_events?: string[]; webhook_url?: string; [key: string]: any } }>;
767
+
768
+ /**
769
+ * Send a test notification through the configured channels
770
+ */
771
+ testNotifications(): Promise<{ success: boolean; [key: string]: any }>;
725
772
 
726
773
  // Instagram Comments
727
774
 
package/index.js CHANGED
@@ -333,6 +333,30 @@ export class UploadPost {
333
333
  if (options.threadsTopicTag) form.append('threads_topic_tag', options.threadsTopicTag);
334
334
  }
335
335
 
336
+ /**
337
+ * Add Google Business Profile parameters
338
+ *
339
+ * gbpLocationId is required for accounts with more than one location; the API
340
+ * only auto-selects when exactly one location exists.
341
+ * @private
342
+ */
343
+ _addGoogleBusinessParams(form, options) {
344
+ if (options.gbpLocationId) form.append('gbp_location_id', options.gbpLocationId);
345
+ if (options.gbpTopicType) form.append('gbp_topic_type', String(options.gbpTopicType).toUpperCase());
346
+ if (options.gbpMediaUrl) form.append('gbp_media_url', options.gbpMediaUrl);
347
+ if (options.gbpMediaFormat) form.append('gbp_media_format', String(options.gbpMediaFormat).toUpperCase());
348
+ if (options.gbpCtaType) form.append('gbp_cta_type', String(options.gbpCtaType).toUpperCase());
349
+ if (options.gbpCtaUrl) form.append('gbp_cta_url', options.gbpCtaUrl);
350
+ if (options.gbpEventTitle) form.append('gbp_event_title', options.gbpEventTitle);
351
+ if (options.gbpEventStartDate) form.append('gbp_event_start_date', options.gbpEventStartDate);
352
+ if (options.gbpEventStartTime) form.append('gbp_event_start_time', options.gbpEventStartTime);
353
+ if (options.gbpEventEndDate) form.append('gbp_event_end_date', options.gbpEventEndDate);
354
+ if (options.gbpEventEndTime) form.append('gbp_event_end_time', options.gbpEventEndTime);
355
+ if (options.gbpOfferCoupon) form.append('gbp_offer_coupon', options.gbpOfferCoupon);
356
+ if (options.gbpOfferRedeemUrl) form.append('gbp_offer_redeem_url', options.gbpOfferRedeemUrl);
357
+ if (options.gbpOfferTerms) form.append('gbp_offer_terms', options.gbpOfferTerms);
358
+ }
359
+
336
360
  /**
337
361
  * Add Reddit-specific parameters
338
362
  * @private
@@ -457,6 +481,7 @@ export class UploadPost {
457
481
  if (platforms.includes('x')) this._addXParams(form, options, false);
458
482
  if (platforms.includes('threads')) this._addThreadsParams(form, options);
459
483
  if (platforms.includes('reddit')) this._addRedditParams(form, options);
484
+ if (platforms.includes('google_business')) this._addGoogleBusinessParams(form, options);
460
485
 
461
486
  return this._request('/upload', 'POST', form, true, this._idempotencyHeaders(options));
462
487
  }
@@ -545,6 +570,7 @@ export class UploadPost {
545
570
  if (platforms.includes('x')) this._addXParams(form, options, false);
546
571
  if (platforms.includes('threads')) this._addThreadsParams(form, options);
547
572
  if (platforms.includes('reddit')) this._addRedditParams(form, options);
573
+ if (platforms.includes('google_business')) this._addGoogleBusinessParams(form, options);
548
574
 
549
575
  return this._request('/upload_photos', 'POST', form, true, this._idempotencyHeaders(options));
550
576
  }
@@ -612,6 +638,7 @@ export class UploadPost {
612
638
  if (platforms.includes('bluesky') && (options.blueskyLinkUrl || options.linkUrl)) {
613
639
  form.append('bluesky_link_url', options.blueskyLinkUrl || options.linkUrl);
614
640
  }
641
+ if (platforms.includes('google_business')) this._addGoogleBusinessParams(form, options);
615
642
 
616
643
  return this._request('/upload_text', 'POST', form, true, this._idempotencyHeaders(options));
617
644
  }
@@ -967,9 +994,22 @@ export class UploadPost {
967
994
  const body = {};
968
995
  if (options.webhookEvents) body.webhook_events = options.webhookEvents;
969
996
  if (options.webhookUrl) body.webhook_url = options.webhookUrl;
997
+ if (options.channels) body.channels = options.channels;
998
+ if (options.telegramChatId) body.telegram_chat_id = options.telegramChatId;
999
+ if (options.slackWebhookUrl) body.slack_webhook_url = options.slackWebhookUrl;
1000
+ if (options.whatsappToPhone) body.whatsapp_to_phone = options.whatsappToPhone;
970
1001
  return this._request('/uploadposts/users/notifications', 'POST', body);
971
1002
  }
972
1003
 
1004
+ /**
1005
+ * Send a test notification through the configured channels
1006
+ *
1007
+ * @returns {Promise<Object>} Test result
1008
+ */
1009
+ async testNotifications() {
1010
+ return this._request('/uploadposts/users/notifications/test', 'POST', {});
1011
+ }
1012
+
973
1013
  // ==================== Instagram Comments ====================
974
1014
 
975
1015
  /**
@@ -1077,10 +1117,11 @@ export class UploadPost {
1077
1117
  * @param {string} [profile] - Profile username
1078
1118
  * @returns {Promise<Object>} Selection result with google_business_id and display_name
1079
1119
  */
1080
- async selectGoogleBusinessLocation(locationId, profile) {
1081
- const data = { location_id: locationId };
1082
- if (profile) data.profile = profile;
1083
- return this._request('/uploadposts/google-business/locations/select', 'POST', data);
1120
+ async selectGoogleBusinessLocation() {
1121
+ throw new Error(
1122
+ 'selectGoogleBusinessLocation is not supported: the API has no persistent location selection. ' +
1123
+ 'Pass gbpLocationId on the upload instead (list ids with getGoogleBusinessLocations).'
1124
+ );
1084
1125
  }
1085
1126
  }
1086
1127
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "upload-post",
3
- "version": "2.4.0",
3
+ "version": "2.5.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",