@publishq/sdk 0.0.1 → 0.0.3
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 +6 -6
- package/dist/index.cjs +280 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +798 -27
- package/dist/index.d.ts +798 -27
- package/dist/index.js +267 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ for (const account of data?.accounts ?? []) {
|
|
|
40
40
|
## Authentication
|
|
41
41
|
|
|
42
42
|
Pass your API key (starts with `pq_live_`). Create one in the PublishQ web app
|
|
43
|
-
under **
|
|
43
|
+
under **API Keys** and select the scopes you need.
|
|
44
44
|
|
|
45
45
|
```ts
|
|
46
46
|
const publishq = new PublishQ({ apiKey: 'pq_live_...' });
|
|
@@ -79,7 +79,7 @@ await publishq.posts.removeAccount('<post-id>', '<account-id>');
|
|
|
79
79
|
// List connected social accounts (filter by workspace / platform)
|
|
80
80
|
const { data } = await publishq.accounts.list({ platform: ['TIKTOK'] });
|
|
81
81
|
|
|
82
|
-
// TikTok requires fetching creator info before posting
|
|
82
|
+
// TikTok requires fetching creator info before posting. The privacyLevel you
|
|
83
83
|
// set must be one of the returned privacyLevelOptions.
|
|
84
84
|
const { data: info } = await publishq.accounts.getTikTokCreatorInfo('<account-id>');
|
|
85
85
|
```
|
|
@@ -95,7 +95,7 @@ await publishq.workspaces.get('<workspace-id>');
|
|
|
95
95
|
### Media
|
|
96
96
|
|
|
97
97
|
```ts
|
|
98
|
-
// Upload a file, then attach the returned media id to a post via `mediaIds
|
|
98
|
+
// Upload a file of up to 4 GB, then attach the returned media id to a post via `mediaIds`.
|
|
99
99
|
const { data: media } = await publishq.media.upload({ file });
|
|
100
100
|
await publishq.media.list({ mediaTypes: ['IMAGE'] });
|
|
101
101
|
await publishq.media.get('<media-id>');
|
|
@@ -114,7 +114,7 @@ returns `403`.
|
|
|
114
114
|
| `workspaces:read` | `workspaces.list`, `workspaces.get` |
|
|
115
115
|
| `workspaces:write` | `workspaces.create` |
|
|
116
116
|
| `media:read` | `media.list`, `media.get` |
|
|
117
|
-
| `media:write` | `media.upload`
|
|
117
|
+
| `media:write` | `media.upload`, `media.createUpload`, `media.completeUpload` |
|
|
118
118
|
|
|
119
119
|
## Configuration
|
|
120
120
|
|
|
@@ -128,8 +128,8 @@ const publishq = new PublishQ({
|
|
|
128
128
|
|
|
129
129
|
## Related
|
|
130
130
|
|
|
131
|
-
- [`@publishq/cli`](https://www.npmjs.com/package/@publishq/cli)
|
|
132
|
-
- [`@publishq/mcp`](https://www.npmjs.com/package/@publishq/mcp)
|
|
131
|
+
- [`@publishq/cli`](https://www.npmjs.com/package/@publishq/cli): command-line interface built on this SDK.
|
|
132
|
+
- [`@publishq/mcp`](https://www.npmjs.com/package/@publishq/mcp): Model Context Protocol server for Claude Desktop and other AI agents.
|
|
133
133
|
- [API reference](https://publishq.com/docs)
|
|
134
134
|
|
|
135
135
|
## License
|
package/dist/index.cjs
CHANGED
|
@@ -34,14 +34,21 @@ __export(index_exports, {
|
|
|
34
34
|
Media2: () => Media,
|
|
35
35
|
Posts: () => Posts,
|
|
36
36
|
PublishQ: () => PublishQ,
|
|
37
|
+
STORAGE_UPLOAD_THRESHOLD_BYTES: () => STORAGE_UPLOAD_THRESHOLD_BYTES,
|
|
37
38
|
Workspaces: () => Workspaces,
|
|
38
39
|
default: () => index_default,
|
|
40
|
+
withInferredContentType: () => withInferredContentType,
|
|
39
41
|
zAccount: () => zAccount,
|
|
40
42
|
zAccountsResponse: () => zAccountsResponse,
|
|
41
43
|
zApiError: () => zApiError,
|
|
42
44
|
zApiErrorCode: () => zApiErrorCode,
|
|
43
45
|
zApiKeyScope: () => zApiKeyScope,
|
|
44
46
|
zBlueskyPlatformSpecificSettings: () => zBlueskyPlatformSpecificSettings,
|
|
47
|
+
zCompleteMediaUploadPath: () => zCompleteMediaUploadPath,
|
|
48
|
+
zCompleteMediaUploadResponse: () => zCompleteMediaUploadResponse,
|
|
49
|
+
zCreateMediaUploadBody: () => zCreateMediaUploadBody,
|
|
50
|
+
zCreateMediaUploadRequest: () => zCreateMediaUploadRequest,
|
|
51
|
+
zCreateMediaUploadResponse: () => zCreateMediaUploadResponse,
|
|
45
52
|
zCreatePostAccount: () => zCreatePostAccount,
|
|
46
53
|
zCreatePostBody: () => zCreatePostBody,
|
|
47
54
|
zCreatePostRequest: () => zCreatePostRequest,
|
|
@@ -74,13 +81,17 @@ __export(index_exports, {
|
|
|
74
81
|
zMediaListResponse: () => zMediaListResponse,
|
|
75
82
|
zMediaSource: () => zMediaSource,
|
|
76
83
|
zMediaType: () => zMediaType,
|
|
84
|
+
zMediaUpload: () => zMediaUpload,
|
|
77
85
|
zPagination: () => zPagination,
|
|
78
86
|
zPlatformSpecificSettings: () => zPlatformSpecificSettings,
|
|
79
87
|
zPost: () => zPost,
|
|
80
88
|
zPostAccount: () => zPostAccount,
|
|
89
|
+
zPostAnalytics: () => zPostAnalytics,
|
|
81
90
|
zPostOverrides: () => zPostOverrides,
|
|
91
|
+
zPostSource: () => zPostSource,
|
|
82
92
|
zPostStatus: () => zPostStatus,
|
|
83
93
|
zPostsResponse: () => zPostsResponse,
|
|
94
|
+
zPublishFailure: () => zPublishFailure,
|
|
84
95
|
zRemovePostAccountPath: () => zRemovePostAccountPath,
|
|
85
96
|
zRemovePostAccountResponse: () => zRemovePostAccountResponse,
|
|
86
97
|
zSocialPlatform: () => zSocialPlatform,
|
|
@@ -89,7 +100,9 @@ __export(index_exports, {
|
|
|
89
100
|
zTikTokCreatorInfoResponse: () => zTikTokCreatorInfoResponse,
|
|
90
101
|
zTikTokPlatformSpecificSettings: () => zTikTokPlatformSpecificSettings,
|
|
91
102
|
zTwitterPlatformSpecificSettings: () => zTwitterPlatformSpecificSettings,
|
|
103
|
+
zUpdatePostAccount: () => zUpdatePostAccount,
|
|
92
104
|
zUpdatePostBody: () => zUpdatePostBody,
|
|
105
|
+
zUpdatePostOverrides: () => zUpdatePostOverrides,
|
|
93
106
|
zUpdatePostPath: () => zUpdatePostPath,
|
|
94
107
|
zUpdatePostRequest: () => zUpdatePostRequest,
|
|
95
108
|
zUpdatePostResponse: () => zUpdatePostResponse,
|
|
@@ -920,7 +933,7 @@ var Posts = class {
|
|
|
920
933
|
/**
|
|
921
934
|
* List posts
|
|
922
935
|
*
|
|
923
|
-
* Returns a paginated list of posts.
|
|
936
|
+
* Returns a paginated list of the posts you created through PublishQ. Posts imported from a connected platform are excluded by default; pass includeImported=true to get both, or onlyImported=true for only the imported ones.
|
|
924
937
|
*/
|
|
925
938
|
static listPosts(options) {
|
|
926
939
|
return (options?.client ?? client).get({
|
|
@@ -1082,7 +1095,7 @@ var Media = class {
|
|
|
1082
1095
|
/**
|
|
1083
1096
|
* Upload media
|
|
1084
1097
|
*
|
|
1085
|
-
* Upload
|
|
1098
|
+
* Upload a file smaller than 100 MB in a single request. It is the simplest way to upload: one call returns a media ID you can attach to posts. For bigger files, use Create media upload.
|
|
1086
1099
|
*/
|
|
1087
1100
|
static uploadMedia(options) {
|
|
1088
1101
|
return (options.client ?? client).post({
|
|
@@ -1108,6 +1121,39 @@ var Media = class {
|
|
|
1108
1121
|
...options
|
|
1109
1122
|
});
|
|
1110
1123
|
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Create media upload
|
|
1126
|
+
*
|
|
1127
|
+
* Upload a file of up to 4 GB (500 MB on the Free plan) straight to storage:
|
|
1128
|
+
*
|
|
1129
|
+
* 1. Call this endpoint with `filename`, `contentType` and `fileSize`. You get back an `id` and an `uploadUrl`.
|
|
1130
|
+
* 2. `PUT` the file to `uploadUrl` with the same `Content-Type` header. Storage answers `200`, or `403` if the file is not exactly `fileSize` bytes.
|
|
1131
|
+
* 3. Optional: call Complete media upload to add the file to your media library now.
|
|
1132
|
+
* 4. Put the `id` in `mediaIds` when you create or update a post.
|
|
1133
|
+
*/
|
|
1134
|
+
static createMediaUpload(options) {
|
|
1135
|
+
return (options.client ?? client).post({
|
|
1136
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
1137
|
+
url: "/media/uploads",
|
|
1138
|
+
...options,
|
|
1139
|
+
headers: {
|
|
1140
|
+
"Content-Type": "application/json",
|
|
1141
|
+
...options.headers
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Complete media upload
|
|
1147
|
+
*
|
|
1148
|
+
* Optional. Adds a file uploaded through Create media upload to your media library now and returns it. Without this call the file is added the first time you use its id in a post, and an upload nobody completes or uses within 1 day expires.
|
|
1149
|
+
*/
|
|
1150
|
+
static completeMediaUpload(options) {
|
|
1151
|
+
return (options.client ?? client).post({
|
|
1152
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
1153
|
+
url: "/media/uploads/{id}/complete",
|
|
1154
|
+
...options
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1111
1157
|
};
|
|
1112
1158
|
|
|
1113
1159
|
// src/generated/zod.gen.ts
|
|
@@ -1127,6 +1173,7 @@ var zApiErrorCode = z.enum([
|
|
|
1127
1173
|
"post_not_editable",
|
|
1128
1174
|
"media_not_found",
|
|
1129
1175
|
"media_in_use",
|
|
1176
|
+
"media_upload_incomplete",
|
|
1130
1177
|
"storage_limit_exceeded",
|
|
1131
1178
|
"free_media_limit_exceeded",
|
|
1132
1179
|
"free_pending_media_limit_exceeded",
|
|
@@ -1137,6 +1184,9 @@ var zApiErrorCode = z.enum([
|
|
|
1137
1184
|
"invalid_file_extension",
|
|
1138
1185
|
"social_accounts_not_found",
|
|
1139
1186
|
"social_accounts_expired",
|
|
1187
|
+
"social_account_limit_exceeded",
|
|
1188
|
+
"platform_temporarily_unavailable",
|
|
1189
|
+
"platform_permission_missing",
|
|
1140
1190
|
"platform_validation_failed",
|
|
1141
1191
|
"posting_limits_exceeded",
|
|
1142
1192
|
"scheduled_at_required",
|
|
@@ -1146,10 +1196,12 @@ var zApiErrorCode = z.enum([
|
|
|
1146
1196
|
"workspace_not_found",
|
|
1147
1197
|
"workspace_forbidden",
|
|
1148
1198
|
"workspace_owner_required",
|
|
1199
|
+
"workspace_limit_exceeded",
|
|
1149
1200
|
"workspace_account_not_found",
|
|
1150
1201
|
"workspace_account_conflict",
|
|
1151
1202
|
"workspace_member_not_found",
|
|
1152
1203
|
"workspace_member_conflict",
|
|
1204
|
+
"team_member_limit_exceeded",
|
|
1153
1205
|
"workspace_invitation_invalid",
|
|
1154
1206
|
"workspace_invitation_expired"
|
|
1155
1207
|
]);
|
|
@@ -1264,6 +1316,9 @@ var zYouTubePlatformSpecificSettings = z.object({
|
|
|
1264
1316
|
})),
|
|
1265
1317
|
playlistId: z.optional(z.string().register(z.globalRegistry, {
|
|
1266
1318
|
description: "Playlist ID to add the video to after upload."
|
|
1319
|
+
})),
|
|
1320
|
+
thumbnailMediaId: z.optional(z.uuid().register(z.globalRegistry, {
|
|
1321
|
+
description: "ID of an image in your media library to set as the custom thumbnail of this video. JPEG or PNG, up to 50MB; YouTube recommends 1280x720 (16:9). Upload the image with the media endpoints first. The thumbnail is set right after the video uploads. If YouTube refuses it (custom thumbnails need a verified channel), the video still publishes and `thumbnail` on the account says why. To replace it on a draft or scheduled post, update the account with the new id; to remove it, send `thumbnailMediaId: null`."
|
|
1267
1322
|
}))
|
|
1268
1323
|
}).register(z.globalRegistry, {
|
|
1269
1324
|
description: "YouTube-specific upload settings, such as description and privacy."
|
|
@@ -1358,6 +1413,56 @@ var zTwitterPlatformSpecificSettings = z.object({
|
|
|
1358
1413
|
}).register(z.globalRegistry, {
|
|
1359
1414
|
description: "X (Twitter) specific post settings. All fields are optional."
|
|
1360
1415
|
});
|
|
1416
|
+
var zPostSource = z.enum(["PUBLISHQ", "IMPORTED"]).register(z.globalRegistry, {
|
|
1417
|
+
description: "Where the post came from. PUBLISHQ: created in PublishQ. IMPORTED: published directly on the platform and discovered during an analytics sync, so the post history stays complete."
|
|
1418
|
+
});
|
|
1419
|
+
var zPostAnalytics = z.object({
|
|
1420
|
+
views: z.int().check(z.gte(0)).register(z.globalRegistry, {
|
|
1421
|
+
description: "Views the platform reports for this post."
|
|
1422
|
+
}),
|
|
1423
|
+
likes: z.int().check(z.gte(0)).register(z.globalRegistry, {
|
|
1424
|
+
description: "Likes the platform reports for this post."
|
|
1425
|
+
}),
|
|
1426
|
+
comments: z.int().check(z.gte(0)).register(z.globalRegistry, {
|
|
1427
|
+
description: "Comments the platform reports for this post."
|
|
1428
|
+
}),
|
|
1429
|
+
shares: z.int().check(z.gte(0)).register(z.globalRegistry, {
|
|
1430
|
+
description: "Shares the platform reports for this post. 0 on platforms that do not disclose it (including YouTube)."
|
|
1431
|
+
}),
|
|
1432
|
+
extraMetrics: z.record(z.string(), z.int().check(z.gte(0))).register(z.globalRegistry, {
|
|
1433
|
+
description: "Metrics only this platform reports, keyed by metric name \u2014 Bluesky bookmarks, for example. Every metric the platform reports is always present, 0 included, so no key has to be guessed. Empty on platforms that expose nothing beyond the fields above, and a metric that already has its own field is never repeated here."
|
|
1434
|
+
}),
|
|
1435
|
+
syncedAt: z.iso.datetime().register(z.globalRegistry, {
|
|
1436
|
+
description: "When these numbers were last recorded. Metrics are refreshed in the background roughly once a day, and only stored when they change."
|
|
1437
|
+
})
|
|
1438
|
+
});
|
|
1439
|
+
var zPublishFailure = z.object({
|
|
1440
|
+
kind: z.enum([
|
|
1441
|
+
"RECONNECT",
|
|
1442
|
+
"MEDIA",
|
|
1443
|
+
"ACCOUNT_STATE",
|
|
1444
|
+
"TRANSIENT",
|
|
1445
|
+
"MAYBE_PUBLISHED",
|
|
1446
|
+
"UNKNOWN"
|
|
1447
|
+
]).register(z.globalRegistry, {
|
|
1448
|
+
description: "What kind of action this failure needs. `RECONNECT`: the account has to be reconnected. `MEDIA`: the platform refused the attached file. `ACCOUNT_STATE`: the platform is blocking this account, so the fix is on the platform. `TRANSIENT`: the platform was unavailable and the same request can succeed unchanged. `MAYBE_PUBLISHED`: the platform reported an error for a post that may have gone out anyway. `UNKNOWN`: unrecognised, so read `errorMessage`."
|
|
1449
|
+
}),
|
|
1450
|
+
title: z.string().register(z.globalRegistry, {
|
|
1451
|
+
description: "Short label for this failure."
|
|
1452
|
+
}),
|
|
1453
|
+
whatHappened: z.string().register(z.globalRegistry, {
|
|
1454
|
+
description: "One sentence about what the platform did."
|
|
1455
|
+
}),
|
|
1456
|
+
whatToDo: z.string().register(z.globalRegistry, {
|
|
1457
|
+
description: "One sentence naming the next action."
|
|
1458
|
+
}),
|
|
1459
|
+
canRetry: z.boolean().register(z.globalRegistry, {
|
|
1460
|
+
description: "Whether retrying can succeed. False when the platform is blocking the account, since the same request will be refused the same way."
|
|
1461
|
+
}),
|
|
1462
|
+
checkPlatformFirst: z.boolean().register(z.globalRegistry, {
|
|
1463
|
+
description: "Whether the post may already be live. A publish is not idempotent and no platform accepts a deduplication key, so when this is true a retry can create a second copy: check the profile before retrying."
|
|
1464
|
+
})
|
|
1465
|
+
});
|
|
1361
1466
|
var zSocialPlatform = z.enum([
|
|
1362
1467
|
"TWITTER",
|
|
1363
1468
|
"INSTAGRAM",
|
|
@@ -1382,8 +1487,8 @@ var zPostOverrides = z.object({
|
|
|
1382
1487
|
thumbnailTimestamp: z.optional(z.int().check(z.gte(0)).register(z.globalRegistry, {
|
|
1383
1488
|
description: "Video thumbnail timestamp in milliseconds. Applies to Instagram and TikTok."
|
|
1384
1489
|
})),
|
|
1385
|
-
mediaIds: z.optional(z.array(z.uuid()).register(z.globalRegistry, {
|
|
1386
|
-
description: "Replacement media for this account. Completely replaces the post-level media."
|
|
1490
|
+
mediaIds: z.optional(z.array(z.uuid()).check(z.maxLength(35)).register(z.globalRegistry, {
|
|
1491
|
+
description: "Replacement media for this account. Completely replaces the post-level media. Up to 35."
|
|
1387
1492
|
}))
|
|
1388
1493
|
});
|
|
1389
1494
|
var zPlatformSpecificSettings = z.union([
|
|
@@ -1431,6 +1536,9 @@ var zPlatformSpecificSettings = z.union([
|
|
|
1431
1536
|
})),
|
|
1432
1537
|
playlistId: z.optional(z.string().register(z.globalRegistry, {
|
|
1433
1538
|
description: "Playlist ID to add the video to after upload."
|
|
1539
|
+
})),
|
|
1540
|
+
thumbnailMediaId: z.optional(z.uuid().register(z.globalRegistry, {
|
|
1541
|
+
description: "ID of an image in your media library to set as the custom thumbnail of this video. JPEG or PNG, up to 50MB; YouTube recommends 1280x720 (16:9). Upload the image with the media endpoints first. The thumbnail is set right after the video uploads. If YouTube refuses it (custom thumbnails need a verified channel), the video still publishes and `thumbnail` on the account says why. To replace it on a draft or scheduled post, update the account with the new id; to remove it, send `thumbnailMediaId: null`."
|
|
1434
1542
|
}))
|
|
1435
1543
|
}).register(z.globalRegistry, {
|
|
1436
1544
|
description: "YouTube-specific upload settings, such as description and privacy."
|
|
@@ -1575,6 +1683,18 @@ var zPostAccount = z.object({
|
|
|
1575
1683
|
errorMessage: z.optional(z.string().register(z.globalRegistry, {
|
|
1576
1684
|
description: "Error message from the platform if publishing failed for this account."
|
|
1577
1685
|
})),
|
|
1686
|
+
thumbnail: z.optional(z.object({
|
|
1687
|
+
status: z.enum(["APPLIED", "FAILED"]),
|
|
1688
|
+
errorMessage: z.optional(z.string())
|
|
1689
|
+
}).register(z.globalRegistry, {
|
|
1690
|
+
description: "Present on a published account whose post set a custom thumbnail: whether the platform applied it, and if not, why. The post itself is live either way."
|
|
1691
|
+
})),
|
|
1692
|
+
failure: z.optional(z.intersection(zPublishFailure, z.unknown().register(z.globalRegistry, {
|
|
1693
|
+
description: "The same failure read as an action: what happened and the one thing to do about it. Present whenever status is FAILED, including when the platform gave no reason. The wording is what the app and the failure email show, so a client does not have to interpret `errorMessage` itself."
|
|
1694
|
+
}))),
|
|
1695
|
+
analytics: z.optional(z.intersection(zPostAnalytics, z.unknown().register(z.globalRegistry, {
|
|
1696
|
+
description: "Latest metrics recorded for this delivery. Present only for published posts on platforms with analytics support, once a background sync has run."
|
|
1697
|
+
}))),
|
|
1578
1698
|
postOverrides: z.optional(zPostOverrides),
|
|
1579
1699
|
platformSpecificSettings: z.optional(zPlatformSpecificSettings)
|
|
1580
1700
|
});
|
|
@@ -1586,6 +1706,7 @@ var zPost = z.object({
|
|
|
1586
1706
|
description: "The shared text content of the post."
|
|
1587
1707
|
}),
|
|
1588
1708
|
status: zPostStatus,
|
|
1709
|
+
source: zPostSource,
|
|
1589
1710
|
errorMessage: z.optional(z.string().register(z.globalRegistry, {
|
|
1590
1711
|
description: "Human-readable error summary when one or more accounts failed."
|
|
1591
1712
|
})),
|
|
@@ -1627,8 +1748,8 @@ var zCreatePostRequest = z.object({
|
|
|
1627
1748
|
content: z._default(z.optional(z.string().register(z.globalRegistry, {
|
|
1628
1749
|
description: "The text content of the post, shared across all accounts unless overridden. Optional when media is attached (platform-dependent)."
|
|
1629
1750
|
})), ""),
|
|
1630
|
-
mediaIds: z.optional(z.array(z.uuid()).register(z.globalRegistry, {
|
|
1631
|
-
description: "Media to attach to the post. Shared across all accounts unless an account provides its own mediaIds in postOverrides."
|
|
1751
|
+
mediaIds: z.optional(z.array(z.uuid()).check(z.maxLength(35)).register(z.globalRegistry, {
|
|
1752
|
+
description: "Media to attach to the post, up to 35. Shared across all accounts unless an account provides its own mediaIds in postOverrides. Accepts IDs from Upload media and from Create media upload."
|
|
1632
1753
|
})),
|
|
1633
1754
|
thumbnailTimestamp: z.optional(z.int().check(z.gte(0)).register(z.globalRegistry, {
|
|
1634
1755
|
description: "Default video thumbnail timestamp in milliseconds for Instagram and TikTok. Can be overridden per account."
|
|
@@ -1637,7 +1758,7 @@ var zCreatePostRequest = z.object({
|
|
|
1637
1758
|
description: "Accounts to post to. Each entry can optionally include content overrides and platform-specific settings."
|
|
1638
1759
|
}),
|
|
1639
1760
|
scheduledAt: z.optional(z.iso.datetime().register(z.globalRegistry, {
|
|
1640
|
-
description: "Schedule the post for a future date. If omitted and publishNow is false or absent, the post is saved as a draft."
|
|
1761
|
+
description: "Schedule the post for a future date. If omitted and publishNow is false or absent, the post is saved as a draft. Accepts UTC (2026-04-01T12:00:00Z) or an explicit UTC offset (2026-04-01T14:00:00+02:00); a time without zone information is rejected."
|
|
1641
1762
|
})),
|
|
1642
1763
|
publishNow: z.optional(z.boolean().register(z.globalRegistry, {
|
|
1643
1764
|
description: "Publish immediately. Cannot be combined with scheduledAt."
|
|
@@ -1645,27 +1766,43 @@ var zCreatePostRequest = z.object({
|
|
|
1645
1766
|
}).register(z.globalRegistry, {
|
|
1646
1767
|
description: "Create a post across one or more connected social accounts."
|
|
1647
1768
|
});
|
|
1769
|
+
var zUpdatePostOverrides = z.object({
|
|
1770
|
+
content: z.nullish(z.string()),
|
|
1771
|
+
mediaIds: z.nullish(z.array(z.uuid()).check(z.maxLength(35))),
|
|
1772
|
+
thumbnailTimestamp: z.nullish(z.int().check(z.gte(0)))
|
|
1773
|
+
}).register(z.globalRegistry, {
|
|
1774
|
+
description: "Override the post-level content, media or thumbnail timestamp for this specific account."
|
|
1775
|
+
});
|
|
1776
|
+
var zUpdatePostAccount = z.object({
|
|
1777
|
+
accountId: z.uuid().register(z.globalRegistry, {
|
|
1778
|
+
description: "ID of a connected social account to post to."
|
|
1779
|
+
}),
|
|
1780
|
+
postOverrides: z.optional(zUpdatePostOverrides),
|
|
1781
|
+
platformSpecificSettings: z.optional(z.intersection(zPlatformSpecificSettings, z.unknown().register(z.globalRegistry, {
|
|
1782
|
+
description: "Platform-specific settings to change. Only the settings you send change, and every setting you leave out keeps its current value. Send a setting as null to remove it. Nested objects merge the same way (JSON Merge Patch, RFC 7396). The resulting settings must still be valid for the platform, so a required setting cannot be removed."
|
|
1783
|
+
})))
|
|
1784
|
+
});
|
|
1648
1785
|
var zUpdatePostRequest = z.object({
|
|
1649
1786
|
content: z.optional(z.string().register(z.globalRegistry, {
|
|
1650
1787
|
description: "New shared text content for the post."
|
|
1651
1788
|
})),
|
|
1652
1789
|
scheduledAt: z.optional(z.iso.datetime().register(z.globalRegistry, {
|
|
1653
|
-
description: "New scheduled date for the post. Must be at least 1 minute in the future."
|
|
1790
|
+
description: "New scheduled date for the post. Must be at least 1 minute in the future. Accepts UTC (2026-04-15T14:00:00Z) or an explicit UTC offset (2026-04-15T16:00:00+02:00); a time without zone information is rejected."
|
|
1654
1791
|
})),
|
|
1655
|
-
mediaIds: z.optional(z.array(z.uuid()).register(z.globalRegistry, {
|
|
1656
|
-
description: "Full replacement for the post media. If omitted, the existing media stays unchanged. If provided, it replaces the current media across all accounts in the post. Send an empty array to remove all media from the post."
|
|
1792
|
+
mediaIds: z.optional(z.array(z.uuid()).check(z.maxLength(35)).register(z.globalRegistry, {
|
|
1793
|
+
description: "Full replacement for the post media, up to 35. If omitted, the existing media stays unchanged. If provided, it replaces the current media across all accounts in the post. Send an empty array to remove all media from the post."
|
|
1657
1794
|
})),
|
|
1658
1795
|
thumbnailTimestamp: z.optional(z.int().check(z.gte(0)).register(z.globalRegistry, {
|
|
1659
1796
|
description: "New video thumbnail timestamp in milliseconds for Instagram and TikTok media. Does not change which media files are attached. Combine with mediaIds if you want to replace media and set the new thumbnail timestamp in the same request."
|
|
1660
1797
|
})),
|
|
1661
|
-
accounts: z.optional(z.array(
|
|
1662
|
-
description: "Accounts
|
|
1798
|
+
accounts: z.optional(z.array(zUpdatePostAccount).check(z.minLength(1)).register(z.globalRegistry, {
|
|
1799
|
+
description: "Accounts to change, plus any connected accounts to add to the post. For an account already in the post, only the fields you send change: omitted fields keep their current value and a field sent as null is removed. Existing accounts left out of this array stay unchanged; remove an account through the account-specific delete endpoint."
|
|
1663
1800
|
})),
|
|
1664
1801
|
publishNow: z.optional(z.boolean().register(z.globalRegistry, {
|
|
1665
1802
|
description: "Publish an existing draft immediately. Cannot be combined with scheduledAt."
|
|
1666
1803
|
}))
|
|
1667
1804
|
}).register(z.globalRegistry, {
|
|
1668
|
-
description: "Update a draft or scheduled post. Published and publishing posts cannot be updated. Media behavior is explicit: omit mediaIds to keep existing media unchanged, provide mediaIds to replace all current media, or provide an empty mediaIds array to remove all media."
|
|
1805
|
+
description: "Update a draft or scheduled post. Published and publishing posts cannot be updated. Only the fields you send change; everything you leave out keeps its current value. To remove an account setting, or an account's own text, media or thumbnail timestamp, send it as null. Media behavior is explicit: omit mediaIds to keep existing media unchanged, provide mediaIds to replace all current media, or provide an empty mediaIds array to remove all media. The post that results from the update is validated as a whole before anything is saved."
|
|
1669
1806
|
});
|
|
1670
1807
|
var zSuccessMessage = z.object({
|
|
1671
1808
|
deleted: z.literal(true),
|
|
@@ -1762,6 +1899,28 @@ var zMediaListResponse = z.object({
|
|
|
1762
1899
|
var zUploadMediaRequest = z.object({
|
|
1763
1900
|
file: z.string()
|
|
1764
1901
|
});
|
|
1902
|
+
var zCreateMediaUploadRequest = z.object({
|
|
1903
|
+
filename: z.string().check(z.minLength(1), z.maxLength(1024)).register(z.globalRegistry, {
|
|
1904
|
+
description: "Name of the file you are uploading."
|
|
1905
|
+
}),
|
|
1906
|
+
contentType: z.string().check(z.minLength(1)).register(z.globalRegistry, {
|
|
1907
|
+
description: "Content type of the file. One of: image/jpeg, image/png, image/gif, image/webp, video/mp4, video/webm, video/quicktime, video/x-msvideo."
|
|
1908
|
+
}),
|
|
1909
|
+
fileSize: z.int().check(z.gt(0)).register(z.globalRegistry, {
|
|
1910
|
+
description: "Exact size of the file in bytes. Your plan's file size limit and your storage are checked before you upload, and storage refuses a file of any other size. Each platform's own size limits are checked when you attach the media to a post."
|
|
1911
|
+
})
|
|
1912
|
+
});
|
|
1913
|
+
var zMediaUpload = z.object({
|
|
1914
|
+
id: z.uuid().register(z.globalRegistry, {
|
|
1915
|
+
description: "Media ID of the file. Attach it to posts through mediaIds once the file is uploaded."
|
|
1916
|
+
}),
|
|
1917
|
+
uploadUrl: z.url().register(z.globalRegistry, {
|
|
1918
|
+
description: "Signed storage URL. PUT the file to it with the same Content-Type. It goes to storage, not to the PublishQ API, so send no Authorization header."
|
|
1919
|
+
}),
|
|
1920
|
+
expiresAt: z.iso.datetime().register(z.globalRegistry, {
|
|
1921
|
+
description: "When uploadUrl stops accepting the file, 1 hour after it is created."
|
|
1922
|
+
})
|
|
1923
|
+
});
|
|
1765
1924
|
var zTikTokCreatorInfoResponse = z.object({
|
|
1766
1925
|
privacyLevelOptions: z.array(z.string()).register(z.globalRegistry, {
|
|
1767
1926
|
description: "Privacy levels available for this account. Use one of these values as `privacyLevel` in `platformSpecificSettings`."
|
|
@@ -1794,7 +1953,13 @@ var zListPostsQuery = z.object({
|
|
|
1794
1953
|
})), 1),
|
|
1795
1954
|
limit: z._default(z.optional(z.int().check(z.gt(0), z.lte(100)).register(z.globalRegistry, {
|
|
1796
1955
|
description: "Page size. Max 100."
|
|
1797
|
-
})), 20)
|
|
1956
|
+
})), 20),
|
|
1957
|
+
includeImported: z._default(z.optional(z.boolean().register(z.globalRegistry, {
|
|
1958
|
+
description: "Also return posts imported from a connected platform (posts with source IMPORTED). Defaults to false: only posts created through PublishQ are returned."
|
|
1959
|
+
})), false),
|
|
1960
|
+
onlyImported: z._default(z.optional(z.boolean().register(z.globalRegistry, {
|
|
1961
|
+
description: "Return ONLY posts imported from a connected platform. Cannot be combined with includeImported."
|
|
1962
|
+
})), false)
|
|
1798
1963
|
});
|
|
1799
1964
|
var zListPostsResponse = zPostsResponse;
|
|
1800
1965
|
var zCreatePostBody = zCreatePostRequest;
|
|
@@ -1865,6 +2030,12 @@ var zGetMediaPath = z.object({
|
|
|
1865
2030
|
id: z.uuid()
|
|
1866
2031
|
});
|
|
1867
2032
|
var zGetMediaResponse = zMedia;
|
|
2033
|
+
var zCreateMediaUploadBody = zCreateMediaUploadRequest;
|
|
2034
|
+
var zCreateMediaUploadResponse = zMediaUpload;
|
|
2035
|
+
var zCompleteMediaUploadPath = z.object({
|
|
2036
|
+
id: z.uuid()
|
|
2037
|
+
});
|
|
2038
|
+
var zCompleteMediaUploadResponse = zMedia;
|
|
1868
2039
|
|
|
1869
2040
|
// src/index.ts
|
|
1870
2041
|
var rawErrorBodies = /* @__PURE__ */ new WeakMap();
|
|
@@ -1966,12 +2137,92 @@ function createWorkspacesNamespace(client2) {
|
|
|
1966
2137
|
get: (id) => preserveApiError(Workspaces.getWorkspace({ client: client2, path: { id } }))
|
|
1967
2138
|
};
|
|
1968
2139
|
}
|
|
2140
|
+
var UPLOAD_MIME_TYPE_BY_EXTENSION = {
|
|
2141
|
+
jpg: "image/jpeg",
|
|
2142
|
+
jpeg: "image/jpeg",
|
|
2143
|
+
png: "image/png",
|
|
2144
|
+
gif: "image/gif",
|
|
2145
|
+
webp: "image/webp",
|
|
2146
|
+
mp4: "video/mp4",
|
|
2147
|
+
webm: "video/webm",
|
|
2148
|
+
mov: "video/quicktime",
|
|
2149
|
+
avi: "video/x-msvideo"
|
|
2150
|
+
};
|
|
2151
|
+
function isUninformativeType(type) {
|
|
2152
|
+
return type === "" || type === "application/octet-stream";
|
|
2153
|
+
}
|
|
2154
|
+
function withInferredContentType(file) {
|
|
2155
|
+
const name = "name" in file ? file.name : void 0;
|
|
2156
|
+
if (!name || !isUninformativeType(file.type)) return file;
|
|
2157
|
+
const extension = name.split(".").pop()?.toLowerCase();
|
|
2158
|
+
const type = extension ? UPLOAD_MIME_TYPE_BY_EXTENSION[extension] : void 0;
|
|
2159
|
+
if (!type) return file;
|
|
2160
|
+
return new File([file], name, { type });
|
|
2161
|
+
}
|
|
2162
|
+
var STORAGE_UPLOAD_THRESHOLD_BYTES = 90 * 1024 * 1024;
|
|
2163
|
+
async function uploadThroughStorage(client2, file) {
|
|
2164
|
+
const created = await preserveApiError(
|
|
2165
|
+
Media.createMediaUpload({
|
|
2166
|
+
client: client2,
|
|
2167
|
+
body: {
|
|
2168
|
+
filename: "name" in file && file.name ? file.name : "upload",
|
|
2169
|
+
contentType: file.type,
|
|
2170
|
+
fileSize: file.size
|
|
2171
|
+
}
|
|
2172
|
+
})
|
|
2173
|
+
);
|
|
2174
|
+
if (!created.data) return created;
|
|
2175
|
+
const response = await fetch(created.data.uploadUrl, {
|
|
2176
|
+
method: "PUT",
|
|
2177
|
+
headers: { "Content-Type": file.type },
|
|
2178
|
+
body: file
|
|
2179
|
+
});
|
|
2180
|
+
if (!response.ok) {
|
|
2181
|
+
return {
|
|
2182
|
+
data: void 0,
|
|
2183
|
+
error: {
|
|
2184
|
+
error: {
|
|
2185
|
+
status: response.status,
|
|
2186
|
+
message: "Storage refused the file upload",
|
|
2187
|
+
mediaId: created.data.id,
|
|
2188
|
+
storageResponse: await response.text()
|
|
2189
|
+
}
|
|
2190
|
+
},
|
|
2191
|
+
request: new Request(created.data.uploadUrl, { method: "PUT" }),
|
|
2192
|
+
response
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
2195
|
+
return preserveApiError(
|
|
2196
|
+
Media.completeMediaUpload({ client: client2, path: { id: created.data.id } })
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
1969
2199
|
function createMediaNamespace(client2) {
|
|
1970
2200
|
return {
|
|
1971
2201
|
/** List your uploaded and generated media files. Filter by id, type, source. Wraps `Media.listMedia`. */
|
|
1972
2202
|
list: (query) => preserveApiError(Media.listMedia({ client: client2, query })),
|
|
1973
|
-
/**
|
|
1974
|
-
|
|
2203
|
+
/**
|
|
2204
|
+
* Upload one media file and get back a media ID to attach to posts. Wraps
|
|
2205
|
+
* `Media.uploadMedia`, or for files over `STORAGE_UPLOAD_THRESHOLD_BYTES`
|
|
2206
|
+
* runs `createUpload`, sends the file to storage and returns the finished
|
|
2207
|
+
* media from `completeUpload`. Both return the same `Media` object.
|
|
2208
|
+
*
|
|
2209
|
+
* The file's content type is filled in from its name when it has none, so a
|
|
2210
|
+
* file read off disk with `new File([buffer], 'photo.jpg')` is not sent as
|
|
2211
|
+
* `application/octet-stream` and refused. See `withInferredContentType`.
|
|
2212
|
+
*/
|
|
2213
|
+
upload: (body) => {
|
|
2214
|
+
const file = withInferredContentType(body.file);
|
|
2215
|
+
if (file.size > STORAGE_UPLOAD_THRESHOLD_BYTES) {
|
|
2216
|
+
return uploadThroughStorage(client2, file);
|
|
2217
|
+
}
|
|
2218
|
+
return preserveApiError(
|
|
2219
|
+
Media.uploadMedia({ client: client2, body: { ...body, file } })
|
|
2220
|
+
);
|
|
2221
|
+
},
|
|
2222
|
+
/** Get a storage upload URL for a file of up to 4 GB. Send the file there, then use the returned media ID in posts. Wraps `Media.createMediaUpload`. */
|
|
2223
|
+
createUpload: (body) => preserveApiError(Media.createMediaUpload({ client: client2, body })),
|
|
2224
|
+
/** Add a file sent to a `createUpload` URL to the media library now and return it. Optional: using the ID in a post does the same. Wraps `Media.completeMediaUpload`. */
|
|
2225
|
+
completeUpload: (id) => preserveApiError(Media.completeMediaUpload({ client: client2, path: { id } })),
|
|
1975
2226
|
/** Get one media file by ID. Wraps `Media.getMedia`. */
|
|
1976
2227
|
get: (id) => preserveApiError(Media.getMedia({ client: client2, path: { id } }))
|
|
1977
2228
|
};
|
|
@@ -1983,13 +2234,20 @@ var index_default = PublishQ;
|
|
|
1983
2234
|
Media2,
|
|
1984
2235
|
Posts,
|
|
1985
2236
|
PublishQ,
|
|
2237
|
+
STORAGE_UPLOAD_THRESHOLD_BYTES,
|
|
1986
2238
|
Workspaces,
|
|
2239
|
+
withInferredContentType,
|
|
1987
2240
|
zAccount,
|
|
1988
2241
|
zAccountsResponse,
|
|
1989
2242
|
zApiError,
|
|
1990
2243
|
zApiErrorCode,
|
|
1991
2244
|
zApiKeyScope,
|
|
1992
2245
|
zBlueskyPlatformSpecificSettings,
|
|
2246
|
+
zCompleteMediaUploadPath,
|
|
2247
|
+
zCompleteMediaUploadResponse,
|
|
2248
|
+
zCreateMediaUploadBody,
|
|
2249
|
+
zCreateMediaUploadRequest,
|
|
2250
|
+
zCreateMediaUploadResponse,
|
|
1993
2251
|
zCreatePostAccount,
|
|
1994
2252
|
zCreatePostBody,
|
|
1995
2253
|
zCreatePostRequest,
|
|
@@ -2022,13 +2280,17 @@ var index_default = PublishQ;
|
|
|
2022
2280
|
zMediaListResponse,
|
|
2023
2281
|
zMediaSource,
|
|
2024
2282
|
zMediaType,
|
|
2283
|
+
zMediaUpload,
|
|
2025
2284
|
zPagination,
|
|
2026
2285
|
zPlatformSpecificSettings,
|
|
2027
2286
|
zPost,
|
|
2028
2287
|
zPostAccount,
|
|
2288
|
+
zPostAnalytics,
|
|
2029
2289
|
zPostOverrides,
|
|
2290
|
+
zPostSource,
|
|
2030
2291
|
zPostStatus,
|
|
2031
2292
|
zPostsResponse,
|
|
2293
|
+
zPublishFailure,
|
|
2032
2294
|
zRemovePostAccountPath,
|
|
2033
2295
|
zRemovePostAccountResponse,
|
|
2034
2296
|
zSocialPlatform,
|
|
@@ -2037,7 +2299,9 @@ var index_default = PublishQ;
|
|
|
2037
2299
|
zTikTokCreatorInfoResponse,
|
|
2038
2300
|
zTikTokPlatformSpecificSettings,
|
|
2039
2301
|
zTwitterPlatformSpecificSettings,
|
|
2302
|
+
zUpdatePostAccount,
|
|
2040
2303
|
zUpdatePostBody,
|
|
2304
|
+
zUpdatePostOverrides,
|
|
2041
2305
|
zUpdatePostPath,
|
|
2042
2306
|
zUpdatePostRequest,
|
|
2043
2307
|
zUpdatePostResponse,
|