tiktok-business-messaging 0.0.1

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 ADDED
@@ -0,0 +1,41 @@
1
+ # TikTok Business Messaging SDK
2
+
3
+ Lightweight **Node.js/TypeScript** wrapper for the TikTok Business Messaging API.
4
+
5
+ > Unofficial SDK. Use at your own risk.
6
+
7
+ ## Features
8
+
9
+ - Simple API client for messaging endpoints
10
+ - Built-in auth header handling
11
+ - Typed request/response models (TypeScript)
12
+ - Webhook signature verification helper
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install tiktok-business
18
+ # or
19
+ pnpm add tiktok-business
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```ts
25
+ import { TikTokBusiness } from "tiktok-business-messaging-sdk";
26
+
27
+ const client = new TikTokBusiness({
28
+ appId: process.env.TT_APP_ID!,
29
+ appSecret: process.env.TT_APP_SECRET!,
30
+ accessToken: process.env.TT_ACCESS_TOKEN!,
31
+ });
32
+
33
+ await client.messages.sendText({
34
+ conversationId: "conv_123",
35
+ text: "Hello from TikTok SDK 👋",
36
+ });
37
+ ```
38
+
39
+ ## License
40
+
41
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,335 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ TikTokApiError: () => TikTokApiError,
24
+ TikTokBusiness: () => TikTokBusiness
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/constants.ts
29
+ var TIKTOK_API_BASE_URL = "https://business-api.tiktok.com/open_api/v1.3";
30
+ var TIKTOK_AUTH_BASE_URL = `${TIKTOK_API_BASE_URL}/tt_user/oauth2`;
31
+ var TIKTOK_ERROR_MESSAGES = {
32
+ [40001 /* NO_PERMISSION */]: "No permission to perform the related operation. Ensure you have the necessary scopes.",
33
+ [40002 /* PARAMETER_ERROR */]: "Parameter error. Check missing fields or incorrect formats in your request.",
34
+ [40007 /* OBJECT_NOT_FOUND */]: "The operation or object does not exist. Check your IDs or URLs.",
35
+ [40100 /* RATE_LIMIT_EXCEEDED */]: "Requests made too frequently. You are being throttled.",
36
+ [40105 /* INVALID_ACCESS_TOKEN */]: "Invalid or incorrect access token. Please refresh or re-authorize.",
37
+ [40908 /* UNSUPPORTED_FILE_TYPE */]: "The file type is unsupported. Refer to documentation for allowed formats.",
38
+ [40064 /* MESSAGE_BLOCKED */]: "The message is blocked due to TikTok direct message rules.",
39
+ [51065 /* SYSTEM_ERROR */]: "TikTok internal system error. Please retry the request.",
40
+ [40131 /* CODE_EXPIRED */]: "The authorization code has expired. Please re-authorize to get a new code."
41
+ };
42
+
43
+ // src/error.ts
44
+ var TikTokApiError = class _TikTokApiError extends Error {
45
+ constructor(code, requestId) {
46
+ const friendlyMessage = TIKTOK_ERROR_MESSAGES[code] || "An unknown TikTok API error occurred.";
47
+ super(`TikTok API Error [${code}]: ${friendlyMessage}`);
48
+ this.code = code;
49
+ this.requestId = requestId;
50
+ this.name = "TikTokApiError";
51
+ this.message = friendlyMessage;
52
+ Object.setPrototypeOf(this, _TikTokApiError.prototype);
53
+ }
54
+ message;
55
+ };
56
+
57
+ // src/http.ts
58
+ var HttpClient = class {
59
+ constructor(config) {
60
+ this.config = config;
61
+ this.baseUrl = config.baseUrl ?? "https://business-api.tiktok.com/open_api/v1.3";
62
+ }
63
+ baseUrl;
64
+ get(path, params, opts) {
65
+ return this.request({ method: "GET", path, params, opts });
66
+ }
67
+ post(path, body, opts) {
68
+ return this.request({ method: "POST", path, body, opts });
69
+ }
70
+ put(path, body, opts) {
71
+ return this.request({ method: "PUT", path, body, opts });
72
+ }
73
+ patch(path, body, opts) {
74
+ return this.request({ method: "PATCH", path, body, opts });
75
+ }
76
+ delete(path, params, opts) {
77
+ return this.request({ method: "DELETE", path, params, opts });
78
+ }
79
+ upload(path, form, opts) {
80
+ return this.request({ method: "POST", path, form, opts });
81
+ }
82
+ async request({
83
+ method,
84
+ path,
85
+ params,
86
+ body,
87
+ form,
88
+ opts
89
+ }) {
90
+ const res = await fetch(
91
+ this.buildUrl(path, params),
92
+ this.buildInit(method, body, opts, form)
93
+ );
94
+ return this.parseResponse(res);
95
+ }
96
+ buildUrl(path, params) {
97
+ const url = new URL(`${this.baseUrl}${path}`);
98
+ if (params) {
99
+ Object.entries(params).forEach(([k, v]) => {
100
+ if (v !== void 0 && v !== null && v !== "") {
101
+ url.searchParams.set(k, String(v));
102
+ }
103
+ });
104
+ }
105
+ return url.toString();
106
+ }
107
+ buildInit(method, body, opts, form) {
108
+ const headers = {
109
+ "Access-Token": opts.accessToken
110
+ };
111
+ if (!form) {
112
+ headers["Content-Type"] = "application/json";
113
+ }
114
+ return {
115
+ method,
116
+ signal: AbortSignal.timeout(
117
+ opts.timeout ?? this.config.timeout ?? 1e4
118
+ ),
119
+ headers,
120
+ ...form ? { body: form } : body !== void 0 ? { body: JSON.stringify(body) } : {}
121
+ };
122
+ }
123
+ async parseResponse(res) {
124
+ console.debug(`[HttpClient] Parsing response for URL: ${res.url}`);
125
+ console.debug(
126
+ `[HttpClient] Response status: ${res.status} ${res.statusText}`
127
+ );
128
+ console.debug(`[HttpClient] Response data:`, await res.clone().text());
129
+ const json = await res.json();
130
+ if (json.code !== 0) {
131
+ throw new TikTokApiError(json.code, json.request_id);
132
+ }
133
+ return json.data;
134
+ }
135
+ };
136
+
137
+ // src/services/auth.ts
138
+ var Auth = class {
139
+ constructor(config) {
140
+ this.config = config;
141
+ }
142
+ getTokens(authCode) {
143
+ return this.post("/token/", {
144
+ client_id: this.config.clientKey,
145
+ client_secret: this.config.clientSecret,
146
+ grant_type: "authorization_code",
147
+ auth_code: decodeURIComponent(authCode),
148
+ redirect_uri: this.config.redirectUri
149
+ });
150
+ }
151
+ refreshTokens(refreshToken) {
152
+ return this.post("/refresh_token/", {
153
+ client_id: this.config.clientKey,
154
+ client_secret: this.config.clientSecret,
155
+ grant_type: "refresh_token",
156
+ refresh_token: refreshToken
157
+ });
158
+ }
159
+ revokeToken(accessToken) {
160
+ return this.post("/token/revoke/", {
161
+ client_id: this.config.clientKey,
162
+ client_secret: this.config.clientSecret,
163
+ token: accessToken
164
+ });
165
+ }
166
+ // for tt_users
167
+ getAuthorizationUrl(state) {
168
+ const params = new URLSearchParams({
169
+ client_key: this.config.clientKey,
170
+ response_type: "code",
171
+ scope: (this.config.scopes ?? []).join(","),
172
+ redirect_uri: this.config.redirectUri ?? "",
173
+ ...state && { state }
174
+ });
175
+ return `https://www.tiktok.com/v2/auth/authorize/?${params.toString()}`;
176
+ }
177
+ async post(path, body) {
178
+ const response = await fetch(`${TIKTOK_AUTH_BASE_URL}${path}`, {
179
+ method: "POST",
180
+ headers: { "Content-Type": "application/json" },
181
+ body: JSON.stringify(body)
182
+ });
183
+ if (!response.ok) {
184
+ throw new TikTokApiError(response.status, response.statusText);
185
+ }
186
+ const json = await response.json();
187
+ if (json.code !== 0) {
188
+ throw new TikTokApiError(json.code, json.request_id);
189
+ }
190
+ return json.data;
191
+ }
192
+ };
193
+
194
+ // src/services/media.ts
195
+ var MediaService = class {
196
+ constructor(http) {
197
+ this.http = http;
198
+ }
199
+ upload(params, opts) {
200
+ const form = new FormData();
201
+ form.append("business_id", params.business_id);
202
+ form.append("file", params.file);
203
+ form.append("media_type", params.media_type);
204
+ return this.http.upload("/business/message/media/upload/", form, opts);
205
+ }
206
+ };
207
+
208
+ // src/services/messaging.ts
209
+ var Messaging = class {
210
+ constructor(http) {
211
+ this.http = http;
212
+ }
213
+ /**
214
+ * List all conversations for a business account
215
+ */
216
+ listConversations(params, opts) {
217
+ console.debug("[Messaging] listConversations called", { params });
218
+ return this.http.get("/business/message/conversation/list/", params, opts).then((response) => {
219
+ console.debug("[Messaging] listConversations succeeded");
220
+ return response;
221
+ }).catch((error) => {
222
+ console.error("[Messaging] listConversations failed", error);
223
+ throw error;
224
+ });
225
+ }
226
+ /**
227
+ * Get messages in a specific conversation
228
+ */
229
+ listMessages(params, opts) {
230
+ console.log("Conversation ID:", encodeURIComponent(params.conversation_id));
231
+ return this.http.get(
232
+ "/business/message/content/list/",
233
+ {
234
+ ...params
235
+ },
236
+ opts
237
+ );
238
+ }
239
+ /**
240
+ * Send a text message to a conversation
241
+ */
242
+ sendTextMessage(body, opts) {
243
+ const { ...restBody } = body;
244
+ return this.http.post(
245
+ "/business/message/send/",
246
+ { recipient_type: "CONVERSATION", ...restBody },
247
+ opts
248
+ );
249
+ }
250
+ /**
251
+ * Send an image message to a conversation
252
+ */
253
+ sendImageMessage(body, opts) {
254
+ return this.http.post(
255
+ "/business/message/send/",
256
+ {
257
+ ...body
258
+ },
259
+ opts
260
+ );
261
+ }
262
+ };
263
+
264
+ // src/services/webhook.ts
265
+ var WebhookService = class {
266
+ constructor(config) {
267
+ this.config = config;
268
+ }
269
+ create(params) {
270
+ return this.post("/business/webhook/update/", {
271
+ event_type: params.event_type,
272
+ callback_url: params.callback_url
273
+ });
274
+ }
275
+ get(params) {
276
+ return this.fetch("/business/webhook/list/", params.event_type);
277
+ }
278
+ delete(params) {
279
+ return this.post("/business/webhook/delete/", {
280
+ event_type: params.event_type
281
+ });
282
+ }
283
+ async post(path, body) {
284
+ console.log("called post");
285
+ const response = await fetch(`${TIKTOK_API_BASE_URL}${path}`, {
286
+ method: "POST",
287
+ headers: { "Content-Type": "application/json" },
288
+ body: JSON.stringify({
289
+ app_id: this.config.clientKey,
290
+ secret: this.config.clientSecret,
291
+ ...body
292
+ })
293
+ });
294
+ console.log(response.status, await response.text());
295
+ return this.parseResponse(response);
296
+ }
297
+ async fetch(path, event_type) {
298
+ const url = new URL(`${TIKTOK_API_BASE_URL}${path}`);
299
+ url.searchParams.set("app_id", this.config.clientKey);
300
+ url.searchParams.set("secret", this.config.clientSecret);
301
+ url.searchParams.set("event_type", event_type);
302
+ const response = await globalThis.fetch(url.toString());
303
+ return this.parseResponse(response);
304
+ }
305
+ async parseResponse(response) {
306
+ if (!response.ok) {
307
+ throw new TikTokApiError(response.status, response.statusText);
308
+ }
309
+ const json = await response.json();
310
+ if (json.code !== 0) {
311
+ throw new TikTokApiError(json.code, json.request_id);
312
+ }
313
+ return json.data;
314
+ }
315
+ };
316
+
317
+ // src/client.ts
318
+ var TikTokBusiness = class {
319
+ auth;
320
+ messaging;
321
+ media;
322
+ webhook;
323
+ constructor(config) {
324
+ const http = new HttpClient(config);
325
+ this.auth = new Auth(config);
326
+ this.messaging = new Messaging(http);
327
+ this.media = new MediaService(http);
328
+ this.webhook = new WebhookService(config);
329
+ }
330
+ };
331
+ // Annotate the CommonJS export names for ESM import in node:
332
+ 0 && (module.exports = {
333
+ TikTokApiError,
334
+ TikTokBusiness
335
+ });
@@ -0,0 +1,263 @@
1
+ interface TikTokTokenResponse {
2
+ access_token: string;
3
+ refresh_token: string;
4
+ access_token_expire_in: number;
5
+ refresh_token_expire_in: number;
6
+ token_type: string;
7
+ scope: string;
8
+ open_id: string;
9
+ }
10
+ interface RefreshTokenResponse {
11
+ access_token: string;
12
+ access_token_expire_in: number;
13
+ refresh_token: string;
14
+ refresh_token_expire_in: number;
15
+ token_type: string;
16
+ scope: string;
17
+ open_id: string;
18
+ }
19
+ interface RevokeTokenResponse {
20
+ }
21
+
22
+ interface TikTokConfig {
23
+ clientKey: string;
24
+ clientSecret: string;
25
+ redirectUri?: string;
26
+ scopes?: string[];
27
+ baseUrl?: string;
28
+ timeout?: number;
29
+ }
30
+
31
+ type MediaType = "IMAGE";
32
+ interface UploadMediaRequest {
33
+ business_id: string;
34
+ file: File | Blob;
35
+ media_type: MediaType;
36
+ }
37
+ interface MediaContent {
38
+ media_id: string;
39
+ }
40
+ type UploadMediaResponse = MediaContent;
41
+
42
+ type MessageType = "TEXT" | "SENDER_ACTION";
43
+ type SenderAction = "TYPING" | "MARK_READ";
44
+ type RecipientType = "CONVERSATION";
45
+ type ConversationStatus = "ACTIVE" | "ARCHIVED";
46
+ interface ListMessagesRequest {
47
+ business_id: string;
48
+ conversation_id: string;
49
+ }
50
+ interface Message {
51
+ message_id: string;
52
+ conversation_id: string;
53
+ message_type: MessageType;
54
+ create_time: number;
55
+ direction: "INBOUND" | "OUTBOUND";
56
+ status: "SENT" | "DELIVERED" | "READ" | "FAILED";
57
+ sender: {
58
+ open_id: string;
59
+ display_name: string;
60
+ avatar_url: string;
61
+ };
62
+ content: {
63
+ body: string;
64
+ };
65
+ }
66
+ interface ListMessagesResponse {
67
+ messages: Message[];
68
+ next_cursor: string | null;
69
+ has_more: boolean;
70
+ total: number;
71
+ }
72
+ interface TextContent {
73
+ body: string;
74
+ }
75
+ interface SendTextMessageRequest {
76
+ conversation_id?: string;
77
+ business_id: string;
78
+ recipient: string;
79
+ message_type: MessageType;
80
+ recipient_type?: RecipientType;
81
+ text?: TextContent;
82
+ sender_action?: SenderAction;
83
+ }
84
+ interface SendTextMessageResponse {
85
+ message_id: string;
86
+ create_time: number;
87
+ }
88
+ type ConversationType = "STRANGER" | "SINGLE";
89
+ interface ReferralAd {
90
+ advertiser_id: string;
91
+ ad_id: string;
92
+ timestamp: number;
93
+ ad_name: string;
94
+ embed_url: string;
95
+ message_material_id?: string;
96
+ }
97
+ interface ReferralShortLink {
98
+ ref: string;
99
+ prefilled_message: string;
100
+ prefilled_message_audit_status: "PASS" | "REJECT";
101
+ }
102
+ interface ConversationReferral {
103
+ ad?: ReferralAd[];
104
+ short_link?: ReferralShortLink[];
105
+ }
106
+ interface Conversation {
107
+ conversation_id: string;
108
+ update_time: number;
109
+ referral: ConversationReferral;
110
+ }
111
+ interface ListConversationsRequest {
112
+ business_id: string;
113
+ cursor?: number;
114
+ conversation_type: ConversationType;
115
+ limit?: number;
116
+ }
117
+ interface ListConversationsResponse {
118
+ conversations: Conversation[];
119
+ has_more: boolean;
120
+ cursor: number;
121
+ }
122
+ interface SendImageMessageRequest {
123
+ business_id: string;
124
+ recipient_type: RecipientType;
125
+ message_type: "IMAGE";
126
+ recipient: string;
127
+ image: MediaContent;
128
+ }
129
+ interface SendImageMessageResponse {
130
+ message_id: string;
131
+ create_time: number;
132
+ }
133
+
134
+ declare class Auth {
135
+ private readonly config;
136
+ constructor(config: TikTokConfig);
137
+ getTokens(authCode: string): Promise<TikTokTokenResponse>;
138
+ refreshTokens(refreshToken: string): Promise<RefreshTokenResponse>;
139
+ revokeToken(accessToken: string): Promise<RevokeTokenResponse>;
140
+ getAuthorizationUrl(state?: string): string;
141
+ private post;
142
+ }
143
+
144
+ interface RequestOptions {
145
+ accessToken: string;
146
+ advertiserIds?: string[];
147
+ timeout?: number;
148
+ }
149
+
150
+ declare class HttpClient {
151
+ private config;
152
+ private baseUrl;
153
+ constructor(config: TikTokConfig);
154
+ get<T>(path: string, params: object, opts: RequestOptions): Promise<T>;
155
+ post<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
156
+ put<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
157
+ patch<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
158
+ delete<T>(path: string, params: object, opts: RequestOptions): Promise<T>;
159
+ upload<T>(path: string, form: FormData, opts: RequestOptions): Promise<T>;
160
+ private request;
161
+ private buildUrl;
162
+ private buildInit;
163
+ private parseResponse;
164
+ }
165
+
166
+ declare class MediaService {
167
+ private readonly http;
168
+ constructor(http: HttpClient);
169
+ upload(params: UploadMediaRequest, opts: RequestOptions): Promise<UploadMediaResponse>;
170
+ }
171
+
172
+ declare class Messaging {
173
+ private readonly http;
174
+ constructor(http: HttpClient);
175
+ /**
176
+ * List all conversations for a business account
177
+ */
178
+ listConversations(params: ListConversationsRequest, opts: RequestOptions): Promise<ListConversationsResponse>;
179
+ /**
180
+ * Get messages in a specific conversation
181
+ */
182
+ listMessages(params: ListMessagesRequest, opts: RequestOptions): Promise<ListMessagesResponse>;
183
+ /**
184
+ * Send a text message to a conversation
185
+ */
186
+ sendTextMessage(body: SendTextMessageRequest, opts: RequestOptions): Promise<SendTextMessageResponse>;
187
+ /**
188
+ * Send an image message to a conversation
189
+ */
190
+ sendImageMessage(body: SendImageMessageRequest, opts: RequestOptions): Promise<SendImageMessageResponse>;
191
+ }
192
+
193
+ type WebhookEventType = "DIRECT_MESSAGE";
194
+ interface CreateWebhookRequest {
195
+ callback_url: string;
196
+ event_type: WebhookEventType;
197
+ }
198
+ interface CreateWebhookResponse {
199
+ app_id: string;
200
+ event_type: WebhookEventType;
201
+ callback_url: string;
202
+ }
203
+ interface GetWebhookRequest {
204
+ event_type: WebhookEventType;
205
+ }
206
+ interface GetWebhookResponse {
207
+ app_id: string;
208
+ event_type: WebhookEventType;
209
+ callback_url?: string;
210
+ }
211
+ interface DeleteWebhookRequest {
212
+ event_type: WebhookEventType;
213
+ }
214
+ interface DeleteWebhookResponse {
215
+ app_id: string;
216
+ event_type: WebhookEventType;
217
+ }
218
+
219
+ declare class WebhookService {
220
+ private readonly config;
221
+ constructor(config: TikTokConfig);
222
+ create(params: CreateWebhookRequest): Promise<CreateWebhookResponse>;
223
+ get(params: GetWebhookRequest): Promise<GetWebhookResponse>;
224
+ delete(params: DeleteWebhookRequest): Promise<DeleteWebhookResponse>;
225
+ private post;
226
+ private fetch;
227
+ private parseResponse;
228
+ }
229
+
230
+ declare class TikTokBusiness {
231
+ readonly auth: Auth;
232
+ readonly messaging: Messaging;
233
+ readonly media: MediaService;
234
+ readonly webhook: WebhookService;
235
+ constructor(config: TikTokConfig);
236
+ }
237
+
238
+ /**
239
+ * TikTok Business API Error Codes
240
+ */
241
+ declare enum TikTokErrorCode {
242
+ NO_PERMISSION = 40001,
243
+ PARAMETER_ERROR = 40002,
244
+ OBJECT_NOT_FOUND = 40007,
245
+ RATE_LIMIT_EXCEEDED = 40100,
246
+ INVALID_ACCESS_TOKEN = 40105,
247
+ UNSUPPORTED_FILE_TYPE = 40908,
248
+ MESSAGE_BLOCKED = 40064,
249
+ SYSTEM_ERROR = 51065,
250
+ CODE_EXPIRED = 40131
251
+ }
252
+
253
+ /**
254
+ * TikTok API Errors
255
+ */
256
+ declare class TikTokApiError extends Error {
257
+ readonly code: TikTokErrorCode;
258
+ readonly requestId?: string | undefined;
259
+ readonly message: string;
260
+ constructor(code: TikTokErrorCode, requestId?: string | undefined);
261
+ }
262
+
263
+ export { type Conversation, type ConversationReferral, type ConversationStatus, type ConversationType, type ListConversationsRequest, type ListConversationsResponse, type ListMessagesRequest, type ListMessagesResponse, type Message, type MessageType, type RecipientType, type ReferralAd, type ReferralShortLink, type RefreshTokenResponse, type RevokeTokenResponse, type SendImageMessageRequest, type SendImageMessageResponse, type SendTextMessageRequest, type SendTextMessageResponse, type SenderAction, type TextContent, TikTokApiError, TikTokBusiness, type TikTokConfig, type TikTokTokenResponse };
@@ -0,0 +1,263 @@
1
+ interface TikTokTokenResponse {
2
+ access_token: string;
3
+ refresh_token: string;
4
+ access_token_expire_in: number;
5
+ refresh_token_expire_in: number;
6
+ token_type: string;
7
+ scope: string;
8
+ open_id: string;
9
+ }
10
+ interface RefreshTokenResponse {
11
+ access_token: string;
12
+ access_token_expire_in: number;
13
+ refresh_token: string;
14
+ refresh_token_expire_in: number;
15
+ token_type: string;
16
+ scope: string;
17
+ open_id: string;
18
+ }
19
+ interface RevokeTokenResponse {
20
+ }
21
+
22
+ interface TikTokConfig {
23
+ clientKey: string;
24
+ clientSecret: string;
25
+ redirectUri?: string;
26
+ scopes?: string[];
27
+ baseUrl?: string;
28
+ timeout?: number;
29
+ }
30
+
31
+ type MediaType = "IMAGE";
32
+ interface UploadMediaRequest {
33
+ business_id: string;
34
+ file: File | Blob;
35
+ media_type: MediaType;
36
+ }
37
+ interface MediaContent {
38
+ media_id: string;
39
+ }
40
+ type UploadMediaResponse = MediaContent;
41
+
42
+ type MessageType = "TEXT" | "SENDER_ACTION";
43
+ type SenderAction = "TYPING" | "MARK_READ";
44
+ type RecipientType = "CONVERSATION";
45
+ type ConversationStatus = "ACTIVE" | "ARCHIVED";
46
+ interface ListMessagesRequest {
47
+ business_id: string;
48
+ conversation_id: string;
49
+ }
50
+ interface Message {
51
+ message_id: string;
52
+ conversation_id: string;
53
+ message_type: MessageType;
54
+ create_time: number;
55
+ direction: "INBOUND" | "OUTBOUND";
56
+ status: "SENT" | "DELIVERED" | "READ" | "FAILED";
57
+ sender: {
58
+ open_id: string;
59
+ display_name: string;
60
+ avatar_url: string;
61
+ };
62
+ content: {
63
+ body: string;
64
+ };
65
+ }
66
+ interface ListMessagesResponse {
67
+ messages: Message[];
68
+ next_cursor: string | null;
69
+ has_more: boolean;
70
+ total: number;
71
+ }
72
+ interface TextContent {
73
+ body: string;
74
+ }
75
+ interface SendTextMessageRequest {
76
+ conversation_id?: string;
77
+ business_id: string;
78
+ recipient: string;
79
+ message_type: MessageType;
80
+ recipient_type?: RecipientType;
81
+ text?: TextContent;
82
+ sender_action?: SenderAction;
83
+ }
84
+ interface SendTextMessageResponse {
85
+ message_id: string;
86
+ create_time: number;
87
+ }
88
+ type ConversationType = "STRANGER" | "SINGLE";
89
+ interface ReferralAd {
90
+ advertiser_id: string;
91
+ ad_id: string;
92
+ timestamp: number;
93
+ ad_name: string;
94
+ embed_url: string;
95
+ message_material_id?: string;
96
+ }
97
+ interface ReferralShortLink {
98
+ ref: string;
99
+ prefilled_message: string;
100
+ prefilled_message_audit_status: "PASS" | "REJECT";
101
+ }
102
+ interface ConversationReferral {
103
+ ad?: ReferralAd[];
104
+ short_link?: ReferralShortLink[];
105
+ }
106
+ interface Conversation {
107
+ conversation_id: string;
108
+ update_time: number;
109
+ referral: ConversationReferral;
110
+ }
111
+ interface ListConversationsRequest {
112
+ business_id: string;
113
+ cursor?: number;
114
+ conversation_type: ConversationType;
115
+ limit?: number;
116
+ }
117
+ interface ListConversationsResponse {
118
+ conversations: Conversation[];
119
+ has_more: boolean;
120
+ cursor: number;
121
+ }
122
+ interface SendImageMessageRequest {
123
+ business_id: string;
124
+ recipient_type: RecipientType;
125
+ message_type: "IMAGE";
126
+ recipient: string;
127
+ image: MediaContent;
128
+ }
129
+ interface SendImageMessageResponse {
130
+ message_id: string;
131
+ create_time: number;
132
+ }
133
+
134
+ declare class Auth {
135
+ private readonly config;
136
+ constructor(config: TikTokConfig);
137
+ getTokens(authCode: string): Promise<TikTokTokenResponse>;
138
+ refreshTokens(refreshToken: string): Promise<RefreshTokenResponse>;
139
+ revokeToken(accessToken: string): Promise<RevokeTokenResponse>;
140
+ getAuthorizationUrl(state?: string): string;
141
+ private post;
142
+ }
143
+
144
+ interface RequestOptions {
145
+ accessToken: string;
146
+ advertiserIds?: string[];
147
+ timeout?: number;
148
+ }
149
+
150
+ declare class HttpClient {
151
+ private config;
152
+ private baseUrl;
153
+ constructor(config: TikTokConfig);
154
+ get<T>(path: string, params: object, opts: RequestOptions): Promise<T>;
155
+ post<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
156
+ put<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
157
+ patch<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
158
+ delete<T>(path: string, params: object, opts: RequestOptions): Promise<T>;
159
+ upload<T>(path: string, form: FormData, opts: RequestOptions): Promise<T>;
160
+ private request;
161
+ private buildUrl;
162
+ private buildInit;
163
+ private parseResponse;
164
+ }
165
+
166
+ declare class MediaService {
167
+ private readonly http;
168
+ constructor(http: HttpClient);
169
+ upload(params: UploadMediaRequest, opts: RequestOptions): Promise<UploadMediaResponse>;
170
+ }
171
+
172
+ declare class Messaging {
173
+ private readonly http;
174
+ constructor(http: HttpClient);
175
+ /**
176
+ * List all conversations for a business account
177
+ */
178
+ listConversations(params: ListConversationsRequest, opts: RequestOptions): Promise<ListConversationsResponse>;
179
+ /**
180
+ * Get messages in a specific conversation
181
+ */
182
+ listMessages(params: ListMessagesRequest, opts: RequestOptions): Promise<ListMessagesResponse>;
183
+ /**
184
+ * Send a text message to a conversation
185
+ */
186
+ sendTextMessage(body: SendTextMessageRequest, opts: RequestOptions): Promise<SendTextMessageResponse>;
187
+ /**
188
+ * Send an image message to a conversation
189
+ */
190
+ sendImageMessage(body: SendImageMessageRequest, opts: RequestOptions): Promise<SendImageMessageResponse>;
191
+ }
192
+
193
+ type WebhookEventType = "DIRECT_MESSAGE";
194
+ interface CreateWebhookRequest {
195
+ callback_url: string;
196
+ event_type: WebhookEventType;
197
+ }
198
+ interface CreateWebhookResponse {
199
+ app_id: string;
200
+ event_type: WebhookEventType;
201
+ callback_url: string;
202
+ }
203
+ interface GetWebhookRequest {
204
+ event_type: WebhookEventType;
205
+ }
206
+ interface GetWebhookResponse {
207
+ app_id: string;
208
+ event_type: WebhookEventType;
209
+ callback_url?: string;
210
+ }
211
+ interface DeleteWebhookRequest {
212
+ event_type: WebhookEventType;
213
+ }
214
+ interface DeleteWebhookResponse {
215
+ app_id: string;
216
+ event_type: WebhookEventType;
217
+ }
218
+
219
+ declare class WebhookService {
220
+ private readonly config;
221
+ constructor(config: TikTokConfig);
222
+ create(params: CreateWebhookRequest): Promise<CreateWebhookResponse>;
223
+ get(params: GetWebhookRequest): Promise<GetWebhookResponse>;
224
+ delete(params: DeleteWebhookRequest): Promise<DeleteWebhookResponse>;
225
+ private post;
226
+ private fetch;
227
+ private parseResponse;
228
+ }
229
+
230
+ declare class TikTokBusiness {
231
+ readonly auth: Auth;
232
+ readonly messaging: Messaging;
233
+ readonly media: MediaService;
234
+ readonly webhook: WebhookService;
235
+ constructor(config: TikTokConfig);
236
+ }
237
+
238
+ /**
239
+ * TikTok Business API Error Codes
240
+ */
241
+ declare enum TikTokErrorCode {
242
+ NO_PERMISSION = 40001,
243
+ PARAMETER_ERROR = 40002,
244
+ OBJECT_NOT_FOUND = 40007,
245
+ RATE_LIMIT_EXCEEDED = 40100,
246
+ INVALID_ACCESS_TOKEN = 40105,
247
+ UNSUPPORTED_FILE_TYPE = 40908,
248
+ MESSAGE_BLOCKED = 40064,
249
+ SYSTEM_ERROR = 51065,
250
+ CODE_EXPIRED = 40131
251
+ }
252
+
253
+ /**
254
+ * TikTok API Errors
255
+ */
256
+ declare class TikTokApiError extends Error {
257
+ readonly code: TikTokErrorCode;
258
+ readonly requestId?: string | undefined;
259
+ readonly message: string;
260
+ constructor(code: TikTokErrorCode, requestId?: string | undefined);
261
+ }
262
+
263
+ export { type Conversation, type ConversationReferral, type ConversationStatus, type ConversationType, type ListConversationsRequest, type ListConversationsResponse, type ListMessagesRequest, type ListMessagesResponse, type Message, type MessageType, type RecipientType, type ReferralAd, type ReferralShortLink, type RefreshTokenResponse, type RevokeTokenResponse, type SendImageMessageRequest, type SendImageMessageResponse, type SendTextMessageRequest, type SendTextMessageResponse, type SenderAction, type TextContent, TikTokApiError, TikTokBusiness, type TikTokConfig, type TikTokTokenResponse };
package/dist/index.js ADDED
@@ -0,0 +1,307 @@
1
+ // src/constants.ts
2
+ var TIKTOK_API_BASE_URL = "https://business-api.tiktok.com/open_api/v1.3";
3
+ var TIKTOK_AUTH_BASE_URL = `${TIKTOK_API_BASE_URL}/tt_user/oauth2`;
4
+ var TIKTOK_ERROR_MESSAGES = {
5
+ [40001 /* NO_PERMISSION */]: "No permission to perform the related operation. Ensure you have the necessary scopes.",
6
+ [40002 /* PARAMETER_ERROR */]: "Parameter error. Check missing fields or incorrect formats in your request.",
7
+ [40007 /* OBJECT_NOT_FOUND */]: "The operation or object does not exist. Check your IDs or URLs.",
8
+ [40100 /* RATE_LIMIT_EXCEEDED */]: "Requests made too frequently. You are being throttled.",
9
+ [40105 /* INVALID_ACCESS_TOKEN */]: "Invalid or incorrect access token. Please refresh or re-authorize.",
10
+ [40908 /* UNSUPPORTED_FILE_TYPE */]: "The file type is unsupported. Refer to documentation for allowed formats.",
11
+ [40064 /* MESSAGE_BLOCKED */]: "The message is blocked due to TikTok direct message rules.",
12
+ [51065 /* SYSTEM_ERROR */]: "TikTok internal system error. Please retry the request.",
13
+ [40131 /* CODE_EXPIRED */]: "The authorization code has expired. Please re-authorize to get a new code."
14
+ };
15
+
16
+ // src/error.ts
17
+ var TikTokApiError = class _TikTokApiError extends Error {
18
+ constructor(code, requestId) {
19
+ const friendlyMessage = TIKTOK_ERROR_MESSAGES[code] || "An unknown TikTok API error occurred.";
20
+ super(`TikTok API Error [${code}]: ${friendlyMessage}`);
21
+ this.code = code;
22
+ this.requestId = requestId;
23
+ this.name = "TikTokApiError";
24
+ this.message = friendlyMessage;
25
+ Object.setPrototypeOf(this, _TikTokApiError.prototype);
26
+ }
27
+ message;
28
+ };
29
+
30
+ // src/http.ts
31
+ var HttpClient = class {
32
+ constructor(config) {
33
+ this.config = config;
34
+ this.baseUrl = config.baseUrl ?? "https://business-api.tiktok.com/open_api/v1.3";
35
+ }
36
+ baseUrl;
37
+ get(path, params, opts) {
38
+ return this.request({ method: "GET", path, params, opts });
39
+ }
40
+ post(path, body, opts) {
41
+ return this.request({ method: "POST", path, body, opts });
42
+ }
43
+ put(path, body, opts) {
44
+ return this.request({ method: "PUT", path, body, opts });
45
+ }
46
+ patch(path, body, opts) {
47
+ return this.request({ method: "PATCH", path, body, opts });
48
+ }
49
+ delete(path, params, opts) {
50
+ return this.request({ method: "DELETE", path, params, opts });
51
+ }
52
+ upload(path, form, opts) {
53
+ return this.request({ method: "POST", path, form, opts });
54
+ }
55
+ async request({
56
+ method,
57
+ path,
58
+ params,
59
+ body,
60
+ form,
61
+ opts
62
+ }) {
63
+ const res = await fetch(
64
+ this.buildUrl(path, params),
65
+ this.buildInit(method, body, opts, form)
66
+ );
67
+ return this.parseResponse(res);
68
+ }
69
+ buildUrl(path, params) {
70
+ const url = new URL(`${this.baseUrl}${path}`);
71
+ if (params) {
72
+ Object.entries(params).forEach(([k, v]) => {
73
+ if (v !== void 0 && v !== null && v !== "") {
74
+ url.searchParams.set(k, String(v));
75
+ }
76
+ });
77
+ }
78
+ return url.toString();
79
+ }
80
+ buildInit(method, body, opts, form) {
81
+ const headers = {
82
+ "Access-Token": opts.accessToken
83
+ };
84
+ if (!form) {
85
+ headers["Content-Type"] = "application/json";
86
+ }
87
+ return {
88
+ method,
89
+ signal: AbortSignal.timeout(
90
+ opts.timeout ?? this.config.timeout ?? 1e4
91
+ ),
92
+ headers,
93
+ ...form ? { body: form } : body !== void 0 ? { body: JSON.stringify(body) } : {}
94
+ };
95
+ }
96
+ async parseResponse(res) {
97
+ console.debug(`[HttpClient] Parsing response for URL: ${res.url}`);
98
+ console.debug(
99
+ `[HttpClient] Response status: ${res.status} ${res.statusText}`
100
+ );
101
+ console.debug(`[HttpClient] Response data:`, await res.clone().text());
102
+ const json = await res.json();
103
+ if (json.code !== 0) {
104
+ throw new TikTokApiError(json.code, json.request_id);
105
+ }
106
+ return json.data;
107
+ }
108
+ };
109
+
110
+ // src/services/auth.ts
111
+ var Auth = class {
112
+ constructor(config) {
113
+ this.config = config;
114
+ }
115
+ getTokens(authCode) {
116
+ return this.post("/token/", {
117
+ client_id: this.config.clientKey,
118
+ client_secret: this.config.clientSecret,
119
+ grant_type: "authorization_code",
120
+ auth_code: decodeURIComponent(authCode),
121
+ redirect_uri: this.config.redirectUri
122
+ });
123
+ }
124
+ refreshTokens(refreshToken) {
125
+ return this.post("/refresh_token/", {
126
+ client_id: this.config.clientKey,
127
+ client_secret: this.config.clientSecret,
128
+ grant_type: "refresh_token",
129
+ refresh_token: refreshToken
130
+ });
131
+ }
132
+ revokeToken(accessToken) {
133
+ return this.post("/token/revoke/", {
134
+ client_id: this.config.clientKey,
135
+ client_secret: this.config.clientSecret,
136
+ token: accessToken
137
+ });
138
+ }
139
+ // for tt_users
140
+ getAuthorizationUrl(state) {
141
+ const params = new URLSearchParams({
142
+ client_key: this.config.clientKey,
143
+ response_type: "code",
144
+ scope: (this.config.scopes ?? []).join(","),
145
+ redirect_uri: this.config.redirectUri ?? "",
146
+ ...state && { state }
147
+ });
148
+ return `https://www.tiktok.com/v2/auth/authorize/?${params.toString()}`;
149
+ }
150
+ async post(path, body) {
151
+ const response = await fetch(`${TIKTOK_AUTH_BASE_URL}${path}`, {
152
+ method: "POST",
153
+ headers: { "Content-Type": "application/json" },
154
+ body: JSON.stringify(body)
155
+ });
156
+ if (!response.ok) {
157
+ throw new TikTokApiError(response.status, response.statusText);
158
+ }
159
+ const json = await response.json();
160
+ if (json.code !== 0) {
161
+ throw new TikTokApiError(json.code, json.request_id);
162
+ }
163
+ return json.data;
164
+ }
165
+ };
166
+
167
+ // src/services/media.ts
168
+ var MediaService = class {
169
+ constructor(http) {
170
+ this.http = http;
171
+ }
172
+ upload(params, opts) {
173
+ const form = new FormData();
174
+ form.append("business_id", params.business_id);
175
+ form.append("file", params.file);
176
+ form.append("media_type", params.media_type);
177
+ return this.http.upload("/business/message/media/upload/", form, opts);
178
+ }
179
+ };
180
+
181
+ // src/services/messaging.ts
182
+ var Messaging = class {
183
+ constructor(http) {
184
+ this.http = http;
185
+ }
186
+ /**
187
+ * List all conversations for a business account
188
+ */
189
+ listConversations(params, opts) {
190
+ console.debug("[Messaging] listConversations called", { params });
191
+ return this.http.get("/business/message/conversation/list/", params, opts).then((response) => {
192
+ console.debug("[Messaging] listConversations succeeded");
193
+ return response;
194
+ }).catch((error) => {
195
+ console.error("[Messaging] listConversations failed", error);
196
+ throw error;
197
+ });
198
+ }
199
+ /**
200
+ * Get messages in a specific conversation
201
+ */
202
+ listMessages(params, opts) {
203
+ console.log("Conversation ID:", encodeURIComponent(params.conversation_id));
204
+ return this.http.get(
205
+ "/business/message/content/list/",
206
+ {
207
+ ...params
208
+ },
209
+ opts
210
+ );
211
+ }
212
+ /**
213
+ * Send a text message to a conversation
214
+ */
215
+ sendTextMessage(body, opts) {
216
+ const { ...restBody } = body;
217
+ return this.http.post(
218
+ "/business/message/send/",
219
+ { recipient_type: "CONVERSATION", ...restBody },
220
+ opts
221
+ );
222
+ }
223
+ /**
224
+ * Send an image message to a conversation
225
+ */
226
+ sendImageMessage(body, opts) {
227
+ return this.http.post(
228
+ "/business/message/send/",
229
+ {
230
+ ...body
231
+ },
232
+ opts
233
+ );
234
+ }
235
+ };
236
+
237
+ // src/services/webhook.ts
238
+ var WebhookService = class {
239
+ constructor(config) {
240
+ this.config = config;
241
+ }
242
+ create(params) {
243
+ return this.post("/business/webhook/update/", {
244
+ event_type: params.event_type,
245
+ callback_url: params.callback_url
246
+ });
247
+ }
248
+ get(params) {
249
+ return this.fetch("/business/webhook/list/", params.event_type);
250
+ }
251
+ delete(params) {
252
+ return this.post("/business/webhook/delete/", {
253
+ event_type: params.event_type
254
+ });
255
+ }
256
+ async post(path, body) {
257
+ console.log("called post");
258
+ const response = await fetch(`${TIKTOK_API_BASE_URL}${path}`, {
259
+ method: "POST",
260
+ headers: { "Content-Type": "application/json" },
261
+ body: JSON.stringify({
262
+ app_id: this.config.clientKey,
263
+ secret: this.config.clientSecret,
264
+ ...body
265
+ })
266
+ });
267
+ console.log(response.status, await response.text());
268
+ return this.parseResponse(response);
269
+ }
270
+ async fetch(path, event_type) {
271
+ const url = new URL(`${TIKTOK_API_BASE_URL}${path}`);
272
+ url.searchParams.set("app_id", this.config.clientKey);
273
+ url.searchParams.set("secret", this.config.clientSecret);
274
+ url.searchParams.set("event_type", event_type);
275
+ const response = await globalThis.fetch(url.toString());
276
+ return this.parseResponse(response);
277
+ }
278
+ async parseResponse(response) {
279
+ if (!response.ok) {
280
+ throw new TikTokApiError(response.status, response.statusText);
281
+ }
282
+ const json = await response.json();
283
+ if (json.code !== 0) {
284
+ throw new TikTokApiError(json.code, json.request_id);
285
+ }
286
+ return json.data;
287
+ }
288
+ };
289
+
290
+ // src/client.ts
291
+ var TikTokBusiness = class {
292
+ auth;
293
+ messaging;
294
+ media;
295
+ webhook;
296
+ constructor(config) {
297
+ const http = new HttpClient(config);
298
+ this.auth = new Auth(config);
299
+ this.messaging = new Messaging(http);
300
+ this.media = new MediaService(http);
301
+ this.webhook = new WebhookService(config);
302
+ }
303
+ };
304
+ export {
305
+ TikTokApiError,
306
+ TikTokBusiness
307
+ };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "tiktok-business-messaging",
3
+ "version": "0.0.1",
4
+ "description": "TypeScript API Client for Tiktokbusiness API",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
14
+ "dev": "tsup src/index.ts --format cjs,esm --watch --dts",
15
+ "test": "vitest run",
16
+ "test-watch": "vitest",
17
+ "lint": "tsc",
18
+ "ci": "pnpm run lint && pnpm run test && pnpm run build"
19
+ },
20
+ "keywords": [],
21
+ "author": "Pshishir",
22
+ "license": "MIT",
23
+ "packageManager": "pnpm@10.30.0",
24
+ "devDependencies": {
25
+ "@types/node": "^25.5.0",
26
+ "tsup": "^8.5.1",
27
+ "typescript": "^5.9.3",
28
+ "vitest": "^4.0.18"
29
+ },
30
+ "dependencies": {
31
+ "tsx": "^4.21.0"
32
+ }
33
+ }