instagram-graph-api-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1696 @@
1
+ import axios from 'axios';
2
+ import crypto from 'crypto';
3
+
4
+ // src/http.ts
5
+
6
+ // src/endpoints.ts
7
+ var INSTAGRAM_BASE_URL = "https://graph.instagram.com";
8
+ function buildUrl(version, path) {
9
+ return `${INSTAGRAM_BASE_URL}/${version}${path}`;
10
+ }
11
+ var USER_ENDPOINTS = {
12
+ /** Get user profile: GET /{user-id} */
13
+ PROFILE: (userId) => `/${userId}`,
14
+ /** Get user media: GET /{user-id}/media */
15
+ MEDIA: (userId) => `/${userId}/media`,
16
+ /** Get user stories: GET /{user-id}/stories */
17
+ STORIES: (userId) => `/${userId}/stories`,
18
+ /** Get user insights: GET /{user-id}/insights */
19
+ INSIGHTS: (userId) => `/${userId}/insights`,
20
+ /** Get live media: GET /{user-id}/live_media */
21
+ LIVE_MEDIA: (userId) => `/${userId}/live_media`,
22
+ /** Get content publishing limit: GET /{user-id}/content_publishing_limit */
23
+ CONTENT_PUBLISHING_LIMIT: (userId) => `/${userId}/content_publishing_limit`,
24
+ /** Get business discovery: GET /{user-id}?fields=business_discovery.username(...) */
25
+ BUSINESS_DISCOVERY: (userId) => `/${userId}`,
26
+ /** Get mentioned media: GET /{user-id}/mentioned_media */
27
+ MENTIONED_MEDIA: (userId) => `/${userId}/mentioned_media`,
28
+ /** Get mentioned comment: GET /{user-id}/mentioned_comment */
29
+ MENTIONED_COMMENT: (userId) => `/${userId}/mentioned_comment`,
30
+ /** Get tags: GET /{user-id}/tags */
31
+ TAGS: (userId) => `/${userId}/tags`,
32
+ /** Get recently searched hashtags: GET /{user-id}/recently_searched_hashtags */
33
+ RECENTLY_SEARCHED_HASHTAGS: (userId) => `/${userId}/recently_searched_hashtags`,
34
+ /** Get available catalogs: GET /{user-id}/available_catalogs */
35
+ AVAILABLE_CATALOGS: (userId) => `/${userId}/available_catalogs`,
36
+ /** Search catalog products: GET /{user-id}/catalog_product_search */
37
+ CATALOG_PRODUCT_SEARCH: (userId) => `/${userId}/catalog_product_search`
38
+ };
39
+ var MEDIA_ENDPOINTS = {
40
+ /** Get media by ID: GET /{media-id} */
41
+ GET: (mediaId) => `/${mediaId}`,
42
+ /** Get media children (carousel): GET /{media-id}/children */
43
+ CHILDREN: (mediaId) => `/${mediaId}/children`,
44
+ /** Get media comments: GET /{media-id}/comments */
45
+ COMMENTS: (mediaId) => `/${mediaId}/comments`,
46
+ /** Get media insights: GET /{media-id}/insights */
47
+ INSIGHTS: (mediaId) => `/${mediaId}/insights`,
48
+ /** Get media collaborators: GET /{media-id}/collaborators */
49
+ COLLABORATORS: (mediaId) => `/${mediaId}/collaborators`,
50
+ /** Get product tags: GET /{media-id}/product_tags */
51
+ PRODUCT_TAGS: (mediaId) => `/${mediaId}/product_tags`
52
+ };
53
+ var PUBLISHING_ENDPOINTS = {
54
+ /** Create media container: POST /{user-id}/media */
55
+ CREATE_CONTAINER: (userId) => `/${userId}/media`,
56
+ /** Publish media: POST /{user-id}/media_publish */
57
+ PUBLISH: (userId) => `/${userId}/media_publish`,
58
+ /** Get container status: GET /{container-id} */
59
+ CONTAINER_STATUS: (containerId) => `/${containerId}`
60
+ };
61
+ var MESSAGING_ENDPOINTS = {
62
+ /** Send message: POST /{user-id}/messages */
63
+ SEND: (userId) => `/${userId}/messages`,
64
+ /** Get conversations: GET /{user-id}/conversations */
65
+ CONVERSATIONS: (userId) => `/${userId}/conversations`,
66
+ /** Get conversation by ID: GET /{conversation-id} */
67
+ CONVERSATION: (conversationId) => `/${conversationId}`,
68
+ /** Get message by ID: GET /{message-id} */
69
+ MESSAGE: (messageId) => `/${messageId}`
70
+ };
71
+ var WELCOME_FLOW_ENDPOINTS = {
72
+ /** Get/Create/Update flows: /{user-id}/welcome_message_flows */
73
+ FLOWS: (userId) => `/${userId}/welcome_message_flows`
74
+ };
75
+ var MESSENGER_PROFILE_ENDPOINTS = {
76
+ /** Get/Set/Delete profile: /{user-id}/messenger_profile */
77
+ PROFILE: (userId) => `/${userId}/messenger_profile`
78
+ };
79
+ var COMMENT_ENDPOINTS = {
80
+ /** Get comment: GET /{comment-id} */
81
+ GET: (commentId) => `/${commentId}`,
82
+ /** Get replies: GET /{comment-id}/replies */
83
+ REPLIES: (commentId) => `/${commentId}/replies`,
84
+ /** Reply to comment: POST /{comment-id}/replies */
85
+ REPLY: (commentId) => `/${commentId}/replies`,
86
+ /** Hide/Unhide comment: POST /{comment-id} */
87
+ UPDATE: (commentId) => `/${commentId}`,
88
+ /** Delete comment: DELETE /{comment-id} */
89
+ DELETE: (commentId) => `/${commentId}`
90
+ };
91
+ var HASHTAG_ENDPOINTS = {
92
+ /** Search hashtag: GET /ig_hashtag_search */
93
+ SEARCH: "/ig_hashtag_search",
94
+ /** Get hashtag: GET /{hashtag-id} */
95
+ GET: (hashtagId) => `/${hashtagId}`,
96
+ /** Get recent media: GET /{hashtag-id}/recent_media */
97
+ RECENT_MEDIA: (hashtagId) => `/${hashtagId}/recent_media`,
98
+ /** Get top media: GET /{hashtag-id}/top_media */
99
+ TOP_MEDIA: (hashtagId) => `/${hashtagId}/top_media`
100
+ };
101
+ var AUTH_ENDPOINTS = {
102
+ /** Get current user: GET /me */
103
+ ME: "/me",
104
+ /** Refresh token: GET /refresh_access_token */
105
+ REFRESH_TOKEN: "/refresh_access_token",
106
+ /** Exchange token: GET /access_token */
107
+ ACCESS_TOKEN: "/access_token"
108
+ };
109
+ var OAUTH_ENDPOINTS = {
110
+ /** Authorization URL - redirect users here to start OAuth flow */
111
+ AUTHORIZE: "https://www.instagram.com/oauth/authorize",
112
+ /** Short-lived token exchange: POST with form data */
113
+ TOKEN: "https://api.instagram.com/oauth/access_token",
114
+ /** Long-lived token exchange: GET with query params */
115
+ LONG_LIVED_TOKEN: "https://graph.instagram.com/access_token"
116
+ };
117
+ var OEMBED_ENDPOINTS = {
118
+ /** Get oEmbed: GET /instagram_oembed */
119
+ GET: "/instagram_oembed"
120
+ };
121
+
122
+ // src/errors.ts
123
+ var InstagramAPIError = class _InstagramAPIError extends Error {
124
+ constructor(message, code, type, subcode, fbTraceId) {
125
+ super(message);
126
+ this.name = "InstagramAPIError";
127
+ this.code = code;
128
+ this.type = type;
129
+ this.subcode = subcode;
130
+ this.fbTraceId = fbTraceId;
131
+ if (Error.captureStackTrace) {
132
+ Error.captureStackTrace(this, _InstagramAPIError);
133
+ }
134
+ }
135
+ /**
136
+ * Create an InstagramAPIError from an API response
137
+ */
138
+ static fromResponse(response) {
139
+ const { error } = response;
140
+ if (error.code === 190) {
141
+ return new AuthenticationError(error.message, error.code, error.fbtrace_id);
142
+ }
143
+ if (error.code === 4 || error.code === 17 || error.code === 32) {
144
+ return new RateLimitError(error.message, error.code, error.fbtrace_id);
145
+ }
146
+ if (error.code === 100) {
147
+ return new ValidationError(error.message, error.code, error.error_subcode, error.fbtrace_id);
148
+ }
149
+ return new _InstagramAPIError(
150
+ error.message,
151
+ error.code,
152
+ error.type,
153
+ error.error_subcode,
154
+ error.fbtrace_id
155
+ );
156
+ }
157
+ };
158
+ var AuthenticationError = class extends InstagramAPIError {
159
+ constructor(message, code = 190, fbTraceId) {
160
+ super(message, code, "OAuthException", void 0, fbTraceId);
161
+ this.name = "AuthenticationError";
162
+ }
163
+ };
164
+ var RateLimitError = class extends InstagramAPIError {
165
+ constructor(message, code = 4, fbTraceId, retryAfter) {
166
+ super(message, code, "RateLimitException", void 0, fbTraceId);
167
+ this.name = "RateLimitError";
168
+ this.retryAfter = retryAfter;
169
+ }
170
+ };
171
+ var ValidationError = class extends InstagramAPIError {
172
+ constructor(message, code = 100, subcode, fbTraceId) {
173
+ super(message, code, "ValidationException", subcode, fbTraceId);
174
+ this.name = "ValidationError";
175
+ }
176
+ };
177
+ var NetworkError = class _NetworkError extends Error {
178
+ constructor(message, originalError) {
179
+ super(message);
180
+ this.name = "NetworkError";
181
+ this.originalError = originalError;
182
+ if (Error.captureStackTrace) {
183
+ Error.captureStackTrace(this, _NetworkError);
184
+ }
185
+ }
186
+ };
187
+ function isInstagramAPIError(error) {
188
+ return error instanceof InstagramAPIError;
189
+ }
190
+ function isAuthenticationError(error) {
191
+ return error instanceof AuthenticationError;
192
+ }
193
+ function isRateLimitError(error) {
194
+ return error instanceof RateLimitError;
195
+ }
196
+ function isValidationError(error) {
197
+ return error instanceof ValidationError;
198
+ }
199
+
200
+ // src/http.ts
201
+ var HttpClient = class {
202
+ constructor(config) {
203
+ this.config = config;
204
+ this.client = axios.create({
205
+ baseURL: `${INSTAGRAM_BASE_URL}/${config.apiVersion}`,
206
+ timeout: config.timeout ?? 3e4,
207
+ headers: {
208
+ "Content-Type": "application/json",
209
+ ...config.headers
210
+ }
211
+ });
212
+ this.setupInterceptors();
213
+ }
214
+ /**
215
+ * Set up request and response interceptors
216
+ */
217
+ setupInterceptors() {
218
+ this.client.interceptors.request.use(
219
+ (config) => {
220
+ if (config.params) {
221
+ config.params.access_token = this.config.accessToken;
222
+ } else {
223
+ config.params = { access_token: this.config.accessToken };
224
+ }
225
+ return config;
226
+ },
227
+ (error) => Promise.reject(error)
228
+ );
229
+ this.client.interceptors.response.use(
230
+ (response) => response,
231
+ (error) => {
232
+ if (!error.response) {
233
+ throw new NetworkError(
234
+ error.message || "Network error occurred",
235
+ error
236
+ );
237
+ }
238
+ const response = error.response;
239
+ if (response.data?.error) {
240
+ throw InstagramAPIError.fromResponse(response.data);
241
+ }
242
+ if (response.status === 429) {
243
+ const retryAfter = parseInt(response.headers["retry-after"] || "60", 10);
244
+ throw new RateLimitError(
245
+ "Rate limit exceeded",
246
+ 4,
247
+ response.headers["x-fb-trace-id"],
248
+ retryAfter
249
+ );
250
+ }
251
+ throw new InstagramAPIError(
252
+ response.data?.message || "Unknown error occurred",
253
+ response.status,
254
+ "UnknownError"
255
+ );
256
+ }
257
+ );
258
+ }
259
+ /**
260
+ * Update access token
261
+ */
262
+ setAccessToken(accessToken) {
263
+ this.config.accessToken = accessToken;
264
+ }
265
+ /**
266
+ * GET request
267
+ */
268
+ async get(path, params, config) {
269
+ const response = await this.client.get(path, {
270
+ ...config,
271
+ params: { ...params }
272
+ });
273
+ return response.data;
274
+ }
275
+ /**
276
+ * POST request
277
+ */
278
+ async post(path, data, params, config) {
279
+ const response = await this.client.post(path, data, {
280
+ ...config,
281
+ params: { ...params }
282
+ });
283
+ return response.data;
284
+ }
285
+ /**
286
+ * DELETE request
287
+ */
288
+ async delete(path, params, config) {
289
+ const response = await this.client.delete(path, {
290
+ ...config,
291
+ params: { ...params }
292
+ });
293
+ return response.data;
294
+ }
295
+ /**
296
+ * POST with form data (for file uploads)
297
+ */
298
+ async postForm(path, data, config) {
299
+ const formData = new URLSearchParams();
300
+ Object.entries(data).forEach(([key, value]) => {
301
+ if (value !== void 0 && value !== null) {
302
+ formData.append(key, String(value));
303
+ }
304
+ });
305
+ const response = await this.client.post(path, formData, {
306
+ ...config,
307
+ headers: {
308
+ ...config?.headers,
309
+ "Content-Type": "application/x-www-form-urlencoded"
310
+ }
311
+ });
312
+ return response.data;
313
+ }
314
+ };
315
+
316
+ // src/api/auth.ts
317
+ var AuthApi = class {
318
+ constructor(http) {
319
+ this.http = http;
320
+ }
321
+ /**
322
+ * Get current user information
323
+ * @param fields - Fields to retrieve
324
+ */
325
+ async me(fields = "id,username") {
326
+ return this.http.get(AUTH_ENDPOINTS.ME, { fields });
327
+ }
328
+ /**
329
+ * Refresh a long-lived token (Instagram Login)
330
+ * Returns a new long-lived token with 60 days expiry
331
+ */
332
+ async refreshToken() {
333
+ return this.http.get(
334
+ AUTH_ENDPOINTS.REFRESH_TOKEN,
335
+ { grant_type: "ig_refresh_token" }
336
+ );
337
+ }
338
+ /**
339
+ * Exchange short-lived token for long-lived token (Instagram Login)
340
+ * @param shortLivedToken - Short-lived access token (1 hour expiry)
341
+ * @param appSecret - Instagram App Secret
342
+ */
343
+ async exchangeToken(shortLivedToken, appSecret) {
344
+ return this.http.get(
345
+ AUTH_ENDPOINTS.ACCESS_TOKEN,
346
+ {
347
+ grant_type: "ig_exchange_token",
348
+ client_secret: appSecret,
349
+ access_token: shortLivedToken
350
+ }
351
+ );
352
+ }
353
+ };
354
+
355
+ // src/api/oauth.ts
356
+ var InstagramOAuth = class {
357
+ /**
358
+ * Build the Instagram authorization URL
359
+ *
360
+ * Direct users to this URL to start the OAuth flow.
361
+ * They will be asked to grant permissions to your app.
362
+ *
363
+ * @param params - Authorization URL parameters
364
+ * @returns Full authorization URL to redirect user to
365
+ *
366
+ * @example
367
+ * ```typescript
368
+ * const url = InstagramOAuth.buildAuthorizationUrl({
369
+ * clientId: process.env.INSTAGRAM_APP_ID,
370
+ * redirectUri: `${process.env.APP_URL}/api/instagram/callback`,
371
+ * scopes: [
372
+ * 'instagram_business_basic',
373
+ * 'instagram_business_manage_messages',
374
+ * ],
375
+ * state: crypto.randomUUID(), // CSRF protection
376
+ * });
377
+ *
378
+ * // Redirect user to url
379
+ * ```
380
+ */
381
+ static buildAuthorizationUrl(params) {
382
+ const {
383
+ clientId,
384
+ redirectUri,
385
+ scopes,
386
+ state,
387
+ responseType = "code",
388
+ forceReauth
389
+ } = params;
390
+ const queryParams = new URLSearchParams({
391
+ client_id: clientId,
392
+ redirect_uri: redirectUri,
393
+ scope: scopes.join(","),
394
+ response_type: responseType
395
+ });
396
+ if (state) {
397
+ queryParams.set("state", state);
398
+ }
399
+ if (forceReauth) {
400
+ queryParams.set("force_reauth", "true");
401
+ }
402
+ return `${OAUTH_ENDPOINTS.AUTHORIZE}?${queryParams.toString()}`;
403
+ }
404
+ /**
405
+ * Parse OAuth callback parameters from URL
406
+ *
407
+ * Use this to extract code, state, and error info from the callback URL.
408
+ *
409
+ * @param url - The callback URL (or just the search params)
410
+ * @returns Parsed callback parameters
411
+ *
412
+ * @example
413
+ * ```typescript
414
+ * const params = InstagramOAuth.parseCallback(request.url);
415
+ *
416
+ * if (params.error) {
417
+ * // User denied access
418
+ * console.log(params.error_description);
419
+ * } else {
420
+ * // Exchange code for token
421
+ * const tokens = await InstagramOAuth.exchangeCodeForToken({
422
+ * code: params.code!,
423
+ * ...
424
+ * });
425
+ * }
426
+ * ```
427
+ */
428
+ static parseCallback(url) {
429
+ const urlObj = new URL(url, "https://placeholder.com");
430
+ const params = urlObj.searchParams;
431
+ let code = params.get("code") || void 0;
432
+ if (code) {
433
+ code = code.replace(/#_$/, "");
434
+ }
435
+ return {
436
+ code,
437
+ state: params.get("state") || void 0,
438
+ error: params.get("error") || void 0,
439
+ error_reason: params.get("error_reason") || void 0,
440
+ error_description: params.get("error_description") || void 0
441
+ };
442
+ }
443
+ /**
444
+ * Exchange authorization code for tokens
445
+ *
446
+ * This is the recommended method - it handles the full flow:
447
+ * 1. Exchange code for short-lived token (1 hour)
448
+ * 2. Exchange short-lived for long-lived token (60 days)
449
+ *
450
+ * @param params - Exchange parameters
451
+ * @returns Long-lived token response with user ID
452
+ * @throws Error if exchange fails
453
+ *
454
+ * @example
455
+ * ```typescript
456
+ * const tokens = await InstagramOAuth.exchangeCodeForToken({
457
+ * clientId: process.env.INSTAGRAM_APP_ID!,
458
+ * clientSecret: process.env.INSTAGRAM_APP_SECRET!,
459
+ * code: codeFromCallback,
460
+ * redirectUri: `${process.env.APP_URL}/api/instagram/callback`,
461
+ * });
462
+ *
463
+ * // Store tokens.access_token and tokens.user_id in your database
464
+ * // Token expires in tokens.expires_in seconds (~60 days)
465
+ * ```
466
+ */
467
+ static async exchangeCodeForToken(params) {
468
+ const shortLived = await this.getShortLivedToken(params);
469
+ const longLived = await this.getLongLivedToken({
470
+ clientSecret: params.clientSecret,
471
+ accessToken: shortLived.access_token
472
+ });
473
+ return {
474
+ access_token: longLived.access_token,
475
+ token_type: longLived.token_type,
476
+ expires_in: longLived.expires_in,
477
+ user_id: shortLived.user_id
478
+ };
479
+ }
480
+ /**
481
+ * Get short-lived token from authorization code
482
+ *
483
+ * Use this if you need more control over the token exchange process.
484
+ * The short-lived token is valid for 1 hour.
485
+ *
486
+ * @param params - Exchange parameters
487
+ * @returns Short-lived token response
488
+ * @throws Error if exchange fails
489
+ */
490
+ static async getShortLivedToken(params) {
491
+ const { clientId, clientSecret, code, redirectUri } = params;
492
+ const cleanCode = code.replace(/#_$/, "");
493
+ const formData = new FormData();
494
+ formData.append("client_id", clientId);
495
+ formData.append("client_secret", clientSecret);
496
+ formData.append("grant_type", "authorization_code");
497
+ formData.append("redirect_uri", redirectUri);
498
+ formData.append("code", cleanCode);
499
+ const response = await fetch(OAUTH_ENDPOINTS.TOKEN, {
500
+ method: "POST",
501
+ body: formData
502
+ });
503
+ const responseText = await response.text();
504
+ if (!response.ok) {
505
+ let error;
506
+ try {
507
+ error = JSON.parse(responseText);
508
+ } catch {
509
+ error = { error_message: responseText };
510
+ }
511
+ throw new Error(
512
+ error.error_message || error.error?.message || "Failed to exchange authorization code for token"
513
+ );
514
+ }
515
+ const data = JSON.parse(responseText);
516
+ if (data.data && Array.isArray(data.data) && data.data.length > 0) {
517
+ return {
518
+ access_token: data.data[0].access_token,
519
+ user_id: data.data[0].user_id,
520
+ permissions: data.data[0].permissions || ""
521
+ };
522
+ } else if (data.access_token) {
523
+ return {
524
+ access_token: data.access_token,
525
+ user_id: data.user_id,
526
+ permissions: data.permissions || ""
527
+ };
528
+ }
529
+ throw new Error("Unexpected token response format");
530
+ }
531
+ /**
532
+ * Exchange short-lived token for long-lived token
533
+ *
534
+ * Long-lived tokens are valid for 60 days and can be refreshed.
535
+ *
536
+ * @param params - Exchange parameters
537
+ * @returns Long-lived token response
538
+ * @throws Error if exchange fails
539
+ */
540
+ static async getLongLivedToken(params) {
541
+ const { clientSecret, accessToken } = params;
542
+ const queryParams = new URLSearchParams({
543
+ grant_type: "ig_exchange_token",
544
+ client_secret: clientSecret,
545
+ access_token: accessToken
546
+ });
547
+ const response = await fetch(
548
+ `${OAUTH_ENDPOINTS.LONG_LIVED_TOKEN}?${queryParams.toString()}`
549
+ );
550
+ const responseText = await response.text();
551
+ if (!response.ok) {
552
+ let error;
553
+ try {
554
+ error = JSON.parse(responseText);
555
+ } catch {
556
+ error = { error: { message: responseText } };
557
+ }
558
+ throw new Error(
559
+ error.error?.message || "Failed to exchange for long-lived token"
560
+ );
561
+ }
562
+ return JSON.parse(responseText);
563
+ }
564
+ /**
565
+ * Get the default scopes for Instagram Business Login
566
+ *
567
+ * @returns Array of commonly used scopes
568
+ */
569
+ static getDefaultScopes() {
570
+ return [
571
+ "instagram_business_basic",
572
+ "instagram_business_manage_messages",
573
+ "instagram_business_manage_comments",
574
+ "instagram_business_content_publish"
575
+ ];
576
+ }
577
+ /**
578
+ * Get all available Instagram Business Login scopes
579
+ *
580
+ * @returns Array of all available scopes
581
+ */
582
+ static getAllScopes() {
583
+ return [
584
+ "instagram_business_basic",
585
+ "instagram_business_manage_messages",
586
+ "instagram_business_manage_comments",
587
+ "instagram_business_content_publish",
588
+ "instagram_business_manage_insights"
589
+ ];
590
+ }
591
+ };
592
+
593
+ // src/types/common.ts
594
+ function formatFields(fields) {
595
+ if (!fields) return void 0;
596
+ if (Array.isArray(fields)) return fields.join(",");
597
+ return fields;
598
+ }
599
+
600
+ // src/api/users.ts
601
+ var UsersApi = class {
602
+ constructor(http, userId) {
603
+ this.http = http;
604
+ this.userId = userId;
605
+ }
606
+ /**
607
+ * Get user profile information
608
+ * @param options - Fields to retrieve
609
+ */
610
+ async getProfile(options) {
611
+ const fields = options?.fields?.join(",") || "id,username,account_type";
612
+ return this.http.get(USER_ENDPOINTS.PROFILE(this.userId), { fields });
613
+ }
614
+ /**
615
+ * Get user's media
616
+ * @param options - Pagination and fields options
617
+ */
618
+ async getMedia(options) {
619
+ return this.http.get(
620
+ USER_ENDPOINTS.MEDIA(this.userId),
621
+ {
622
+ fields: formatFields(options?.fields) || "id,caption,media_type,media_url,permalink,timestamp",
623
+ limit: options?.limit,
624
+ after: options?.after,
625
+ before: options?.before
626
+ }
627
+ );
628
+ }
629
+ /**
630
+ * Get user's stories
631
+ */
632
+ async getStories() {
633
+ return this.http.get(
634
+ USER_ENDPOINTS.STORIES(this.userId),
635
+ { fields: "id,media_type,media_url,timestamp" }
636
+ );
637
+ }
638
+ /**
639
+ * Get user's live media
640
+ */
641
+ async getLiveMedia() {
642
+ return this.http.get(
643
+ USER_ENDPOINTS.LIVE_MEDIA(this.userId),
644
+ { fields: "id,media_type,timestamp" }
645
+ );
646
+ }
647
+ /**
648
+ * Get content publishing limit (quota usage)
649
+ */
650
+ async getContentPublishingLimit() {
651
+ return this.http.get(
652
+ USER_ENDPOINTS.CONTENT_PUBLISHING_LIMIT(this.userId),
653
+ { fields: "quota_usage,config" }
654
+ );
655
+ }
656
+ /**
657
+ * Discover another business account by username
658
+ * @param options - Username and fields to retrieve
659
+ */
660
+ async getBusinessDiscovery(options) {
661
+ const fields = options.fields?.join(",") || "id,username,followers_count,media_count";
662
+ return this.http.get(
663
+ USER_ENDPOINTS.BUSINESS_DISCOVERY(this.userId),
664
+ {
665
+ fields: `business_discovery.username(${options.username}){${fields}}`
666
+ }
667
+ );
668
+ }
669
+ /**
670
+ * Get media where user is mentioned
671
+ */
672
+ async getMentionedMedia() {
673
+ return this.http.get(
674
+ USER_ENDPOINTS.MENTIONED_MEDIA(this.userId),
675
+ { fields: "id,caption,media_type,timestamp" }
676
+ );
677
+ }
678
+ /**
679
+ * Get comments where user is mentioned
680
+ */
681
+ async getMentionedComment() {
682
+ return this.http.get(
683
+ USER_ENDPOINTS.MENTIONED_COMMENT(this.userId),
684
+ { fields: "id,text,timestamp" }
685
+ );
686
+ }
687
+ /**
688
+ * Get media user is tagged in
689
+ */
690
+ async getTags() {
691
+ return this.http.get(
692
+ USER_ENDPOINTS.TAGS(this.userId),
693
+ { fields: "id,media_type,timestamp" }
694
+ );
695
+ }
696
+ /**
697
+ * Get recently searched hashtags
698
+ */
699
+ async getRecentlySearchedHashtags() {
700
+ return this.http.get(
701
+ USER_ENDPOINTS.RECENTLY_SEARCHED_HASHTAGS(this.userId)
702
+ );
703
+ }
704
+ /**
705
+ * Get available product catalogs
706
+ */
707
+ async getAvailableCatalogs() {
708
+ return this.http.get(
709
+ USER_ENDPOINTS.AVAILABLE_CATALOGS(this.userId)
710
+ );
711
+ }
712
+ /**
713
+ * Search products in a catalog
714
+ * @param catalogId - Catalog ID to search in
715
+ * @param query - Search query
716
+ */
717
+ async searchCatalogProducts(catalogId, query) {
718
+ return this.http.get(
719
+ USER_ENDPOINTS.CATALOG_PRODUCT_SEARCH(this.userId),
720
+ { catalog_id: catalogId, q: query }
721
+ );
722
+ }
723
+ };
724
+
725
+ // src/api/media.ts
726
+ var MediaApi = class {
727
+ constructor(http) {
728
+ this.http = http;
729
+ }
730
+ /**
731
+ * Get media by ID
732
+ * @param mediaId - Media ID
733
+ * @param options - Fields to retrieve
734
+ */
735
+ async get(mediaId, options) {
736
+ const fields = options?.fields?.join(",") || "id,caption,media_type,media_url,permalink,timestamp";
737
+ return this.http.get(MEDIA_ENDPOINTS.GET(mediaId), { fields });
738
+ }
739
+ /**
740
+ * Get carousel children
741
+ * @param mediaId - Carousel media ID
742
+ * @param options - Fields to retrieve
743
+ */
744
+ async getChildren(mediaId, options) {
745
+ const fields = formatFields(options?.fields) || "id,media_type,media_url";
746
+ return this.http.get(
747
+ MEDIA_ENDPOINTS.CHILDREN(mediaId),
748
+ { fields }
749
+ );
750
+ }
751
+ /**
752
+ * Get comments on media
753
+ * @param mediaId - Media ID
754
+ * @param options - Pagination and fields options
755
+ */
756
+ async getComments(mediaId, options) {
757
+ return this.http.get(
758
+ MEDIA_ENDPOINTS.COMMENTS(mediaId),
759
+ {
760
+ fields: formatFields(options?.fields) || "id,text,username,timestamp",
761
+ limit: options?.limit,
762
+ after: options?.after
763
+ }
764
+ );
765
+ }
766
+ /**
767
+ * Get media insights
768
+ * @param mediaId - Media ID
769
+ * @param options - Metrics to retrieve
770
+ */
771
+ async getInsights(mediaId, options) {
772
+ return this.http.get(
773
+ MEDIA_ENDPOINTS.INSIGHTS(mediaId),
774
+ { metric: options.metric.join(",") }
775
+ );
776
+ }
777
+ /**
778
+ * Get media collaborators
779
+ * @param mediaId - Media ID
780
+ */
781
+ async getCollaborators(mediaId) {
782
+ return this.http.get(
783
+ MEDIA_ENDPOINTS.COLLABORATORS(mediaId),
784
+ { fields: "id,username" }
785
+ );
786
+ }
787
+ /**
788
+ * Get product tags on media
789
+ * @param mediaId - Media ID
790
+ */
791
+ async getProductTags(mediaId) {
792
+ return this.http.get(
793
+ MEDIA_ENDPOINTS.PRODUCT_TAGS(mediaId)
794
+ );
795
+ }
796
+ };
797
+
798
+ // src/api/publishing.ts
799
+ var PublishingApi = class {
800
+ constructor(http, userId) {
801
+ this.http = http;
802
+ this.userId = userId;
803
+ }
804
+ /**
805
+ * Create an image container
806
+ * @param options - Image URL and optional caption, location, tags
807
+ */
808
+ async createImageContainer(options) {
809
+ return this.http.post(
810
+ PUBLISHING_ENDPOINTS.CREATE_CONTAINER(this.userId),
811
+ {
812
+ image_url: options.image_url,
813
+ caption: options.caption,
814
+ location_id: options.location_id,
815
+ user_tags: options.user_tags ? JSON.stringify(options.user_tags) : void 0,
816
+ collaborators: options.collaborators?.join(","),
817
+ alt_text: options.alt_text,
818
+ is_carousel_item: options.is_carousel_item
819
+ }
820
+ );
821
+ }
822
+ /**
823
+ * Create a video/reel/story container
824
+ * @param options - Video URL, media type, and optional settings
825
+ */
826
+ async createVideoContainer(options) {
827
+ return this.http.post(
828
+ PUBLISHING_ENDPOINTS.CREATE_CONTAINER(this.userId),
829
+ {
830
+ video_url: options.video_url,
831
+ media_type: options.media_type,
832
+ caption: options.caption,
833
+ location_id: options.location_id,
834
+ user_tags: options.user_tags ? JSON.stringify(options.user_tags) : void 0,
835
+ collaborators: options.collaborators?.join(","),
836
+ share_to_feed: options.share_to_feed,
837
+ is_carousel_item: options.is_carousel_item,
838
+ cover_url: options.cover_url,
839
+ thumb_offset: options.thumb_offset,
840
+ audio_name: options.audio_name
841
+ }
842
+ );
843
+ }
844
+ /**
845
+ * Create a carousel container
846
+ * @param options - Child container IDs and optional caption
847
+ */
848
+ async createCarouselContainer(options) {
849
+ return this.http.post(
850
+ PUBLISHING_ENDPOINTS.CREATE_CONTAINER(this.userId),
851
+ {
852
+ media_type: "CAROUSEL",
853
+ children: options.children.join(","),
854
+ caption: options.caption,
855
+ location_id: options.location_id,
856
+ collaborators: options.collaborators?.join(",")
857
+ }
858
+ );
859
+ }
860
+ /**
861
+ * Create a resumable upload session
862
+ * @param options - Media type and settings
863
+ */
864
+ async createResumableUpload(options) {
865
+ return this.http.post(
866
+ PUBLISHING_ENDPOINTS.CREATE_CONTAINER(this.userId),
867
+ {
868
+ media_type: options.media_type,
869
+ upload_type: "resumable",
870
+ caption: options.caption,
871
+ location_id: options.location_id,
872
+ collaborators: options.collaborators?.join(",")
873
+ }
874
+ );
875
+ }
876
+ /**
877
+ * Get container status
878
+ * @param containerId - Container ID
879
+ */
880
+ async getContainerStatus(containerId) {
881
+ return this.http.get(
882
+ PUBLISHING_ENDPOINTS.CONTAINER_STATUS(containerId),
883
+ { fields: "id,status_code,status" }
884
+ );
885
+ }
886
+ /**
887
+ * Publish a container
888
+ * @param containerId - Container ID to publish
889
+ */
890
+ async publishContainer(containerId) {
891
+ return this.http.post(
892
+ PUBLISHING_ENDPOINTS.PUBLISH(this.userId),
893
+ { creation_id: containerId }
894
+ );
895
+ }
896
+ /**
897
+ * Wait for container to be ready, then publish
898
+ * @param containerId - Container ID
899
+ * @param maxAttempts - Maximum number of status checks (default: 30)
900
+ * @param intervalMs - Interval between checks in ms (default: 2000)
901
+ */
902
+ async waitAndPublish(containerId, maxAttempts = 30, intervalMs = 2e3) {
903
+ for (let i = 0; i < maxAttempts; i++) {
904
+ const status = await this.getContainerStatus(containerId);
905
+ if (status.status_code === "FINISHED") {
906
+ return this.publishContainer(containerId);
907
+ }
908
+ if (status.status_code === "ERROR" || status.status_code === "EXPIRED") {
909
+ throw new Error(`Container failed with status: ${status.status_code}`);
910
+ }
911
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
912
+ }
913
+ throw new Error("Container did not become ready in time");
914
+ }
915
+ };
916
+
917
+ // src/api/messaging.ts
918
+ var MessagingApi = class {
919
+ constructor(http, userId) {
920
+ this.http = http;
921
+ this.userId = userId;
922
+ }
923
+ /**
924
+ * Send a text message
925
+ * @param recipientId - Instagram-scoped user ID (IGSID)
926
+ * @param text - Message text
927
+ * @param options - Optional settings (humanAgent for 7-day window)
928
+ */
929
+ async sendText(recipientId, text, options) {
930
+ const payload = {
931
+ recipient: { id: recipientId },
932
+ message: { text }
933
+ };
934
+ if (options?.humanAgent) {
935
+ payload.messaging_type = "MESSAGE_TAG";
936
+ payload.tag = "HUMAN_AGENT";
937
+ }
938
+ return this.http.post(
939
+ MESSAGING_ENDPOINTS.SEND(this.userId),
940
+ payload
941
+ );
942
+ }
943
+ /**
944
+ * Send a media message (image, video, audio)
945
+ * @param recipientId - Instagram-scoped user ID
946
+ * @param options - Media type and URL
947
+ */
948
+ async sendMedia(recipientId, options) {
949
+ return this.http.post(
950
+ MESSAGING_ENDPOINTS.SEND(this.userId),
951
+ {
952
+ recipient: { id: recipientId },
953
+ message: {
954
+ attachment: {
955
+ type: options.type,
956
+ payload: { url: options.url }
957
+ }
958
+ }
959
+ }
960
+ );
961
+ }
962
+ /**
963
+ * Send a sticker (like_heart)
964
+ * @param recipientId - Instagram-scoped user ID
965
+ */
966
+ async sendLikeHeart(recipientId) {
967
+ return this.http.post(
968
+ MESSAGING_ENDPOINTS.SEND(this.userId),
969
+ {
970
+ recipient: { id: recipientId },
971
+ message: {
972
+ attachment: {
973
+ type: "like_heart"
974
+ }
975
+ }
976
+ }
977
+ );
978
+ }
979
+ /**
980
+ * Send a generic template
981
+ * @param recipientId - Instagram-scoped user ID
982
+ * @param options - Template elements
983
+ */
984
+ async sendGenericTemplate(recipientId, options) {
985
+ return this.http.post(
986
+ MESSAGING_ENDPOINTS.SEND(this.userId),
987
+ {
988
+ recipient: { id: recipientId },
989
+ message: {
990
+ attachment: {
991
+ type: "template",
992
+ payload: {
993
+ template_type: "generic",
994
+ elements: options.elements
995
+ }
996
+ }
997
+ }
998
+ }
999
+ );
1000
+ }
1001
+ /**
1002
+ * Send a button template
1003
+ * @param recipientId - Instagram-scoped user ID
1004
+ * @param options - Text and buttons
1005
+ */
1006
+ async sendButtonTemplate(recipientId, options) {
1007
+ return this.http.post(
1008
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1009
+ {
1010
+ recipient: { id: recipientId },
1011
+ message: {
1012
+ attachment: {
1013
+ type: "template",
1014
+ payload: {
1015
+ template_type: "button",
1016
+ text: options.text,
1017
+ buttons: options.buttons
1018
+ }
1019
+ }
1020
+ }
1021
+ }
1022
+ );
1023
+ }
1024
+ /**
1025
+ * Send quick replies
1026
+ * @param recipientId - Instagram-scoped user ID
1027
+ * @param options - Text and quick reply options
1028
+ */
1029
+ async sendQuickReplies(recipientId, options) {
1030
+ return this.http.post(
1031
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1032
+ {
1033
+ recipient: { id: recipientId },
1034
+ message: {
1035
+ text: options.text,
1036
+ quick_replies: options.replies
1037
+ }
1038
+ }
1039
+ );
1040
+ }
1041
+ /**
1042
+ * Send a private reply to a comment
1043
+ * @param commentId - Comment ID to reply to
1044
+ * @param message - Message text
1045
+ */
1046
+ async sendPrivateReply(commentId, message) {
1047
+ return this.http.post(
1048
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1049
+ {
1050
+ recipient: { comment_id: commentId },
1051
+ message: { text: message }
1052
+ }
1053
+ );
1054
+ }
1055
+ /**
1056
+ * Share a published post via message
1057
+ * @param recipientId - Instagram-scoped user ID
1058
+ * @param mediaId - Media ID of the post to share
1059
+ */
1060
+ async sendMediaShare(recipientId, mediaId) {
1061
+ return this.http.post(
1062
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1063
+ {
1064
+ recipient: { id: recipientId },
1065
+ message: {
1066
+ attachment: {
1067
+ type: "MEDIA_SHARE",
1068
+ payload: { id: mediaId }
1069
+ }
1070
+ }
1071
+ }
1072
+ );
1073
+ }
1074
+ /**
1075
+ * React to a message
1076
+ * @param recipientId - Instagram-scoped user ID
1077
+ * @param messageId - Message ID to react to
1078
+ * @param reaction - Reaction type
1079
+ */
1080
+ async reactToMessage(recipientId, messageId, reaction) {
1081
+ return this.http.post(
1082
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1083
+ {
1084
+ recipient: { id: recipientId },
1085
+ sender_action: "react",
1086
+ payload: {
1087
+ message_id: messageId,
1088
+ reaction
1089
+ }
1090
+ }
1091
+ );
1092
+ }
1093
+ /**
1094
+ * Remove reaction from a message
1095
+ * @param recipientId - Instagram-scoped user ID
1096
+ * @param messageId - Message ID to unreact from
1097
+ */
1098
+ async unreactToMessage(recipientId, messageId) {
1099
+ return this.http.post(
1100
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1101
+ {
1102
+ recipient: { id: recipientId },
1103
+ sender_action: "unreact",
1104
+ payload: {
1105
+ message_id: messageId
1106
+ }
1107
+ }
1108
+ );
1109
+ }
1110
+ /**
1111
+ * Send typing indicator
1112
+ * @param recipientId - Instagram-scoped user ID
1113
+ * @param typing - Whether to show typing (true) or stop (false)
1114
+ */
1115
+ async sendTypingIndicator(recipientId, typing = true) {
1116
+ return this.http.post(
1117
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1118
+ {
1119
+ recipient: { id: recipientId },
1120
+ sender_action: typing ? "typing_on" : "typing_off"
1121
+ }
1122
+ );
1123
+ }
1124
+ };
1125
+
1126
+ // src/api/conversations.ts
1127
+ var ConversationsApi = class {
1128
+ constructor(http, userId) {
1129
+ this.http = http;
1130
+ this.userId = userId;
1131
+ }
1132
+ /**
1133
+ * Get list of conversations
1134
+ * @param options - Pagination and filter options
1135
+ */
1136
+ async list(options) {
1137
+ return this.http.get(
1138
+ MESSAGING_ENDPOINTS.CONVERSATIONS(this.userId),
1139
+ {
1140
+ platform: options?.platform || "instagram",
1141
+ user_id: options?.user_id,
1142
+ limit: options?.limit,
1143
+ after: options?.after,
1144
+ fields: "id,updated_time"
1145
+ }
1146
+ );
1147
+ }
1148
+ /**
1149
+ * Find conversation with a specific user
1150
+ * @param igsid - Instagram-scoped user ID
1151
+ */
1152
+ async findByUser(igsid) {
1153
+ return this.http.get(
1154
+ MESSAGING_ENDPOINTS.CONVERSATIONS(this.userId),
1155
+ {
1156
+ platform: "instagram",
1157
+ user_id: igsid,
1158
+ fields: "id,updated_time"
1159
+ }
1160
+ );
1161
+ }
1162
+ /**
1163
+ * Get messages in a conversation
1164
+ * @param conversationId - Conversation ID
1165
+ * @param fields - Fields to retrieve (default: from,to)
1166
+ */
1167
+ async getMessages(conversationId, fields = "messages{id,created_time,from,to,message}") {
1168
+ return this.http.get(
1169
+ MESSAGING_ENDPOINTS.CONVERSATION(conversationId),
1170
+ { fields }
1171
+ );
1172
+ }
1173
+ /**
1174
+ * Get a specific message
1175
+ * @param messageId - Message ID
1176
+ * @param fields - Fields to retrieve
1177
+ */
1178
+ async getMessage(messageId, fields = "id,created_time,from,to,message") {
1179
+ return this.http.get(
1180
+ MESSAGING_ENDPOINTS.MESSAGE(messageId),
1181
+ { fields }
1182
+ );
1183
+ }
1184
+ };
1185
+
1186
+ // src/api/comments.ts
1187
+ var CommentsApi = class {
1188
+ constructor(http) {
1189
+ this.http = http;
1190
+ }
1191
+ /**
1192
+ * Get comment by ID
1193
+ * @param commentId - Comment ID
1194
+ * @param fields - Fields to retrieve
1195
+ */
1196
+ async get(commentId, fields = ["id", "text", "username", "timestamp"]) {
1197
+ return this.http.get(
1198
+ COMMENT_ENDPOINTS.GET(commentId),
1199
+ { fields: fields.join(",") }
1200
+ );
1201
+ }
1202
+ /**
1203
+ * Get replies to a comment
1204
+ * @param commentId - Comment ID
1205
+ * @param options - Pagination and fields options
1206
+ */
1207
+ async getReplies(commentId, options) {
1208
+ return this.http.get(
1209
+ COMMENT_ENDPOINTS.REPLIES(commentId),
1210
+ {
1211
+ fields: options?.fields?.join(",") || "id,text,username,timestamp",
1212
+ limit: options?.limit,
1213
+ after: options?.after
1214
+ }
1215
+ );
1216
+ }
1217
+ /**
1218
+ * Reply to a comment
1219
+ * @param commentId - Comment ID to reply to
1220
+ * @param options - Reply message
1221
+ */
1222
+ async reply(commentId, options) {
1223
+ return this.http.post(
1224
+ COMMENT_ENDPOINTS.REPLY(commentId),
1225
+ { message: options.message }
1226
+ );
1227
+ }
1228
+ /**
1229
+ * Hide a comment
1230
+ * @param commentId - Comment ID
1231
+ */
1232
+ async hide(commentId) {
1233
+ return this.http.post(
1234
+ COMMENT_ENDPOINTS.UPDATE(commentId),
1235
+ { hide: true }
1236
+ );
1237
+ }
1238
+ /**
1239
+ * Unhide a comment
1240
+ * @param commentId - Comment ID
1241
+ */
1242
+ async unhide(commentId) {
1243
+ return this.http.post(
1244
+ COMMENT_ENDPOINTS.UPDATE(commentId),
1245
+ { hide: false }
1246
+ );
1247
+ }
1248
+ /**
1249
+ * Delete a comment
1250
+ * @param commentId - Comment ID
1251
+ */
1252
+ async delete(commentId) {
1253
+ return this.http.delete(COMMENT_ENDPOINTS.DELETE(commentId));
1254
+ }
1255
+ };
1256
+
1257
+ // src/api/hashtags.ts
1258
+ var HashtagsApi = class {
1259
+ constructor(http) {
1260
+ this.http = http;
1261
+ }
1262
+ /**
1263
+ * Search for a hashtag
1264
+ * @param options - User ID and search query
1265
+ */
1266
+ async search(options) {
1267
+ return this.http.get(
1268
+ HASHTAG_ENDPOINTS.SEARCH,
1269
+ {
1270
+ user_id: options.user_id,
1271
+ q: options.q
1272
+ }
1273
+ );
1274
+ }
1275
+ /**
1276
+ * Get hashtag information
1277
+ * @param hashtagId - Hashtag ID
1278
+ */
1279
+ async get(hashtagId) {
1280
+ return this.http.get(
1281
+ HASHTAG_ENDPOINTS.GET(hashtagId),
1282
+ { fields: "id,name" }
1283
+ );
1284
+ }
1285
+ /**
1286
+ * Get recent media with hashtag
1287
+ * @param hashtagId - Hashtag ID
1288
+ * @param options - User ID and pagination options
1289
+ */
1290
+ async getRecentMedia(hashtagId, options) {
1291
+ return this.http.get(
1292
+ HASHTAG_ENDPOINTS.RECENT_MEDIA(hashtagId),
1293
+ {
1294
+ user_id: options.user_id,
1295
+ fields: options.fields?.join(",") || "id,caption,media_type,permalink",
1296
+ limit: options.limit,
1297
+ after: options.after
1298
+ }
1299
+ );
1300
+ }
1301
+ /**
1302
+ * Get top media with hashtag
1303
+ * @param hashtagId - Hashtag ID
1304
+ * @param options - User ID and pagination options
1305
+ */
1306
+ async getTopMedia(hashtagId, options) {
1307
+ return this.http.get(
1308
+ HASHTAG_ENDPOINTS.TOP_MEDIA(hashtagId),
1309
+ {
1310
+ user_id: options.user_id,
1311
+ fields: options.fields?.join(",") || "id,caption,media_type,permalink",
1312
+ limit: options.limit,
1313
+ after: options.after
1314
+ }
1315
+ );
1316
+ }
1317
+ };
1318
+
1319
+ // src/api/insights.ts
1320
+ var InsightsApi = class {
1321
+ constructor(http, userId) {
1322
+ this.http = http;
1323
+ this.userId = userId;
1324
+ }
1325
+ /**
1326
+ * Get account insights
1327
+ * @param options - Metrics, period, and breakdown options
1328
+ */
1329
+ async getAccountInsights(options) {
1330
+ return this.http.get(
1331
+ USER_ENDPOINTS.INSIGHTS(this.userId),
1332
+ {
1333
+ metric: options.metric.join(","),
1334
+ period: options.period || "day",
1335
+ since: options.since,
1336
+ until: options.until,
1337
+ metric_type: options.metric_type,
1338
+ breakdown: options.breakdown
1339
+ }
1340
+ );
1341
+ }
1342
+ /**
1343
+ * Get media insights
1344
+ * @param mediaId - Media ID
1345
+ * @param options - Metrics to retrieve
1346
+ */
1347
+ async getMediaInsights(mediaId, options) {
1348
+ return this.http.get(
1349
+ MEDIA_ENDPOINTS.INSIGHTS(mediaId),
1350
+ { metric: options.metric.join(",") }
1351
+ );
1352
+ }
1353
+ };
1354
+
1355
+ // src/api/welcomeFlows.ts
1356
+ var WelcomeFlowsApi = class {
1357
+ constructor(http, userId) {
1358
+ this.http = http;
1359
+ this.userId = userId;
1360
+ }
1361
+ /**
1362
+ * Get all welcome message flows
1363
+ */
1364
+ async list() {
1365
+ return this.http.get(
1366
+ WELCOME_FLOW_ENDPOINTS.FLOWS(this.userId)
1367
+ );
1368
+ }
1369
+ /**
1370
+ * Get a specific welcome message flow
1371
+ * @param flowId - Flow ID
1372
+ */
1373
+ async get(flowId) {
1374
+ return this.http.get(
1375
+ WELCOME_FLOW_ENDPOINTS.FLOWS(this.userId),
1376
+ { flow_id: flowId }
1377
+ );
1378
+ }
1379
+ /**
1380
+ * Create a new welcome message flow
1381
+ * @param options - Flow name and screens
1382
+ */
1383
+ async create(options) {
1384
+ return this.http.post(
1385
+ WELCOME_FLOW_ENDPOINTS.FLOWS(this.userId),
1386
+ {
1387
+ name: options.name,
1388
+ screens: options.screens
1389
+ }
1390
+ );
1391
+ }
1392
+ /**
1393
+ * Update an existing welcome message flow
1394
+ * @param flowId - Flow ID
1395
+ * @param options - Updated flow name and screens
1396
+ */
1397
+ async update(flowId, options) {
1398
+ return this.http.post(
1399
+ WELCOME_FLOW_ENDPOINTS.FLOWS(this.userId),
1400
+ {
1401
+ flow_id: flowId,
1402
+ name: options.name,
1403
+ screens: options.screens
1404
+ }
1405
+ );
1406
+ }
1407
+ /**
1408
+ * Delete a welcome message flow
1409
+ * @param flowId - Flow ID
1410
+ */
1411
+ async delete(flowId) {
1412
+ return this.http.delete(
1413
+ WELCOME_FLOW_ENDPOINTS.FLOWS(this.userId),
1414
+ { flow_id: flowId }
1415
+ );
1416
+ }
1417
+ };
1418
+
1419
+ // src/api/messengerProfile.ts
1420
+ var MessengerProfileApi = class {
1421
+ constructor(http, userId) {
1422
+ this.http = http;
1423
+ this.userId = userId;
1424
+ }
1425
+ /**
1426
+ * Get current ice breakers
1427
+ */
1428
+ async getIceBreakers() {
1429
+ return this.http.get(
1430
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1431
+ { fields: "ice_breakers" }
1432
+ );
1433
+ }
1434
+ /**
1435
+ * Set ice breakers (FAQ questions)
1436
+ * @param iceBreakers - Ice breaker configurations (max 4 questions per locale)
1437
+ */
1438
+ async setIceBreakers(iceBreakers) {
1439
+ return this.http.post(
1440
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1441
+ {
1442
+ platform: "instagram",
1443
+ ice_breakers: iceBreakers
1444
+ }
1445
+ );
1446
+ }
1447
+ /**
1448
+ * Delete ice breakers
1449
+ */
1450
+ async deleteIceBreakers() {
1451
+ return this.http.delete(
1452
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1453
+ { fields: ["ice_breakers"] }
1454
+ );
1455
+ }
1456
+ /**
1457
+ * Get persistent menu
1458
+ */
1459
+ async getPersistentMenu() {
1460
+ return this.http.get(
1461
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1462
+ { fields: "persistent_menu" }
1463
+ );
1464
+ }
1465
+ /**
1466
+ * Set persistent menu
1467
+ * @param menus - Persistent menu configurations
1468
+ */
1469
+ async setPersistentMenu(menus) {
1470
+ return this.http.post(
1471
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1472
+ {
1473
+ platform: "instagram",
1474
+ persistent_menu: menus
1475
+ }
1476
+ );
1477
+ }
1478
+ /**
1479
+ * Delete persistent menu
1480
+ */
1481
+ async deletePersistentMenu() {
1482
+ return this.http.delete(
1483
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1484
+ { fields: ["persistent_menu"] }
1485
+ );
1486
+ }
1487
+ };
1488
+
1489
+ // src/api/oembed.ts
1490
+ var OEmbedApi = class {
1491
+ constructor(http) {
1492
+ this.http = http;
1493
+ }
1494
+ /**
1495
+ * Get oEmbed data for an Instagram URL
1496
+ * @param options - URL and optional formatting options
1497
+ */
1498
+ async get(options) {
1499
+ return this.http.get(
1500
+ OEMBED_ENDPOINTS.GET,
1501
+ {
1502
+ url: options.url,
1503
+ maxwidth: options.maxwidth,
1504
+ hidecaption: options.hidecaption,
1505
+ omitscript: options.omitscript
1506
+ }
1507
+ );
1508
+ }
1509
+ };
1510
+
1511
+ // src/api/webhooks.ts
1512
+ var WebhooksApi = class {
1513
+ constructor(http, userId) {
1514
+ this.http = http;
1515
+ this.userId = userId;
1516
+ }
1517
+ /**
1518
+ * Subscribe your app to specific fields for this user/page.
1519
+ *
1520
+ * @param fields List of fields to subscribe to (e.g., ['messages', 'comments'])
1521
+ */
1522
+ async subscribe(fields) {
1523
+ return this.http.post(`/${this.userId}/subscribed_apps`, {
1524
+ subscribed_fields: fields.join(",")
1525
+ });
1526
+ }
1527
+ /**
1528
+ * Unsubscribe from specific fields or all fields.
1529
+ *
1530
+ * @param fields Optional list of fields to unsubscribe from. If empty, unsubscribes from all.
1531
+ */
1532
+ async unsubscribe(fields) {
1533
+ if (fields && fields.length > 0) {
1534
+ throw new Error("Partial unsubscribe not directly supported by API. Use subscribe() with the fields you want to keep.");
1535
+ }
1536
+ return this.http.delete(`/${this.userId}/subscribed_apps`);
1537
+ }
1538
+ /**
1539
+ * Get the list of fields your app is currently subscribed to.
1540
+ */
1541
+ async getSubscribedFields() {
1542
+ return this.http.get(`/${this.userId}/subscribed_apps`);
1543
+ }
1544
+ };
1545
+
1546
+ // src/client.ts
1547
+ var DEFAULT_API_VERSION = "v22.0";
1548
+ var InstagramClient = class {
1549
+ /**
1550
+ * Create a new Instagram client
1551
+ * @param config - Client configuration
1552
+ */
1553
+ constructor(config) {
1554
+ this.cachedUserId = null;
1555
+ this.config = {
1556
+ accessToken: config.accessToken,
1557
+ apiVersion: config.apiVersion || DEFAULT_API_VERSION,
1558
+ timeout: config.timeout || 3e4
1559
+ };
1560
+ this.http = new HttpClient({
1561
+ accessToken: this.config.accessToken,
1562
+ apiVersion: this.config.apiVersion,
1563
+ timeout: this.config.timeout
1564
+ });
1565
+ const userId = "me";
1566
+ this.auth = new AuthApi(this.http);
1567
+ this.users = new UsersApi(this.http, userId);
1568
+ this.media = new MediaApi(this.http);
1569
+ this.publishing = new PublishingApi(this.http, userId);
1570
+ this.messaging = new MessagingApi(this.http, userId);
1571
+ this.conversations = new ConversationsApi(this.http, userId);
1572
+ this.comments = new CommentsApi(this.http);
1573
+ this.hashtags = new HashtagsApi(this.http);
1574
+ this.insights = new InsightsApi(this.http, userId);
1575
+ this.welcomeFlows = new WelcomeFlowsApi(this.http, userId);
1576
+ this.messengerProfile = new MessengerProfileApi(this.http, userId);
1577
+ this.oembed = new OEmbedApi(this.http);
1578
+ this.webhooks = new WebhooksApi(this.http, userId);
1579
+ }
1580
+ /**
1581
+ * Get the current user ID
1582
+ * Fetches from API if not cached
1583
+ */
1584
+ async getUserId() {
1585
+ if (this.cachedUserId) {
1586
+ return this.cachedUserId;
1587
+ }
1588
+ const user = await this.auth.me("id");
1589
+ this.cachedUserId = user.id;
1590
+ return user.id;
1591
+ }
1592
+ /**
1593
+ * Update the access token
1594
+ * @param accessToken - New access token
1595
+ */
1596
+ setAccessToken(accessToken) {
1597
+ this.http.setAccessToken(accessToken);
1598
+ }
1599
+ /**
1600
+ * Get the current configuration
1601
+ */
1602
+ getConfig() {
1603
+ return { ...this.config };
1604
+ }
1605
+ /**
1606
+ * Get the API version
1607
+ */
1608
+ getApiVersion() {
1609
+ return this.config.apiVersion;
1610
+ }
1611
+ };
1612
+ var InstagramWebhooks = class {
1613
+ /**
1614
+ * Verify that the webhook request came from Meta.
1615
+ * Calculates SHA256 HMAC of the raw body using the App Secret.
1616
+ *
1617
+ * @param body Raw request body as string
1618
+ * @param signature Signature from X-Hub-Signature-256 header (e.g., "sha256=...")
1619
+ * @param appSecret Your Instagram App Secret
1620
+ * @returns true if signature is valid
1621
+ */
1622
+ static verifySignature(body, signature, appSecret) {
1623
+ if (!signature || !signature.startsWith("sha256=")) {
1624
+ return false;
1625
+ }
1626
+ const expectedSignature = "sha256=" + crypto.createHmac("sha256", appSecret).update(body).digest("hex");
1627
+ return crypto.timingSafeEqual(
1628
+ Buffer.from(signature),
1629
+ Buffer.from(expectedSignature)
1630
+ );
1631
+ }
1632
+ /**
1633
+ * Type guard and parser for webhook payloads.
1634
+ *
1635
+ * @param body Parsed JSON body
1636
+ * @returns Typed WebhookPayload or throws error
1637
+ */
1638
+ static parsePayload(body) {
1639
+ if (body?.object !== "instagram" || !Array.isArray(body?.entry)) {
1640
+ throw new Error("Invalid Instagram webhook payload");
1641
+ }
1642
+ return body;
1643
+ }
1644
+ };
1645
+
1646
+ // src/types/user.ts
1647
+ var IG_USER_FIELDS = [
1648
+ "id",
1649
+ "account_type",
1650
+ "biography",
1651
+ "followers_count",
1652
+ "follows_count",
1653
+ "media_count",
1654
+ "username",
1655
+ "name",
1656
+ "profile_picture_url",
1657
+ "website",
1658
+ "ig_id"
1659
+ ];
1660
+
1661
+ // src/types/media.ts
1662
+ var IG_MEDIA_FIELDS = [
1663
+ "id",
1664
+ "caption",
1665
+ "media_type",
1666
+ "media_product_type",
1667
+ "media_url",
1668
+ "permalink",
1669
+ "thumbnail_url",
1670
+ "timestamp",
1671
+ "username",
1672
+ "like_count",
1673
+ "comments_count",
1674
+ "is_comment_enabled",
1675
+ "is_shared_to_feed",
1676
+ "owner",
1677
+ "shortcode"
1678
+ ];
1679
+
1680
+ // src/types/comment.ts
1681
+ var IG_COMMENT_FIELDS = [
1682
+ "id",
1683
+ "text",
1684
+ "username",
1685
+ "timestamp",
1686
+ "like_count",
1687
+ "hidden",
1688
+ "from",
1689
+ "media",
1690
+ "parent_id",
1691
+ "replies"
1692
+ ];
1693
+
1694
+ export { AUTH_ENDPOINTS, AuthApi, AuthenticationError, COMMENT_ENDPOINTS, CommentsApi, ConversationsApi, HASHTAG_ENDPOINTS, HashtagsApi, HttpClient, IG_COMMENT_FIELDS, IG_MEDIA_FIELDS, IG_USER_FIELDS, INSTAGRAM_BASE_URL, InsightsApi, InstagramAPIError, InstagramClient, InstagramOAuth, InstagramWebhooks, MEDIA_ENDPOINTS, MESSAGING_ENDPOINTS, MESSENGER_PROFILE_ENDPOINTS, MediaApi, MessagingApi, MessengerProfileApi, NetworkError, OAUTH_ENDPOINTS, OEMBED_ENDPOINTS, OEmbedApi, PUBLISHING_ENDPOINTS, PublishingApi, RateLimitError, USER_ENDPOINTS, UsersApi, ValidationError, WELCOME_FLOW_ENDPOINTS, WebhooksApi, WelcomeFlowsApi, buildUrl, formatFields, isAuthenticationError, isInstagramAPIError, isRateLimitError, isValidationError };
1695
+ //# sourceMappingURL=index.mjs.map
1696
+ //# sourceMappingURL=index.mjs.map