n8n-nodes-upload-post 0.5.0 → 0.6.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.
@@ -900,6 +900,7 @@ const applyCredentialPlatformOptions = (ctx, platforms, formData) => {
900
900
  setIfFilled(formData, 'bluesky_labels', ctx.node.getNodeParameter('blueskyLabels', ctx.itemIndex, ''));
901
901
  setIfFilled(formData, 'bluesky_threadgate', ctx.node.getNodeParameter('blueskyThreadgate', ctx.itemIndex, ''));
902
902
  setIfFilled(formData, 'bluesky_quote_uri', ctx.node.getNodeParameter('blueskyQuoteUri', ctx.itemIndex, ''));
903
+ setIfFilled(formData, 'bluesky_gallery', ctx.node.getNodeParameter('blueskyGallery', ctx.itemIndex, false));
903
904
  }
904
905
  if (has('discord')) {
905
906
  setIfFilled(formData, 'discord_thread_id', ctx.node.getNodeParameter('discordThreadId', ctx.itemIndex, ''));
@@ -1332,6 +1333,18 @@ const buildMonitoringRequest = (ctx) => {
1332
1333
  waitForCompletion: false,
1333
1334
  };
1334
1335
  }
1336
+ case 'resolveLinkedinOrganization': {
1337
+ return {
1338
+ endpoint: '/uploadposts/linkedin/resolve-organization',
1339
+ method: 'GET',
1340
+ qs: {
1341
+ user: ctx.node.getNodeParameter('linkedinResolveProfile', ctx.itemIndex),
1342
+ url: ctx.node.getNodeParameter('linkedinResolveUrl', ctx.itemIndex).trim(),
1343
+ },
1344
+ isUploadOperation: false,
1345
+ waitForCompletion: false,
1346
+ };
1347
+ }
1335
1348
  case 'getGoogleBusinessLocations': {
1336
1349
  const qs = {};
1337
1350
  const profile = ctx.node.getNodeParameter('gbpLocationsProfile', ctx.itemIndex, '');
@@ -1661,6 +1674,56 @@ const buildUserRequest = async (ctx) => {
1661
1674
  isUploadOperation: false,
1662
1675
  waitForCompletion: false,
1663
1676
  };
1677
+ case 'listWebhookDeliveries': {
1678
+ const qs = {};
1679
+ const filters = [
1680
+ ['webhookDeliveriesEvent', 'event'],
1681
+ ['webhookDeliveriesStatus', 'status'],
1682
+ ['webhookDeliveriesRequestId', 'request_id'],
1683
+ ['webhookDeliveriesJobId', 'job_id'],
1684
+ ['webhookDeliveriesProfile', 'profile_username'],
1685
+ ];
1686
+ for (const [param, field] of filters) {
1687
+ const value = ctx.node.getNodeParameter(param, ctx.itemIndex, '');
1688
+ if (value)
1689
+ qs[field] = value;
1690
+ }
1691
+ const limit = ctx.node.getNodeParameter('webhookDeliveriesLimit', ctx.itemIndex, 20);
1692
+ if (limit)
1693
+ qs.limit = limit;
1694
+ const page = ctx.node.getNodeParameter('webhookDeliveriesPage', ctx.itemIndex, 1);
1695
+ if (page)
1696
+ qs.page = page;
1697
+ const includePayload = ctx.node.getNodeParameter('webhookDeliveriesIncludePayload', ctx.itemIndex, false);
1698
+ if (includePayload)
1699
+ qs.include_payload = 'true';
1700
+ return {
1701
+ endpoint: '/uploadposts/webhooks/deliveries',
1702
+ method: 'GET',
1703
+ qs,
1704
+ isUploadOperation: false,
1705
+ waitForCompletion: false,
1706
+ };
1707
+ }
1708
+ case 'getWebhookDelivery': {
1709
+ const deliveryId = ctx.node.getNodeParameter('webhookDeliveryId', ctx.itemIndex);
1710
+ return {
1711
+ endpoint: `/uploadposts/webhooks/deliveries/${encodeURIComponent(deliveryId.trim())}`,
1712
+ method: 'GET',
1713
+ isUploadOperation: false,
1714
+ waitForCompletion: false,
1715
+ };
1716
+ }
1717
+ case 'redeliverWebhook': {
1718
+ const deliveryId = ctx.node.getNodeParameter('webhookDeliveryId', ctx.itemIndex);
1719
+ return {
1720
+ endpoint: `/uploadposts/webhooks/deliveries/${encodeURIComponent(deliveryId.trim())}/redeliver`,
1721
+ method: 'POST',
1722
+ body: {},
1723
+ isUploadOperation: false,
1724
+ waitForCompletion: false,
1725
+ };
1726
+ }
1664
1727
  default:
1665
1728
  throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `Unsupported user operation: ${ctx.operation}`, {
1666
1729
  itemIndex: ctx.itemIndex,
@@ -1668,7 +1731,139 @@ const buildUserRequest = async (ctx) => {
1668
1731
  }
1669
1732
  };
1670
1733
  const COMMENT_ACTIONS_WITHOUT_POST = new Set(['like', 'unlike']);
1734
+ const AUTODM_RULE_ACTIONS = {
1735
+ pauseAutodmRule: 'pause',
1736
+ resumeAutodmRule: 'resume',
1737
+ deleteAutodmRule: 'delete',
1738
+ };
1739
+ const DM_ACTIONS_NEEDING_MESSAGE_ID = new Set(['react', 'unreact', 'delete']);
1740
+ const parseJsonObjectParameter = (ctx, name, label) => {
1741
+ const raw = ctx.node.getNodeParameter(name, ctx.itemIndex, '');
1742
+ if (raw === '' || raw === undefined || raw === null)
1743
+ return {};
1744
+ if (typeof raw === 'object')
1745
+ return raw;
1746
+ const trimmed = raw.trim();
1747
+ if (!trimmed)
1748
+ return {};
1749
+ try {
1750
+ const parsed = JSON.parse(trimmed);
1751
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
1752
+ return parsed;
1753
+ }
1754
+ catch {
1755
+ }
1756
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `${label} must be a JSON object, e.g. {"quick_replies": [...]}`, {
1757
+ itemIndex: ctx.itemIndex,
1758
+ });
1759
+ };
1760
+ const buildDmRequest = (ctx) => {
1761
+ var _a;
1762
+ const user = ctx.node.getNodeParameter('dmUser', ctx.itemIndex);
1763
+ const platform = ctx.node.getNodeParameter('dmPlatform', ctx.itemIndex, 'instagram');
1764
+ switch (ctx.operation) {
1765
+ case 'sendDm': {
1766
+ const body = { platform, user };
1767
+ const recipientId = ctx.node.getNodeParameter('dmRecipientId', ctx.itemIndex, '');
1768
+ const conversationId = platform === 'bluesky'
1769
+ ? ctx.node.getNodeParameter('dmConversationId', ctx.itemIndex, '')
1770
+ : '';
1771
+ if (!recipientId && !conversationId) {
1772
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), platform === 'bluesky'
1773
+ ? 'Set Recipient ID (handle, DID or profile link) or Conversation ID.'
1774
+ : 'Recipient ID is required.', { itemIndex: ctx.itemIndex });
1775
+ }
1776
+ if (recipientId)
1777
+ body.recipient_id = recipientId;
1778
+ if (conversationId)
1779
+ body.conversation_id = conversationId;
1780
+ const message = ctx.node.getNodeParameter('dmMessage', ctx.itemIndex, '');
1781
+ if (message)
1782
+ body.message = message;
1783
+ const replyTo = ctx.node.getNodeParameter('dmReplyTo', ctx.itemIndex, '');
1784
+ if (replyTo)
1785
+ body.reply_to = replyTo;
1786
+ const senderAction = ctx.node.getNodeParameter(platform === 'bluesky' ? 'dmBlueskySenderAction' : 'dmInstagramSenderAction', ctx.itemIndex, '');
1787
+ if (senderAction) {
1788
+ body.sender_action = senderAction;
1789
+ const messageId = ctx.node.getNodeParameter('dmMessageId', ctx.itemIndex, '');
1790
+ if (messageId)
1791
+ body.message_id = messageId;
1792
+ if (DM_ACTIONS_NEEDING_MESSAGE_ID.has(senderAction) && !messageId) {
1793
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `Message ID is required for the ${senderAction} action.`, {
1794
+ itemIndex: ctx.itemIndex,
1795
+ });
1796
+ }
1797
+ if (senderAction === 'react' || senderAction === 'unreact') {
1798
+ const reaction = ctx.node.getNodeParameter('dmReaction', ctx.itemIndex, '');
1799
+ if (reaction)
1800
+ body.reaction = reaction;
1801
+ }
1802
+ }
1803
+ if (platform === 'bluesky') {
1804
+ const sharePostUrl = ctx.node.getNodeParameter('dmBlueskySharePostUrl', ctx.itemIndex, '');
1805
+ if (sharePostUrl)
1806
+ body.attachments = { type: 'post', url: sharePostUrl };
1807
+ }
1808
+ else {
1809
+ const buttons = ctx.node.getNodeParameter('dmButtons', ctx.itemIndex, {});
1810
+ const buttonList = ((_a = buttons.button) !== null && _a !== void 0 ? _a : [])
1811
+ .filter(button => button.title && button.url)
1812
+ .map(button => ({ title: button.title, url: button.url }));
1813
+ if (buttonList.length > 0)
1814
+ body.buttons = buttonList;
1815
+ Object.assign(body, parseJsonObjectParameter(ctx, 'dmExtraFields', 'Extra Fields (JSON)'));
1816
+ }
1817
+ if (!body.message && !body.sender_action && !body.attachments) {
1818
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), 'Message is required unless you send a Sender Action or share a post.', {
1819
+ itemIndex: ctx.itemIndex,
1820
+ });
1821
+ }
1822
+ return {
1823
+ endpoint: '/uploadposts/dms/send',
1824
+ method: 'POST',
1825
+ body,
1826
+ isUploadOperation: false,
1827
+ waitForCompletion: false,
1828
+ };
1829
+ }
1830
+ case 'listDmConversations': {
1831
+ const qs = { platform, user };
1832
+ const limit = ctx.node.getNodeParameter('dmLimit', ctx.itemIndex, 0);
1833
+ if (limit)
1834
+ qs.limit = limit;
1835
+ const messagesLimit = ctx.node.getNodeParameter('dmMessagesLimit', ctx.itemIndex, 0);
1836
+ if (messagesLimit)
1837
+ qs.messages_limit = messagesLimit;
1838
+ const stringParams = [
1839
+ ['dmCursor', 'cursor'],
1840
+ ['dmSince', 'since'],
1841
+ ['dmParticipantId', 'participant_id'],
1842
+ ];
1843
+ if (platform === 'bluesky') {
1844
+ stringParams.push(['dmConversationFilterId', 'conversation_id'], ['dmMessagesCursor', 'messages_cursor'], ['dmStatus', 'status'], ['dmReadState', 'read_state']);
1845
+ }
1846
+ for (const [param, field] of stringParams) {
1847
+ const value = ctx.node.getNodeParameter(param, ctx.itemIndex, '');
1848
+ if (value)
1849
+ qs[field] = value;
1850
+ }
1851
+ return {
1852
+ endpoint: '/uploadposts/dms/conversations',
1853
+ method: 'GET',
1854
+ qs,
1855
+ isUploadOperation: false,
1856
+ waitForCompletion: false,
1857
+ };
1858
+ }
1859
+ default:
1860
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `Unsupported direct message operation: ${ctx.operation}`, {
1861
+ itemIndex: ctx.itemIndex,
1862
+ });
1863
+ }
1864
+ };
1671
1865
  const buildInstagramRequest = (ctx) => {
1866
+ var _a;
1672
1867
  switch (ctx.operation) {
1673
1868
  case 'getPostComments': {
1674
1869
  const user = ctx.node.getNodeParameter('instagramUser', ctx.itemIndex);
@@ -1689,6 +1884,12 @@ const buildInstagramRequest = (ctx) => {
1689
1884
  const commentId = ctx.node.getNodeParameter('commentsCommentId', ctx.itemIndex, '');
1690
1885
  if (commentId)
1691
1886
  qs.comment_id = commentId;
1887
+ const limit = ctx.node.getNodeParameter('commentsLimit', ctx.itemIndex, 0);
1888
+ if (limit)
1889
+ qs.limit = limit;
1890
+ const after = ctx.node.getNodeParameter('commentsAfter', ctx.itemIndex, '');
1891
+ if (after)
1892
+ qs.after = after;
1692
1893
  return {
1693
1894
  endpoint: '/uploadposts/comments',
1694
1895
  method: 'GET',
@@ -1792,6 +1993,68 @@ const buildInstagramRequest = (ctx) => {
1792
1993
  waitForCompletion: false,
1793
1994
  };
1794
1995
  }
1996
+ case 'createAutodmRule': {
1997
+ const body = {
1998
+ profile_username: ctx.node.getNodeParameter('autodmRuleProfile', ctx.itemIndex),
1999
+ reply_message: ctx.node.getNodeParameter('autodmRuleReplyMessage', ctx.itemIndex),
2000
+ };
2001
+ const triggers = ctx.node.getNodeParameter('autodmRuleTriggers', ctx.itemIndex, ['comment']);
2002
+ if (Array.isArray(triggers) && triggers.length > 0)
2003
+ body.triggers = triggers;
2004
+ const keywords = ensureArrayFromCommaSeparated(ctx.node.getNodeParameter('autodmRuleKeywords', ctx.itemIndex, ''));
2005
+ if (body.triggers && body.triggers.includes('dm_keyword') && keywords.length === 0) {
2006
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), 'The DM Keyword trigger needs at least one Trigger Keyword.', {
2007
+ itemIndex: ctx.itemIndex,
2008
+ });
2009
+ }
2010
+ if (keywords.length > 0)
2011
+ body.trigger_keywords = keywords;
2012
+ const buttons = ctx.node.getNodeParameter('autodmRuleButtons', ctx.itemIndex, {});
2013
+ const buttonList = ((_a = buttons.button) !== null && _a !== void 0 ? _a : [])
2014
+ .filter(button => button.title && button.url)
2015
+ .map(button => ({ title: button.title, url: button.url }));
2016
+ if (buttonList.length > 0)
2017
+ body.buttons = buttonList;
2018
+ const cooldown = ctx.node.getNodeParameter('autodmRuleCooldownHours', ctx.itemIndex, 24);
2019
+ if (!Number.isInteger(cooldown)) {
2020
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), 'Per-User Cooldown (Hours) must be a whole number from 0 to 720.', {
2021
+ itemIndex: ctx.itemIndex,
2022
+ });
2023
+ }
2024
+ body.per_user_cooldown_hours = cooldown;
2025
+ return {
2026
+ endpoint: '/uploadposts/autodms/rules',
2027
+ method: 'POST',
2028
+ body,
2029
+ isUploadOperation: false,
2030
+ waitForCompletion: false,
2031
+ };
2032
+ }
2033
+ case 'listAutodmRules': {
2034
+ const qs = {};
2035
+ const profile = ctx.node.getNodeParameter('autodmRulesListProfile', ctx.itemIndex, '');
2036
+ if (profile)
2037
+ qs.profile_username = profile;
2038
+ return {
2039
+ endpoint: '/uploadposts/autodms/rules',
2040
+ method: 'GET',
2041
+ qs,
2042
+ isUploadOperation: false,
2043
+ waitForCompletion: false,
2044
+ };
2045
+ }
2046
+ case 'pauseAutodmRule':
2047
+ case 'resumeAutodmRule':
2048
+ case 'deleteAutodmRule': {
2049
+ const action = AUTODM_RULE_ACTIONS[ctx.operation];
2050
+ return {
2051
+ endpoint: `/uploadposts/autodms/rules/${action}`,
2052
+ method: 'POST',
2053
+ body: { rule_id: ctx.node.getNodeParameter('autodmRuleId', ctx.itemIndex) },
2054
+ isUploadOperation: false,
2055
+ waitForCompletion: false,
2056
+ };
2057
+ }
1795
2058
  default:
1796
2059
  throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `Unsupported Instagram operation: ${ctx.operation}`, {
1797
2060
  itemIndex: ctx.itemIndex,
@@ -1870,6 +2133,9 @@ const buildRequestConfig = async (ctx) => {
1870
2133
  if (resource === 'insights') {
1871
2134
  return buildInsightsRequest(ctx);
1872
2135
  }
2136
+ if (resource === 'dms') {
2137
+ return buildDmRequest(ctx);
2138
+ }
1873
2139
  if (resource === 'monitoring') {
1874
2140
  return buildMonitoringRequest(ctx);
1875
2141
  }
@@ -1952,7 +2218,7 @@ class UploadPost {
1952
2218
  this.description = {
1953
2219
  displayName: 'Upload Post',
1954
2220
  name: 'uploadPost',
1955
- icon: 'file:uploadpost.svg',
2221
+ icon: { light: 'file:uploadpost.svg', dark: 'file:uploadpost.svg' },
1956
2222
  group: ['output'],
1957
2223
  version: 1,
1958
2224
  subtitle: '={{$parameter["operation"]}}',
@@ -1977,6 +2243,7 @@ class UploadPost {
1977
2243
  noDataExpression: true,
1978
2244
  options: [
1979
2245
  { name: 'Comment', value: 'instagram' },
2246
+ { name: 'Direct Message', value: 'dms' },
1980
2247
  { name: 'Insight', value: 'insights' },
1981
2248
  { name: 'Post Action', value: 'postActions' },
1982
2249
  { name: 'Status & History', value: 'monitoring' },
@@ -2013,7 +2280,7 @@ class UploadPost {
2013
2280
  { name: 'Get Job Status', value: 'getJobStatus', action: 'Get job status', description: 'Check the status of a scheduled or queued post using the job_id' },
2014
2281
  { name: 'Get Media', value: 'getMedia', action: 'Get media', description: 'List published media for a connected network (LinkedIn, Facebook, Instagram, YouTube, and others the API exposes)' },
2015
2282
  { name: 'Get Platform Metrics', value: 'getPlatformMetrics', action: 'Get platform metrics', description: 'List the analytics metrics available for each platform' },
2016
- { name: 'Get Post Analytics', value: 'getPostAnalytics', action: 'Get post analytics', description: 'Retrieve per-post analytics for an upload using its request_id. On TikTok post_metrics also carries retention, impression_sources, audience_types, new_followers, reach and the watch times.' },
2283
+ { name: 'Get Post Analytics', value: 'getPostAnalytics', action: 'Get post analytics', description: 'Retrieve per-post analytics for an upload using its request_id. On TikTok post_metrics also carries retention, impression_sources, audience_types, new_followers, reach and the watch times, plus original_publish_id. A TikTok draft gets its metrics once the creator publishes it as public from the TikTok app (TikTok gives no post ID for private posts); after 30 days without a post use Get Post Analytics by Platform ID.' },
2017
2284
  { name: 'Get Post Analytics by Platform ID', value: 'getPostAnalyticsByPlatformId', action: 'Get post analytics by platform id', description: 'Retrieve per-post analytics using the native platform post ID, for posts not published through Upload-Post' },
2018
2285
  { name: 'Get Reddit Detailed Posts', value: 'getRedditDetailedPosts', action: 'Get reddit detailed posts', description: 'Retrieve detailed Reddit posts with full media information' },
2019
2286
  { name: 'Get TikTok Locations', value: 'getTiktokLocations', action: 'Get tiktok locations', description: 'Search TikTok locations for a profile to use as tiktok_location_id' },
@@ -2023,6 +2290,7 @@ class UploadPost {
2023
2290
  { name: 'Get Upload History', value: 'getHistory', action: 'Get upload history', description: 'List past uploads with optional filters' },
2024
2291
  { name: 'Get Upload Status', value: 'getStatus', action: 'Get upload status', description: 'Check the status of an upload using the request_id' },
2025
2292
  { name: 'List Scheduled Posts', value: 'listScheduled', action: 'List scheduled posts', description: 'List your scheduled (future) posts' },
2293
+ { name: 'Resolve LinkedIn Organization', value: 'resolveLinkedinOrganization', action: 'Resolve a linkedin organization', description: 'Turn any LinkedIn company, showcase or school Page URL, vanity name or ID into its URN and exact name, ready to mention in a post as @[Exact Name](urn:li:organization:ID). Works for any Page, not only the ones you administer.' },
2026
2294
  { name: 'Search TikTok Music', value: 'searchTiktokMusic', action: 'Search tiktok music', description: 'Search TikTok music by query for a connected profile' },
2027
2295
  ],
2028
2296
  default: 'getStatus',
@@ -2039,9 +2307,12 @@ class UploadPost {
2039
2307
  { name: 'Generate JWT (for Platform Integration)', value: 'generateJwt', action: 'Generate jwt for platform integration', description: 'Generate a connection URL (JWT) for a profile. Only needed when integrating Upload-Post into your own platform.' },
2040
2308
  { name: 'Get Notification Preferences', value: 'getNotificationPrefs', action: 'Get notification preferences', description: 'Get current webhook and notification settings' },
2041
2309
  { name: 'Get User Preferences', value: 'getUserPreferences', action: 'Get user preferences', description: 'Get user preferences including calendar week start day' },
2310
+ { name: 'Get Webhook Delivery', value: 'getWebhookDelivery', action: 'Get a webhook delivery', description: 'Read one logged webhook delivery with its attempts and the exact payload that was sent' },
2042
2311
  { name: 'List Users', value: 'listUsers', action: 'List users', description: 'List Upload-Post users (profiles)' },
2312
+ { name: 'List Webhook Deliveries', value: 'listWebhookDeliveries', action: 'List webhook deliveries', description: 'History of the last 7 days of outgoing webhooks (account and per-profile): status, attempts and the HTTP code your endpoint answered. Use it to check whether an event reached your server.' },
2313
+ { name: 'Redeliver Webhook', value: 'redeliverWebhook', action: 'Redeliver a webhook', description: 'Send a logged delivery again now, with the same body, to the webhook URL configured today. It is a new delivery with its own ID (header X-Upload-Post-Redelivery-Of names the original).' },
2043
2314
  { name: 'Test Notifications', value: 'testNotifications', action: 'Test notifications', description: 'Send a test event to the configured webhook and notification channels' },
2044
- { name: 'Update Notification Preferences', value: 'updateNotificationPrefs', action: 'Update notification preferences', description: 'Configure webhook URL and event types for real-time notifications (upload_completed, social_account.connected, social_account.disconnected, social_account.reauth_required)' },
2315
+ { name: 'Update Notification Preferences', value: 'updateNotificationPrefs', action: 'Update notification preferences', description: 'Configure webhook URL and event types for real-time notifications (upload_completed, social_account.connected, social_account.disconnected, social_account.reauth_required, dm_received, comment_received). Webhooks are delivered at least once with automatic retries: deduplicate by the X-Upload-Post-Delivery header.' },
2045
2316
  { name: 'Update User Preferences', value: 'updateUserPreferences', action: 'Update user preferences', description: 'Update user preferences including calendar week start day (0=Sunday, 1=Monday)' },
2046
2317
  { name: 'Validate JWT (for Platform Integration)', value: 'validateJwt', action: 'Validate jwt for platform integration', description: 'Validate a connection token from your backend. Only needed for custom platform integration.' },
2047
2318
  ],
@@ -2055,11 +2326,16 @@ class UploadPost {
2055
2326
  noDataExpression: true,
2056
2327
  options: [
2057
2328
  { name: 'Comment Action', value: 'commentAction', action: 'Act on a comment', description: 'Moderate one comment: hide, unhide, like, unlike, pin or unpin it. Platform says which connected network the comment lives on. Post ID is required to hide, unhide, pin or unpin, and is never sent for like or unlike.' },
2329
+ { name: 'Create AutoDM Rule', value: 'createAutodmRule', action: 'Create an account wide autodm rule', description: 'Instagram: answer with a DM every comment on any post or reel of the account (present and future), story replies, story mentions and DMs with a keyword. Rules never expire and work from Meta webhooks only.' },
2058
2330
  { name: 'Create Comment', value: 'createComment', action: 'Create a comment', description: 'Post a comment or a reply. Instagram only supports replies (comment ID required). TikTok always needs the video ID, plus comment ID to reply.' },
2331
+ { name: 'Delete AutoDM Rule', value: 'deleteAutodmRule', action: 'Delete an autodm rule', description: 'Delete an account-wide AutoDM rule and drop its queued DMs' },
2059
2332
  { name: 'Delete Comment', value: 'deleteComment', action: 'Delete a comment', description: 'Delete a comment you own or that is on your post' },
2060
- { name: 'Get Post Comments', value: 'getPostComments', action: 'Get post comments', description: 'Retrieve the comments on a post from the network given by Platform (Instagram, Facebook, YouTube, LinkedIn or TikTok). Fill Comment ID to get the replies to that comment instead of the top-level comments of the post.' },
2333
+ { name: 'Get Post Comments', value: 'getPostComments', action: 'Get post comments', description: 'Retrieve the comments on a post from the network given by Platform. Fill Comment ID to get the replies to that comment instead (Instagram, Facebook, LinkedIn company Pages, TikTok, Threads and YouTube; X and Bluesky answer platform_not_supported).' },
2334
+ { name: 'List AutoDM Rules', value: 'listAutodmRules', action: 'List autodm rules', description: 'List the account-wide AutoDM rules with their status, stats, queued DMs and whether Meta webhooks are arriving for each Instagram account' },
2335
+ { name: 'Pause AutoDM Rule', value: 'pauseAutodmRule', action: 'Pause an autodm rule', description: 'Pause an account-wide AutoDM rule: it answers nothing until resumed' },
2061
2336
  { name: 'Private Reply to Comment', value: 'privateReplyToComment', action: 'Private reply to comment', description: 'Send a private reply (DM) to the author of a comment. Instagram only.' },
2062
2337
  { name: 'Public Reply to Comment', value: 'publicReplyToComment', action: 'Public reply to comment', description: 'Post a public reply visible under the original comment. Instagram only.' },
2338
+ { name: 'Resume AutoDM Rule', value: 'resumeAutodmRule', action: 'Resume an autodm rule', description: 'Resume a paused account-wide AutoDM rule. A stopped rule (its Instagram account left the profile) only comes back once the account is connected to that profile again.' },
2063
2339
  ],
2064
2340
  default: 'getPostComments',
2065
2341
  displayOptions: { show: { resource: ['instagram'] } },
@@ -2108,7 +2384,34 @@ class UploadPost {
2108
2384
  name: 'commentsCommentId',
2109
2385
  type: 'string',
2110
2386
  default: '',
2111
- description: 'Leave empty for the top-level comments of the post. Set it to a comment ID to get the replies to that comment instead: it is the same question with one parameter more.',
2387
+ description: 'Leave empty for the top-level comments of the post. Set it to a comment ID to get the replies to that comment instead (Instagram, Facebook, LinkedIn, TikTok, Threads, YouTube). Instagram: the numeric comment ID. Facebook: the {post}_{comment} ID from the listing (Post ID is still needed to pick the Page). LinkedIn: preferably the comment_urn from the listing; a bare ID is only looked up among the first 250 comments of the post. Post ID is always required.',
2388
+ displayOptions: {
2389
+ show: {
2390
+ resource: ['instagram'],
2391
+ operation: ['getPostComments'],
2392
+ },
2393
+ },
2394
+ },
2395
+ {
2396
+ displayName: 'Page Size',
2397
+ name: 'commentsLimit',
2398
+ type: 'number',
2399
+ typeOptions: { minValue: 0, maxValue: 50 },
2400
+ default: 0,
2401
+ description: 'Comments per page, 1-50. 0 = the platform default. LinkedIn\'s top-level listing ignores it; LinkedIn replies apply it.',
2402
+ displayOptions: {
2403
+ show: {
2404
+ resource: ['instagram'],
2405
+ operation: ['getPostComments'],
2406
+ },
2407
+ },
2408
+ },
2409
+ {
2410
+ displayName: 'After (Cursor)',
2411
+ name: 'commentsAfter',
2412
+ type: 'string',
2413
+ default: '',
2414
+ description: 'The pagination.next_cursor value of the previous page, to read the next one. On LinkedIn replies it is a number (the offset of the next page).',
2112
2415
  displayOptions: {
2113
2416
  show: {
2114
2417
  resource: ['instagram'],
@@ -2257,6 +2560,338 @@ class UploadPost {
2257
2560
  },
2258
2561
  },
2259
2562
  },
2563
+ {
2564
+ displayName: 'User Identifier Name or ID',
2565
+ name: 'autodmRuleProfile',
2566
+ type: 'options',
2567
+ noDataExpression: true,
2568
+ required: true,
2569
+ default: '',
2570
+ description: 'Profile with the Instagram professional account connected. Choose from your created profiles. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
2571
+ typeOptions: { loadOptionsMethod: 'getUserProfiles' },
2572
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2573
+ },
2574
+ {
2575
+ displayName: 'Reply Message',
2576
+ name: 'autodmRuleReplyMessage',
2577
+ type: 'string',
2578
+ required: true,
2579
+ default: '',
2580
+ description: 'The DM text (max 1,000 characters; 640 when buttons are added)',
2581
+ typeOptions: { rows: 3 },
2582
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2583
+ },
2584
+ {
2585
+ displayName: 'Triggers',
2586
+ name: 'autodmRuleTriggers',
2587
+ type: 'multiOptions',
2588
+ options: [
2589
+ { name: 'Comment', value: 'comment', description: 'A top-level comment on any post or reel of the account (private reply, one per comment, within 7 days)' },
2590
+ { name: 'DM Keyword', value: 'dm_keyword', description: 'Someone sends a DM containing one of the Trigger Keywords (required for this trigger)' },
2591
+ { name: 'Story Mention', value: 'story_mention', description: 'Someone mentions the account in their story (keywords do not apply: a mention carries no text)' },
2592
+ { name: 'Story Reply', value: 'story_reply', description: 'Someone replies to one of the account\'s stories' },
2593
+ ],
2594
+ default: ['comment'],
2595
+ description: 'What fires the rule. Story replies, story mentions and keyword DMs are answered inside Meta\'s 24-hour messaging window.',
2596
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2597
+ },
2598
+ {
2599
+ displayName: 'Trigger Keywords',
2600
+ name: 'autodmRuleKeywords',
2601
+ type: 'string',
2602
+ default: '',
2603
+ placeholder: 'guide, price, info',
2604
+ description: 'Comma-separated, up to 20. Only events whose text contains one of them fire the rule (case- and accent-insensitive). Leave empty for a rule without a filter. Required for the DM Keyword trigger.',
2605
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2606
+ },
2607
+ {
2608
+ displayName: 'Buttons',
2609
+ name: 'autodmRuleButtons',
2610
+ type: 'fixedCollection',
2611
+ typeOptions: { multipleValues: true, maxValue: 3 },
2612
+ default: {},
2613
+ placeholder: 'Add Button',
2614
+ description: 'Up to 3 link buttons added to the DM',
2615
+ options: [
2616
+ {
2617
+ name: 'button',
2618
+ displayName: 'Button',
2619
+ values: [
2620
+ { displayName: 'Title', name: 'title', type: 'string', default: '', description: 'Button label (max 20 characters)' },
2621
+ { displayName: 'URL', name: 'url', type: 'string', default: '', description: 'Link (http or https) the button opens' },
2622
+ ],
2623
+ },
2624
+ ],
2625
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2626
+ },
2627
+ {
2628
+ displayName: 'Per-User Cooldown (Hours)',
2629
+ name: 'autodmRuleCooldownHours',
2630
+ type: 'number',
2631
+ typeOptions: { minValue: 0, maxValue: 720, numberPrecision: 0 },
2632
+ default: 24,
2633
+ description: 'The same person gets at most one DM from this rule in this many hours, whatever they comment on or reply to. Whole number from 0 to 720. 0 turns it off for comments only: story replies, story mentions and keyword DMs always wait at least 24 hours, so two bots cannot answer each other in a loop.',
2634
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2635
+ },
2636
+ {
2637
+ displayName: 'Profile Username',
2638
+ name: 'autodmRulesListProfile',
2639
+ type: 'string',
2640
+ default: '',
2641
+ description: 'Only the rules of this profile. Leave empty for every rule of the account.',
2642
+ displayOptions: { show: { resource: ['instagram'], operation: ['listAutodmRules'] } },
2643
+ },
2644
+ {
2645
+ displayName: 'Rule ID',
2646
+ name: 'autodmRuleId',
2647
+ type: 'string',
2648
+ required: true,
2649
+ default: '',
2650
+ placeholder: 'rule_4f1c2a9e0b7d4c51a2e3f6b8',
2651
+ description: 'The rule_id returned by Create AutoDM Rule or List AutoDM Rules',
2652
+ displayOptions: { show: { resource: ['instagram'], operation: ['pauseAutodmRule', 'resumeAutodmRule', 'deleteAutodmRule'] } },
2653
+ },
2654
+ {
2655
+ displayName: 'Operation',
2656
+ name: 'operation',
2657
+ type: 'options',
2658
+ noDataExpression: true,
2659
+ options: [
2660
+ { name: 'List Conversations', value: 'listDmConversations', action: 'List DM conversations', description: 'List the DM conversations of the account connected to a profile (Instagram or Bluesky), newest first, with participants and recent messages' },
2661
+ { name: 'Send DM', value: 'sendDm', action: 'Send a direct message', description: 'Send a direct message, or a sender action (mark read, react...), from the Instagram or Bluesky account of a profile. Sender actions spend no daily DM quota.' },
2662
+ ],
2663
+ default: 'sendDm',
2664
+ displayOptions: { show: { resource: ['dms'] } },
2665
+ },
2666
+ {
2667
+ displayName: 'User Identifier Name or ID',
2668
+ name: 'dmUser',
2669
+ type: 'options',
2670
+ noDataExpression: true,
2671
+ required: true,
2672
+ default: '',
2673
+ description: 'Choose from your created profiles. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
2674
+ typeOptions: { loadOptionsMethod: 'getUserProfiles' },
2675
+ displayOptions: { show: { resource: ['dms'] } },
2676
+ },
2677
+ {
2678
+ displayName: 'Platform',
2679
+ name: 'dmPlatform',
2680
+ type: 'options',
2681
+ options: [
2682
+ { name: 'Bluesky', value: 'bluesky' },
2683
+ { name: 'Instagram', value: 'instagram' },
2684
+ ],
2685
+ default: 'instagram',
2686
+ description: 'Network of the account. Bluesky needs an app password created with "Allow access to your direct messages" (otherwise 403 bluesky_dm_scope_missing: create a new app password and reconnect Bluesky). Instagram needs the person to have written to the account in the last 24 hours.',
2687
+ displayOptions: { show: { resource: ['dms'] } },
2688
+ },
2689
+ {
2690
+ displayName: 'Recipient ID',
2691
+ name: 'dmRecipientId',
2692
+ type: 'string',
2693
+ default: '',
2694
+ description: 'Instagram: the Instagram-scoped ID of the person (from List Conversations or the dm_received webhook). Bluesky: handle (alice.bsky.social), DID (did:plc:...) or profile link; leave empty when Conversation ID is set.',
2695
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'] } },
2696
+ },
2697
+ {
2698
+ displayName: 'Conversation ID',
2699
+ name: 'dmConversationId',
2700
+ type: 'string',
2701
+ default: '',
2702
+ description: 'Bluesky: send to this conversation (the ID from List Conversations) instead of looking it up from Recipient ID. Also works for group conversations.',
2703
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['bluesky'] } },
2704
+ },
2705
+ {
2706
+ displayName: 'Message',
2707
+ name: 'dmMessage',
2708
+ type: 'string',
2709
+ default: '',
2710
+ description: 'Text to send. Required unless you send a Sender Action or share a Bluesky post. Bluesky: up to 1,000 characters (an emoji counts as one); links, #hashtags and up to 10 different @handle mentions become clickable.',
2711
+ typeOptions: { rows: 3 },
2712
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'] } },
2713
+ },
2714
+ {
2715
+ displayName: 'Reply To (Message ID)',
2716
+ name: 'dmReplyTo',
2717
+ type: 'string',
2718
+ default: '',
2719
+ description: 'ID of a message in the same conversation to reply to',
2720
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'] } },
2721
+ },
2722
+ {
2723
+ displayName: 'Sender Action',
2724
+ name: 'dmInstagramSenderAction',
2725
+ type: 'options',
2726
+ options: [
2727
+ { name: 'Mark Seen', value: 'mark_seen' },
2728
+ { name: 'None', value: '' },
2729
+ { name: 'React', value: 'react' },
2730
+ { name: 'Typing Off', value: 'typing_off' },
2731
+ { name: 'Typing On', value: 'typing_on' },
2732
+ { name: 'Unreact', value: 'unreact' },
2733
+ ],
2734
+ default: '',
2735
+ description: 'Instead of (or with) a message. React and Unreact need Message ID.',
2736
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['instagram'] } },
2737
+ },
2738
+ {
2739
+ displayName: 'Sender Action',
2740
+ name: 'dmBlueskySenderAction',
2741
+ type: 'options',
2742
+ options: [
2743
+ { name: 'Accept Request', value: 'accept' },
2744
+ { name: 'Delete Message (For Me)', value: 'delete' },
2745
+ { name: 'Leave Conversation', value: 'leave' },
2746
+ { name: 'Mark Read', value: 'mark_read' },
2747
+ { name: 'Mute', value: 'mute' },
2748
+ { name: 'None', value: '' },
2749
+ { name: 'React', value: 'react' },
2750
+ { name: 'Unmute', value: 'unmute' },
2751
+ { name: 'Unreact', value: 'unreact' },
2752
+ ],
2753
+ default: '',
2754
+ description: 'An action on the conversation instead of a message. React, Unreact and Delete need Message ID; Mark Read takes it optionally (read up to that message). Pass Recipient ID or Conversation ID to say which conversation.',
2755
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['bluesky'] } },
2756
+ },
2757
+ {
2758
+ displayName: 'Message ID',
2759
+ name: 'dmMessageId',
2760
+ type: 'string',
2761
+ default: '',
2762
+ description: 'The message the sender action applies to',
2763
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'] } },
2764
+ },
2765
+ {
2766
+ displayName: 'Reaction',
2767
+ name: 'dmReaction',
2768
+ type: 'string',
2769
+ default: '',
2770
+ placeholder: '❤️',
2771
+ description: 'Emoji for React / Unreact. Bluesky accepts a single emoji only (anything else: 400 bluesky_rejected, ReactionInvalidValue).',
2772
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'] } },
2773
+ },
2774
+ {
2775
+ displayName: 'Share Bluesky Post URL',
2776
+ name: 'dmBlueskySharePostUrl',
2777
+ type: 'string',
2778
+ default: '',
2779
+ placeholder: 'https://bsky.app/profile/alice.bsky.social/post/3l5tt...',
2780
+ description: 'A Bluesky post to share in the DM (bsky.app link or at:// URI). Bluesky chat takes no images, video or files.',
2781
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['bluesky'] } },
2782
+ },
2783
+ {
2784
+ displayName: 'Buttons',
2785
+ name: 'dmButtons',
2786
+ type: 'fixedCollection',
2787
+ typeOptions: { multipleValues: true, maxValue: 3 },
2788
+ default: {},
2789
+ placeholder: 'Add Button',
2790
+ description: 'Up to 3 link buttons (Instagram)',
2791
+ options: [
2792
+ {
2793
+ name: 'button',
2794
+ displayName: 'Button',
2795
+ values: [
2796
+ { displayName: 'Title', name: 'title', type: 'string', default: '', description: 'Button label (max 20 characters)' },
2797
+ { displayName: 'URL', name: 'url', type: 'string', default: '', description: 'Link (http or https) the button opens' },
2798
+ ],
2799
+ },
2800
+ ],
2801
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['instagram'] } },
2802
+ },
2803
+ {
2804
+ displayName: 'Extra Fields (JSON)',
2805
+ name: 'dmExtraFields',
2806
+ type: 'json',
2807
+ default: '',
2808
+ description: 'Other Instagram DM fields, merged into the request body as is: attachments (objects with a type and a link or an attachment_id; types image, video, audio, file, like_heart, media_share), quick_replies (up to 13) or generic_template (up to 10 elements)',
2809
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['instagram'] } },
2810
+ },
2811
+ {
2812
+ displayName: 'Page Size',
2813
+ name: 'dmLimit',
2814
+ type: 'number',
2815
+ typeOptions: { minValue: 0, maxValue: 50 },
2816
+ default: 0,
2817
+ description: 'Conversations per page, 1-50 (Bluesky returns at most 25). 0 = the default (20).',
2818
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'] } },
2819
+ },
2820
+ {
2821
+ displayName: 'Messages per Conversation',
2822
+ name: 'dmMessagesLimit',
2823
+ type: 'number',
2824
+ typeOptions: { minValue: 0, maxValue: 25 },
2825
+ default: 0,
2826
+ description: 'Latest messages returned per conversation, 1-25. 0 = the default (10). On Bluesky 1 is the fastest page.',
2827
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'] } },
2828
+ },
2829
+ {
2830
+ displayName: 'Cursor',
2831
+ name: 'dmCursor',
2832
+ type: 'string',
2833
+ default: '',
2834
+ description: 'The pagination.next_cursor value of the previous page',
2835
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'] } },
2836
+ },
2837
+ {
2838
+ displayName: 'Since',
2839
+ name: 'dmSince',
2840
+ type: 'string',
2841
+ default: '',
2842
+ placeholder: '2026-10-20T12:00:00Z',
2843
+ description: 'Only conversations with activity after this moment (Unix seconds, Unix milliseconds or ISO 8601)',
2844
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'] } },
2845
+ },
2846
+ {
2847
+ displayName: 'Participant ID',
2848
+ name: 'dmParticipantId',
2849
+ type: 'string',
2850
+ default: '',
2851
+ description: 'Only your conversation with this person. Instagram: Instagram-scoped ID. Bluesky: handle or DID (reading never creates the conversation).',
2852
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'] } },
2853
+ },
2854
+ {
2855
+ displayName: 'Conversation ID',
2856
+ name: 'dmConversationFilterId',
2857
+ type: 'string',
2858
+ default: '',
2859
+ description: 'Bluesky: only this conversation (direct or group)',
2860
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'], dmPlatform: ['bluesky'] } },
2861
+ },
2862
+ {
2863
+ displayName: 'Messages Cursor',
2864
+ name: 'dmMessagesCursor',
2865
+ type: 'string',
2866
+ default: '',
2867
+ description: 'Bluesky, with Conversation ID: messages.paging.next_cursor of that conversation, to read older messages',
2868
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'], dmPlatform: ['bluesky'] } },
2869
+ },
2870
+ {
2871
+ displayName: 'Status',
2872
+ name: 'dmStatus',
2873
+ type: 'options',
2874
+ options: [
2875
+ { name: 'All', value: '' },
2876
+ { name: 'Accepted', value: 'accepted' },
2877
+ { name: 'Requests', value: 'request' },
2878
+ ],
2879
+ default: '',
2880
+ description: 'Bluesky: only message requests or only accepted conversations',
2881
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'], dmPlatform: ['bluesky'] } },
2882
+ },
2883
+ {
2884
+ displayName: 'Read State',
2885
+ name: 'dmReadState',
2886
+ type: 'options',
2887
+ options: [
2888
+ { name: 'All', value: '' },
2889
+ { name: 'Unread', value: 'unread' },
2890
+ ],
2891
+ default: '',
2892
+ description: 'Bluesky: only conversations with unread messages',
2893
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'], dmPlatform: ['bluesky'] } },
2894
+ },
2260
2895
  {
2261
2896
  displayName: 'Operation',
2262
2897
  name: 'operation',
@@ -2602,7 +3237,7 @@ class UploadPost {
2602
3237
  name: 'linkedinTitle',
2603
3238
  type: 'string',
2604
3239
  default: '',
2605
- description: 'Optional override for LinkedIn title',
3240
+ description: 'Optional override for LinkedIn title (the post text of Upload Text). LinkedIn mentions: @[Exact Page Name](urn:li:organization:ID), @[Name](urn:li:person:ID) or @company-vanity. A company name must match LinkedIn exactly, capitals included, or it is published as plain text; get the URN and name with Resolve LinkedIn Organization.',
2606
3241
  displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['linkedin', '__manual_platform__'] } },
2607
3242
  },
2608
3243
  {
@@ -2743,7 +3378,7 @@ class UploadPost {
2743
3378
  name: 'linkedinDescription',
2744
3379
  type: 'string',
2745
3380
  default: '',
2746
- description: 'Override for LinkedIn post commentary. When empty we repeat the main title.',
3381
+ description: 'Override for LinkedIn post commentary. When empty we repeat the main title. Put mentions here rather than only in the title (the title also becomes the images\' alt text). LinkedIn mentions: @[Exact Page Name](urn:li:organization:ID), @[Name](urn:li:person:ID) or @company-vanity. A company name must match LinkedIn exactly, capitals included, or it is published as plain text; get the URN and name with Resolve LinkedIn Organization.',
2747
3382
  displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo'], platform: ['linkedin', '__manual_platform__'] } },
2748
3383
  },
2749
3384
  {
@@ -3362,6 +3997,27 @@ class UploadPost {
3362
3997
  description: 'Pagination cursor from a previous Get Media response',
3363
3998
  displayOptions: { show: { operation: ['getMedia'] } },
3364
3999
  },
4000
+ {
4001
+ displayName: 'User Identifier Name or ID',
4002
+ name: 'linkedinResolveProfile',
4003
+ type: 'options',
4004
+ noDataExpression: true,
4005
+ required: true,
4006
+ default: '',
4007
+ description: 'Profile whose LinkedIn connection makes the lookup. Choose from your created profiles. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
4008
+ typeOptions: { loadOptionsMethod: 'getUserProfiles' },
4009
+ displayOptions: { show: { operation: ['resolveLinkedinOrganization'] } },
4010
+ },
4011
+ {
4012
+ displayName: 'LinkedIn Page',
4013
+ name: 'linkedinResolveUrl',
4014
+ type: 'string',
4015
+ required: true,
4016
+ default: '',
4017
+ placeholder: 'https://www.linkedin.com/company/linkedin/',
4018
+ description: 'Company, showcase or school Page URL, vanity name (with or without @), numeric ID or urn:li:organization:ID. Person profiles (/in/) cannot be resolved. Live lookups count against 60 per hour per account; cached Pages are free.',
4019
+ displayOptions: { show: { operation: ['resolveLinkedinOrganization'] } },
4020
+ },
3365
4021
  {
3366
4022
  displayName: 'User Identifier Name or ID',
3367
4023
  name: 'gbpLocationsProfile',
@@ -3595,6 +4251,97 @@ class UploadPost {
3595
4251
  description: 'JWT to validate',
3596
4252
  displayOptions: { show: { operation: ['validateJwt'] } },
3597
4253
  },
4254
+ {
4255
+ displayName: 'Event',
4256
+ name: 'webhookDeliveriesEvent',
4257
+ type: 'options',
4258
+ options: [
4259
+ { name: 'Account Connected', value: 'social_account_connected' },
4260
+ { name: 'Account Disconnected', value: 'social_account_disconnected' },
4261
+ { name: 'All Events', value: '' },
4262
+ { name: 'Comment Received', value: 'comment_received' },
4263
+ { name: 'DM Received', value: 'dm_received' },
4264
+ { name: 'Re-Auth Required', value: 'social_account_reauth_required' },
4265
+ { name: 'Upload Completed', value: 'upload_completed' },
4266
+ ],
4267
+ default: '',
4268
+ description: 'Only deliveries of this event',
4269
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4270
+ },
4271
+ {
4272
+ displayName: 'Status',
4273
+ name: 'webhookDeliveriesStatus',
4274
+ type: 'options',
4275
+ options: [
4276
+ { name: 'All Statuses', value: '' },
4277
+ { name: 'Cancelled', value: 'cancelled', description: 'The webhook or the event subscription was removed' },
4278
+ { name: 'Delivered', value: 'delivered' },
4279
+ { name: 'Failed', value: 'failed', description: 'Gave up after 6 attempts, or refused with a 4xx' },
4280
+ { name: 'Pending', value: 'pending', description: 'A retry is scheduled' },
4281
+ ],
4282
+ default: '',
4283
+ description: 'Only deliveries in this state',
4284
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4285
+ },
4286
+ {
4287
+ displayName: 'Request ID',
4288
+ name: 'webhookDeliveriesRequestId',
4289
+ type: 'string',
4290
+ default: '',
4291
+ description: 'Only the deliveries of one upload (request_id of the upload response)',
4292
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4293
+ },
4294
+ {
4295
+ displayName: 'Job ID',
4296
+ name: 'webhookDeliveriesJobId',
4297
+ type: 'string',
4298
+ default: '',
4299
+ description: 'Only the deliveries of one scheduled or async job',
4300
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4301
+ },
4302
+ {
4303
+ displayName: 'Profile Username',
4304
+ name: 'webhookDeliveriesProfile',
4305
+ type: 'string',
4306
+ default: '',
4307
+ description: 'Only the deliveries of that profile\'s webhook',
4308
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4309
+ },
4310
+ {
4311
+ displayName: 'Page Size',
4312
+ name: 'webhookDeliveriesLimit',
4313
+ type: 'number',
4314
+ typeOptions: { minValue: 1, maxValue: 100 },
4315
+ default: 20,
4316
+ description: 'Deliveries per page, 1-100, newest first',
4317
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4318
+ },
4319
+ {
4320
+ displayName: 'Page',
4321
+ name: 'webhookDeliveriesPage',
4322
+ type: 'number',
4323
+ typeOptions: { minValue: 1, maxValue: 50 },
4324
+ default: 1,
4325
+ description: 'Page number, 1-50. For older rows narrow the list with Status, Event or Request ID.',
4326
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4327
+ },
4328
+ {
4329
+ displayName: 'Include Payload',
4330
+ name: 'webhookDeliveriesIncludePayload',
4331
+ type: 'boolean',
4332
+ default: false,
4333
+ description: 'Whether to include the exact body that was sent with each delivery (left out by default to keep the list light; Get Webhook Delivery always includes it)',
4334
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4335
+ },
4336
+ {
4337
+ displayName: 'Delivery ID',
4338
+ name: 'webhookDeliveryId',
4339
+ type: 'string',
4340
+ required: true,
4341
+ default: '',
4342
+ description: 'The delivery_id from List Webhook Deliveries (the X-Upload-Post-Delivery header your endpoint received). Deliveries are kept for 7 days.',
4343
+ displayOptions: { show: { resource: ['users'], operation: ['getWebhookDelivery', 'redeliverWebhook'] } },
4344
+ },
3598
4345
  {
3599
4346
  displayName: 'Webhook Enabled',
3600
4347
  name: 'webhookEnabled',
@@ -3617,10 +4364,12 @@ class UploadPost {
3617
4364
  name: 'webhookEvents',
3618
4365
  type: 'multiOptions',
3619
4366
  options: [
3620
- { name: 'Upload Completed', value: 'upload_completed', description: 'When a post upload finishes (success or failure)' },
3621
4367
  { name: 'Account Connected', value: 'social_account.connected', description: 'When a social account is connected or reconnected' },
3622
4368
  { name: 'Account Disconnected', value: 'social_account.disconnected', description: 'When a social account is disconnected' },
4369
+ { name: 'Comment Received', value: 'comment_received', description: 'When a comment arrives on a connected Instagram, TikTok or LinkedIn Page account' },
4370
+ { name: 'DM Received', value: 'dm_received', description: 'When a direct message arrives on a connected Instagram or Bluesky account (Bluesky is checked every 5 minutes)' },
3623
4371
  { name: 'Re-Auth Required', value: 'social_account.reauth_required', description: 'When a social account needs re-authentication' },
4372
+ { name: 'Upload Completed', value: 'upload_completed', description: 'When a post upload finishes (success or failure)' },
3624
4373
  ],
3625
4374
  default: ['upload_completed', 'social_account.connected', 'social_account.disconnected', 'social_account.reauth_required'],
3626
4375
  description: 'Which webhook events to subscribe to',
@@ -4651,6 +5400,14 @@ class UploadPost {
4651
5400
  description: 'Language code for the Google Business post (e.g. en, es)',
4652
5401
  displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['google_business', '__manual_platform__'] } },
4653
5402
  },
5403
+ {
5404
+ displayName: 'Bluesky Gallery',
5405
+ name: 'blueskyGallery',
5406
+ type: 'boolean',
5407
+ default: false,
5408
+ description: 'Whether to publish up to 10 images as a Bluesky gallery (app.bsky.embed.gallery). Without it Bluesky takes the first 4 images; with more than 10 the first 10 are published and the response carries a warning.',
5409
+ displayOptions: { show: { operation: ['uploadPhotos'], platform: ['bluesky', '__manual_platform__'] } },
5410
+ },
4654
5411
  {
4655
5412
  displayName: 'Bluesky Alt Text',
4656
5413
  name: 'blueskyAltText',