@nextsparkjs/plugin-social-media-publisher 0.1.0-beta.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.
@@ -0,0 +1,764 @@
1
+ # Provider APIs
2
+
3
+ ## Overview
4
+
5
+ The Social Media Publisher plugin provides two comprehensive API wrappers for interacting with Facebook Graph API and Instagram Business API. These wrappers abstract the complexity of API calls, handle authentication, and provide type-safe interfaces.
6
+
7
+ **Available Providers:**
8
+ - **FacebookAPI** - Facebook Pages publishing and insights
9
+ - **InstagramAPI** - Instagram Business publishing and analytics
10
+
11
+ ## FacebookAPI
12
+
13
+ ### Import
14
+
15
+ ```typescript
16
+ import { FacebookAPI } from '@/contents/plugins/social-media-publisher/lib/providers/facebook'
17
+ ```
18
+
19
+ ### Publishing Methods
20
+
21
+ #### `publishTextPost()`
22
+
23
+ Publish text-only post to Facebook Page.
24
+
25
+ **Signature:**
26
+ ```typescript
27
+ static async publishTextPost(options: {
28
+ pageId: string
29
+ pageAccessToken: string
30
+ message: string
31
+ }): Promise<FacebookPublishResult>
32
+ ```
33
+
34
+ **Parameters:**
35
+ - `pageId` - Facebook Page ID
36
+ - `pageAccessToken` - Page access token (decrypted)
37
+ - `message` - Post text content
38
+
39
+ **Returns:**
40
+ ```typescript
41
+ {
42
+ success: boolean
43
+ postId?: string // e.g., "123456789_987654321"
44
+ postUrl?: string // e.g., "https://www.facebook.com/123456789/posts/987654321"
45
+ error?: string
46
+ errorDetails?: unknown
47
+ }
48
+ ```
49
+
50
+ **Example:**
51
+ ```typescript
52
+ const result = await FacebookAPI.publishTextPost({
53
+ pageId: '123456789',
54
+ pageAccessToken: decryptedToken,
55
+ message: 'Hello from our Facebook Page! 👋'
56
+ })
57
+
58
+ if (result.success) {
59
+ console.log(`Published: ${result.postUrl}`)
60
+ } else {
61
+ console.error(`Failed: ${result.error}`)
62
+ }
63
+ ```
64
+
65
+ **Graph API Call:**
66
+ ```
67
+ POST https://graph.facebook.com/v18.0/{PAGE_ID}/feed
68
+ {
69
+ "message": "Post text here",
70
+ "access_token": "{PAGE_TOKEN}"
71
+ }
72
+ ```
73
+
74
+ #### `publishPhotoPost()`
75
+
76
+ Publish photo with optional caption to Facebook Page.
77
+
78
+ **Signature:**
79
+ ```typescript
80
+ static async publishPhotoPost(options: {
81
+ pageId: string
82
+ pageAccessToken: string
83
+ message: string
84
+ imageUrl: string
85
+ }): Promise<FacebookPublishResult>
86
+ ```
87
+
88
+ **Parameters:**
89
+ - `pageId` - Facebook Page ID
90
+ - `pageAccessToken` - Page access token
91
+ - `message` - Photo caption
92
+ - `imageUrl` - Public HTTPS URL to image
93
+
94
+ **Example:**
95
+ ```typescript
96
+ const result = await FacebookAPI.publishPhotoPost({
97
+ pageId: '123456789',
98
+ pageAccessToken: decryptedToken,
99
+ message: 'Check out our new product!',
100
+ imageUrl: 'https://cdn.example.com/product.jpg'
101
+ })
102
+ ```
103
+
104
+ **Graph API Call:**
105
+ ```
106
+ POST https://graph.facebook.com/v18.0/{PAGE_ID}/photos
107
+ {
108
+ "url": "https://example.com/image.jpg",
109
+ "message": "Photo caption",
110
+ "access_token": "{PAGE_TOKEN}"
111
+ }
112
+ ```
113
+
114
+ **Image Requirements:**
115
+ - Format: JPG, PNG, GIF, BMP
116
+ - Max size: 4MB (recommended), up to 15MB
117
+ - Must be publicly accessible via HTTPS
118
+
119
+ #### `publishLinkPost()`
120
+
121
+ Publish link with preview to Facebook Page.
122
+
123
+ **Signature:**
124
+ ```typescript
125
+ static async publishLinkPost(options: {
126
+ pageId: string
127
+ pageAccessToken: string
128
+ message: string
129
+ link: string
130
+ }): Promise<FacebookPublishResult>
131
+ ```
132
+
133
+ **Example:**
134
+ ```typescript
135
+ const result = await FacebookAPI.publishLinkPost({
136
+ pageId: '123456789',
137
+ pageAccessToken: decryptedToken,
138
+ message: 'Read our latest blog post!',
139
+ link: 'https://example.com/blog/post'
140
+ })
141
+ ```
142
+
143
+ **Graph API Call:**
144
+ ```
145
+ POST https://graph.facebook.com/v18.0/{PAGE_ID}/feed
146
+ {
147
+ "message": "Check this out!",
148
+ "link": "https://example.com/article",
149
+ "access_token": "{PAGE_TOKEN}"
150
+ }
151
+ ```
152
+
153
+ **Link Requirements:**
154
+ - Must use HTTPS
155
+ - Facebook auto-generates preview using Open Graph tags
156
+ - Preview customization via meta tags:
157
+ ```html
158
+ <meta property="og:title" content="Article Title" />
159
+ <meta property="og:description" content="Description" />
160
+ <meta property="og:image" content="https://example.com/preview.jpg" />
161
+ ```
162
+
163
+ ### Page Management Methods
164
+
165
+ #### `getUserPages()`
166
+
167
+ Get list of Facebook Pages user manages.
168
+
169
+ **Signature:**
170
+ ```typescript
171
+ static async getUserPages(userAccessToken: string): Promise<FacebookPageInfo[]>
172
+ ```
173
+
174
+ **Returns:**
175
+ ```typescript
176
+ interface FacebookPageInfo {
177
+ id: string // Page ID
178
+ name: string // Page name
179
+ category: string // Page category
180
+ accessToken: string // Page-specific access token
181
+ tasks: string[] // Permissions granted
182
+ pictureUrl?: string // Profile picture URL
183
+ }
184
+ ```
185
+
186
+ **Example:**
187
+ ```typescript
188
+ const pages = await FacebookAPI.getUserPages(userAccessToken)
189
+
190
+ pages.forEach(page => {
191
+ console.log(`${page.name} (${page.id})`)
192
+ console.log(`Permissions: ${page.tasks.join(', ')}`)
193
+ })
194
+ ```
195
+
196
+ **Graph API Call:**
197
+ ```
198
+ GET https://graph.facebook.com/v18.0/me/accounts
199
+ ?fields=id,name,category,access_token,tasks,picture
200
+ &access_token={USER_TOKEN}
201
+ ```
202
+
203
+ #### `getPageInfo()`
204
+
205
+ Get detailed information about Facebook Page.
206
+
207
+ **Signature:**
208
+ ```typescript
209
+ static async getPageInfo(
210
+ pageId: string,
211
+ pageAccessToken: string
212
+ ): Promise<FacebookPageStats>
213
+ ```
214
+
215
+ **Returns:**
216
+ ```typescript
217
+ interface FacebookPageStats {
218
+ id: string
219
+ name: string
220
+ fanCount: number // Follower count
221
+ about?: string
222
+ category?: string
223
+ profilePictureUrl?: string
224
+ coverPhotoUrl?: string
225
+ link?: string // Page URL
226
+ }
227
+ ```
228
+
229
+ **Example:**
230
+ ```typescript
231
+ const pageInfo = await FacebookAPI.getPageInfo(pageId, pageAccessToken)
232
+
233
+ console.log(`${pageInfo.name}`)
234
+ console.log(`Followers: ${pageInfo.fanCount.toLocaleString()}`)
235
+ console.log(`About: ${pageInfo.about}`)
236
+ ```
237
+
238
+ **Graph API Call:**
239
+ ```
240
+ GET https://graph.facebook.com/v18.0/{PAGE_ID}
241
+ ?fields=id,name,fan_count,about,category,picture,cover,link
242
+ &access_token={PAGE_TOKEN}
243
+ ```
244
+
245
+ #### `getPageInsights()`
246
+
247
+ Get analytics for Facebook Page.
248
+
249
+ **Signature:**
250
+ ```typescript
251
+ static async getPageInsights(
252
+ pageId: string,
253
+ pageAccessToken: string
254
+ ): Promise<FacebookInsights>
255
+ ```
256
+
257
+ **Returns:**
258
+ ```typescript
259
+ interface FacebookInsights {
260
+ impressions: number // Total impressions
261
+ reach: number // Total reach
262
+ engagement: number // Total engagement
263
+ reactions: number // Total reactions
264
+ comments: number // Total comments
265
+ shares: number // Total shares
266
+ }
267
+ ```
268
+
269
+ **Example:**
270
+ ```typescript
271
+ const insights = await FacebookAPI.getPageInsights(pageId, pageAccessToken)
272
+
273
+ console.log(`Impressions: ${insights.impressions}`)
274
+ console.log(`Engagement Rate: ${(insights.engagement / insights.reach * 100).toFixed(2)}%`)
275
+ ```
276
+
277
+ **Note:** Requires `read_insights` permission.
278
+
279
+ ### Instagram Integration Methods
280
+
281
+ #### `getInstagramBusinessAccount()`
282
+
283
+ Get Instagram Business Account linked to Facebook Page.
284
+
285
+ **Signature:**
286
+ ```typescript
287
+ static async getInstagramBusinessAccount(
288
+ pageId: string,
289
+ pageAccessToken: string
290
+ ): Promise<{
291
+ id: string
292
+ username: string
293
+ name?: string
294
+ profilePictureUrl?: string
295
+ followersCount?: number
296
+ followsCount?: number
297
+ mediaCount?: number
298
+ biography?: string
299
+ website?: string
300
+ } | null>
301
+ ```
302
+
303
+ **Returns:** Instagram account info or `null` if not connected
304
+
305
+ **Example:**
306
+ ```typescript
307
+ const igAccount = await FacebookAPI.getInstagramBusinessAccount(
308
+ pageId,
309
+ pageAccessToken
310
+ )
311
+
312
+ if (igAccount) {
313
+ console.log(`Instagram: @${igAccount.username}`)
314
+ console.log(`Followers: ${igAccount.followersCount}`)
315
+ } else {
316
+ console.log('No Instagram account connected to this Page')
317
+ }
318
+ ```
319
+
320
+ **Graph API Call:**
321
+ ```
322
+ GET https://graph.facebook.com/v18.0/{PAGE_ID}
323
+ ?fields=instagram_business_account{id,username,name,profile_picture_url,followers_count,follows_count,media_count,biography,website}
324
+ &access_token={PAGE_TOKEN}
325
+ ```
326
+
327
+ #### `validatePagePermissions()`
328
+
329
+ Check which permissions are granted for a Page.
330
+
331
+ **Signature:**
332
+ ```typescript
333
+ static async validatePagePermissions(
334
+ pageId: string,
335
+ pageAccessToken: string
336
+ ): Promise<{
337
+ valid: boolean
338
+ permissions: string[]
339
+ missing: string[]
340
+ }>
341
+ ```
342
+
343
+ **Example:**
344
+ ```typescript
345
+ const validation = await FacebookAPI.validatePagePermissions(
346
+ pageId,
347
+ pageAccessToken
348
+ )
349
+
350
+ console.log('Granted:', validation.permissions)
351
+ console.log('Missing:', validation.missing)
352
+
353
+ if (validation.missing.includes('pages_manage_posts')) {
354
+ console.warn('Cannot publish - missing pages_manage_posts permission')
355
+ }
356
+ ```
357
+
358
+ ## InstagramAPI
359
+
360
+ ### Import
361
+
362
+ ```typescript
363
+ import { InstagramAPI } from '@/contents/plugins/social-media-publisher/lib/providers/instagram'
364
+ ```
365
+
366
+ ### Publishing Methods
367
+
368
+ #### `publishPhoto()`
369
+
370
+ Publish photo to Instagram Business Account.
371
+
372
+ **Signature:**
373
+ ```typescript
374
+ static async publishPhoto(options: {
375
+ igAccountId: string
376
+ accessToken: string
377
+ imageUrl: string
378
+ caption?: string
379
+ }): Promise<InstagramPublishResult>
380
+ ```
381
+
382
+ **Parameters:**
383
+ - `igAccountId` - Instagram Business Account ID
384
+ - `accessToken` - Page access token (for linked Page)
385
+ - `imageUrl` - Public HTTPS URL to image
386
+ - `caption` - Optional caption (max 2,200 chars)
387
+
388
+ **Returns:**
389
+ ```typescript
390
+ {
391
+ success: boolean
392
+ postId?: string // e.g., "17899618652010220"
393
+ postUrl?: string // e.g., "https://www.instagram.com/p/ABC123"
394
+ error?: string
395
+ errorDetails?: unknown
396
+ }
397
+ ```
398
+
399
+ **Example:**
400
+ ```typescript
401
+ const result = await InstagramAPI.publishPhoto({
402
+ igAccountId: '17841401234567890',
403
+ accessToken: decryptedToken,
404
+ imageUrl: 'https://cdn.example.com/photo.jpg',
405
+ caption: 'My awesome Instagram post! 📸 #photography'
406
+ })
407
+
408
+ if (result.success) {
409
+ console.log(`Posted: ${result.postUrl}`)
410
+ }
411
+ ```
412
+
413
+ **Process (2-Step Container):**
414
+ 1. **Create Media Container:**
415
+ ```
416
+ POST https://graph.facebook.com/v18.0/{IG_ACCOUNT_ID}/media
417
+ {
418
+ "image_url": "https://example.com/image.jpg",
419
+ "caption": "Caption here",
420
+ "access_token": "{TOKEN}"
421
+ }
422
+ ```
423
+
424
+ 2. **Publish Container:**
425
+ ```
426
+ POST https://graph.facebook.com/v18.0/{IG_ACCOUNT_ID}/media_publish
427
+ {
428
+ "creation_id": "{CONTAINER_ID}",
429
+ "access_token": "{TOKEN}"
430
+ }
431
+ ```
432
+
433
+ **Image Requirements:**
434
+ - Format: JPG or PNG
435
+ - Size: Max 8MB
436
+ - Dimensions: 320px to 1080px (width)
437
+ - Aspect Ratio: 4:5 to 1.91:1
438
+ - Must be publicly accessible via HTTPS
439
+
440
+ **Caption Requirements:**
441
+ - Max length: 2,200 characters
442
+ - Max hashtags: 30
443
+ - Max mentions: 20 (@username)
444
+ - Supports emojis and line breaks
445
+
446
+ #### `publishVideo()`
447
+
448
+ Publish video to Instagram Business Account.
449
+
450
+ **Signature:**
451
+ ```typescript
452
+ static async publishVideo(options: {
453
+ igAccountId: string
454
+ accessToken: string
455
+ videoUrl: string
456
+ caption?: string
457
+ }): Promise<InstagramPublishResult>
458
+ ```
459
+
460
+ **Example:**
461
+ ```typescript
462
+ const result = await InstagramAPI.publishVideo({
463
+ igAccountId: '17841401234567890',
464
+ accessToken: decryptedToken,
465
+ videoUrl: 'https://cdn.example.com/video.mp4',
466
+ caption: 'Watch this! 🎬 #video'
467
+ })
468
+ ```
469
+
470
+ **Video Requirements:**
471
+ - Format: MP4 or MOV
472
+ - Size: Max 100MB
473
+ - Duration: 3 to 60 seconds
474
+ - Dimensions: Min 600px (any dimension)
475
+ - Aspect Ratio: 4:5 to 1.91:1
476
+ - Frame Rate: Max 30fps
477
+
478
+ **Processing Time:**
479
+ - Videos require processing before publishing
480
+ - Method waits up to 30 seconds for `FINISHED` status
481
+ - Polls every 2 seconds for completion
482
+
483
+ ### Account Information Methods
484
+
485
+ #### `getAccountInfo()`
486
+
487
+ Get Instagram Business Account information.
488
+
489
+ **Signature:**
490
+ ```typescript
491
+ static async getAccountInfo(
492
+ igAccountId: string,
493
+ accessToken: string
494
+ ): Promise<InstagramAccountInfo>
495
+ ```
496
+
497
+ **Returns:**
498
+ ```typescript
499
+ interface InstagramAccountInfo {
500
+ id: string
501
+ username: string
502
+ accountType?: string
503
+ profilePictureUrl?: string
504
+ followersCount?: number
505
+ followsCount?: number
506
+ mediaCount?: number
507
+ }
508
+ ```
509
+
510
+ **Example:**
511
+ ```typescript
512
+ const info = await InstagramAPI.getAccountInfo(igAccountId, accessToken)
513
+
514
+ console.log(`@${info.username}`)
515
+ console.log(`Followers: ${info.followersCount?.toLocaleString()}`)
516
+ console.log(`Posts: ${info.mediaCount}`)
517
+ ```
518
+
519
+ **Graph API Call:**
520
+ ```
521
+ GET https://graph.facebook.com/v18.0/{IG_ACCOUNT_ID}
522
+ ?fields=id,username,account_type,profile_picture_url,followers_count,follows_count,media_count
523
+ &access_token={TOKEN}
524
+ ```
525
+
526
+ #### `getAccountInsights()`
527
+
528
+ Get Instagram account-level insights.
529
+
530
+ **Signature:**
531
+ ```typescript
532
+ static async getAccountInsights(
533
+ igAccountId: string,
534
+ accessToken: string
535
+ ): Promise<InstagramInsights>
536
+ ```
537
+
538
+ **Returns:**
539
+ ```typescript
540
+ interface InstagramInsights {
541
+ impressions: number
542
+ reach: number
543
+ engagement: number
544
+ likes: number
545
+ comments: number
546
+ saves: number
547
+ profileViews: number
548
+ }
549
+ ```
550
+
551
+ **Example:**
552
+ ```typescript
553
+ const insights = await InstagramAPI.getAccountInsights(igAccountId, accessToken)
554
+
555
+ console.log(`Total Impressions: ${insights.impressions}`)
556
+ console.log(`Engagement Rate: ${(insights.engagement / insights.reach * 100).toFixed(2)}%`)
557
+ ```
558
+
559
+ **Note:** Requires `instagram_manage_insights` permission.
560
+
561
+ #### `getMediaInsights()`
562
+
563
+ Get insights for specific Instagram post.
564
+
565
+ **Signature:**
566
+ ```typescript
567
+ static async getMediaInsights(
568
+ mediaId: string,
569
+ accessToken: string
570
+ ): Promise<Partial<InstagramInsights>>
571
+ ```
572
+
573
+ **Example:**
574
+ ```typescript
575
+ const postInsights = await InstagramAPI.getMediaInsights(
576
+ '17899618652010220',
577
+ accessToken
578
+ )
579
+
580
+ console.log(`Likes: ${postInsights.likes}`)
581
+ console.log(`Comments: ${postInsights.comments}`)
582
+ console.log(`Saves: ${postInsights.saves}`)
583
+ ```
584
+
585
+ **Graph API Call:**
586
+ ```
587
+ GET https://graph.facebook.com/v18.0/{MEDIA_ID}/insights
588
+ ?metric=impressions,reach,engagement,likes,comments,saves
589
+ &access_token={TOKEN}
590
+ ```
591
+
592
+ ## Error Handling
593
+
594
+ ### Common Error Types
595
+
596
+ **Invalid Token:**
597
+ ```typescript
598
+ try {
599
+ await FacebookAPI.publishTextPost({ ... })
600
+ } catch (error) {
601
+ if (error.message.includes('Invalid OAuth')) {
602
+ // Token expired or revoked - need to reconnect
603
+ console.error('Token invalid - user must reconnect account')
604
+ }
605
+ }
606
+ ```
607
+
608
+ **Rate Limiting:**
609
+ ```typescript
610
+ try {
611
+ await InstagramAPI.publishPhoto({ ... })
612
+ } catch (error) {
613
+ if (error.message.includes('rate limit')) {
614
+ // Too many requests - wait and retry
615
+ console.error('Rate limit reached - retry later')
616
+ }
617
+ }
618
+ ```
619
+
620
+ **Permission Errors:**
621
+ ```typescript
622
+ try {
623
+ await FacebookAPI.getPageInsights({ ... })
624
+ } catch (error) {
625
+ if (error.message.includes('read_insights')) {
626
+ // Missing required permission
627
+ console.error('User needs to grant read_insights permission')
628
+ }
629
+ }
630
+ ```
631
+
632
+ **Image Not Accessible:**
633
+ ```typescript
634
+ try {
635
+ await InstagramAPI.publishPhoto({ ... })
636
+ } catch (error) {
637
+ if (error.message.includes('publicly accessible')) {
638
+ // Image URL not reachable
639
+ console.error('Image must be publicly accessible via HTTPS')
640
+ }
641
+ }
642
+ ```
643
+
644
+ ### Error Handling Pattern
645
+
646
+ ```typescript
647
+ async function safePublish(options: PublishOptions): Promise<PublishResult> {
648
+ try {
649
+ const result = await InstagramAPI.publishPhoto(options)
650
+
651
+ if (!result.success) {
652
+ // Log failure
653
+ await logAuditEvent('post_failed', {
654
+ error: result.error,
655
+ errorDetails: result.errorDetails
656
+ })
657
+
658
+ return { success: false, error: result.error }
659
+ }
660
+
661
+ // Log success
662
+ await logAuditEvent('post_published', {
663
+ postId: result.postId,
664
+ postUrl: result.postUrl
665
+ })
666
+
667
+ return result
668
+
669
+ } catch (error) {
670
+ // Unexpected error
671
+ console.error('Unexpected publish error:', error)
672
+
673
+ await logAuditEvent('post_failed', {
674
+ error: 'Unexpected error',
675
+ details: error instanceof Error ? error.message : String(error)
676
+ })
677
+
678
+ return { success: false, error: 'Unexpected error occurred' }
679
+ }
680
+ }
681
+ ```
682
+
683
+ ## Usage Examples
684
+
685
+ ### Batch Publishing
686
+
687
+ ```typescript
688
+ async function publishToMultiplePlatforms(
689
+ accounts: SocialAccount[],
690
+ imageUrl: string,
691
+ caption: string
692
+ ) {
693
+ const results = await Promise.allSettled(
694
+ accounts.map(async (account) => {
695
+ const [encrypted, iv, keyId] = account.accessToken.split(':')
696
+ const token = await TokenEncryption.decrypt(encrypted, iv, keyId)
697
+
698
+ if (account.platform === 'instagram_business') {
699
+ return await InstagramAPI.publishPhoto({
700
+ igAccountId: account.platformAccountId,
701
+ accessToken: token,
702
+ imageUrl,
703
+ caption
704
+ })
705
+ } else {
706
+ return await FacebookAPI.publishPhotoPost({
707
+ pageId: account.platformAccountId,
708
+ pageAccessToken: token,
709
+ message: caption,
710
+ imageUrl
711
+ })
712
+ }
713
+ })
714
+ )
715
+
716
+ return {
717
+ successful: results.filter(r => r.status === 'fulfilled').length,
718
+ failed: results.filter(r => r.status === 'rejected').length,
719
+ results
720
+ }
721
+ }
722
+ ```
723
+
724
+ ### Account Sync
725
+
726
+ ```typescript
727
+ async function syncAccountStats(accountId: string) {
728
+ const account = await getAccountFromDB(accountId)
729
+ const [encrypted, iv, keyId] = account.accessToken.split(':')
730
+ const token = await TokenEncryption.decrypt(encrypted, iv, keyId)
731
+
732
+ if (account.platform === 'instagram_business') {
733
+ const info = await InstagramAPI.getAccountInfo(
734
+ account.platformAccountId,
735
+ token
736
+ )
737
+
738
+ await updateAccountMetadata(accountId, {
739
+ followersCount: info.followersCount,
740
+ mediaCount: info.mediaCount,
741
+ profilePictureUrl: info.profilePictureUrl,
742
+ lastSyncAt: new Date().toISOString()
743
+ })
744
+ } else {
745
+ const info = await FacebookAPI.getPageInfo(
746
+ account.platformAccountId,
747
+ token
748
+ )
749
+
750
+ await updateAccountMetadata(accountId, {
751
+ fanCount: info.fanCount,
752
+ about: info.about,
753
+ profilePictureUrl: info.profilePictureUrl,
754
+ lastSyncAt: new Date().toISOString()
755
+ })
756
+ }
757
+ }
758
+ ```
759
+
760
+ ## Next Steps
761
+
762
+ - **[Custom Integrations](./02-custom-integrations.md)** - Build custom features with providers
763
+ - **[Publishing](../02-core-features/02-publishing.md)** - Use providers in publishing endpoint
764
+ - **[Token Management](../02-core-features/03-token-management.md)** - Secure token handling