@back23/promptly-sdk 2.11.0 → 2.13.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.
Files changed (2) hide show
  1. package/README.md +206 -850
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @back23/promptly-sdk
2
2
 
3
- Promptly AI CMS SDK for JavaScript/TypeScript
3
+ Official SDK for Promptly AI CMS - A headless CMS with built-in AI capabilities.
4
4
 
5
5
  ## Installation
6
6
 
@@ -14,608 +14,234 @@ npm install @back23/promptly-sdk
14
14
  import { Promptly } from '@back23/promptly-sdk';
15
15
 
16
16
  const client = new Promptly({
17
- tenantId: 'demo',
18
- apiKey: 'pky_your_api_key_here', // Required - Get from Dashboard > Settings > API Tokens
17
+ tenantId: 'your-tenant-id',
18
+ apiKey: 'pky_xxxxxxxxxxxxxxxx', // Required - Get from Dashboard > Settings > API Tokens
19
19
  baseUrl: 'https://promptly.webbyon.com', // Optional
20
20
  });
21
21
 
22
- // All API calls require API key
23
- const { data: posts } = await client.blog.list();
24
- const products = await client.shop.listProducts();
25
- ```
26
-
27
- ## v2.5.0 Changes
22
+ // Fetch blog posts
23
+ const { data: posts, meta } = await client.blog.list();
28
24
 
29
- ### BoardPost Type Extension
30
-
31
- Added `is_secret` and `is_mine` fields to posts.
32
-
33
- ```typescript
34
- interface BoardPost {
35
- // ... existing fields
36
- is_notice: boolean; // Whether it's a notice
37
- is_secret: boolean; // Whether it's a secret post
38
- is_mine: boolean; // Whether current logged-in user is the author
39
- }
25
+ // Fetch products
26
+ const { data: products } = await client.shop.listProducts();
40
27
  ```
41
28
 
42
- ---
43
-
44
- ## v2.3.1 Changes (Bug Fix)
45
-
46
- ### Dual Authentication Support Fixed
29
+ ## Authentication
47
30
 
48
- Fixed issue where API Key and Bearer Token are now sent simultaneously. In previous versions, when API Key was set, the Authorization header was ignored, causing member-only API calls to fail after login.
31
+ All API requests require an API key. Get yours from **Dashboard > Settings > API Tokens**.
49
32
 
50
33
  ```typescript
51
- // API Key + Bearer Token now work together
52
34
  const client = new Promptly({
53
35
  tenantId: 'demo',
54
- apiKey: 'pky_xxx', // X-API-Key header
36
+ apiKey: 'pky_xxxxxxxxxxxxxxxx', // Required
55
37
  });
56
-
57
- // Set token after login
58
- await client.auth.login({ email, password });
59
-
60
- // Member-only APIs work correctly (Authorization: Bearer xxx header included)
61
- const profile = await client.members.getProfile();
62
- const orders = await client.orders.list();
63
38
  ```
64
39
 
65
- ---
66
-
67
- ## v2.3.0 Changes
68
-
69
- ### Polymorphic Comments API
70
-
71
- The comment system has been redesigned to support various use cases including boards, blogs, and guestbooks (standalone pages).
40
+ For member-specific operations (orders, profile, etc.), authenticate first:
72
41
 
73
42
  ```typescript
74
- // Board post comments
75
- const boardComments = await client.comments.boardPost(postId);
76
- await client.comments.createBoardPost(postId, {
77
- author_name: 'John Doe',
78
- content: 'Comment content',
79
- password: '1234', // For guest comments
80
- });
43
+ // Login
44
+ await client.auth.login({ email: 'user@example.com', password: 'password' });
81
45
 
82
- // Blog post comments
83
- const blogComments = await client.comments.blogPost('post-slug');
84
- await client.comments.createBlogPost('post-slug', {
85
- author_name: 'Jane',
86
- content: 'Great post!',
87
- });
46
+ // Now member-only APIs work
47
+ const profile = await client.members.getProfile();
48
+ const orders = await client.orders.list();
88
49
 
89
- // Guestbook (standalone page)
90
- const guestbook = await client.comments.standalone('guestbook');
91
- await client.comments.createStandalone('guestbook', {
92
- author_name: 'Visitor',
93
- content: 'Hello!',
94
- });
50
+ // Check auth status
51
+ client.isAuthenticated(); // true
95
52
 
96
- // Common features
97
- await client.comments.update(commentId, { content: 'Updated comment' });
98
- await client.comments.delete(commentId, { password: '1234' });
99
- await client.comments.like(commentId);
53
+ // Logout
54
+ await client.auth.logout();
100
55
  ```
101
56
 
102
- ---
103
-
104
- ## v2.2.0 Changes
105
-
106
- ### Entity Definition CRUD
57
+ ## API Reference
107
58
 
108
- You can now create/update/delete custom entity definitions directly from the API/SDK.
59
+ ### Blog
109
60
 
110
61
  ```typescript
111
- // Create entity definition
112
- const entity = await client.entities.create({
113
- name: 'Customer',
114
- slug: 'customers', // optional, auto-generated from name
115
- description: 'Customer management',
116
- schema: {
117
- fields: [
118
- { name: 'company', label: 'Company Name', type: 'text', required: true },
119
- { name: 'email', label: 'Email', type: 'email', required: true },
120
- { name: 'status', label: 'Status', type: 'select', options: [
121
- { value: 'active', label: 'Active' },
122
- { value: 'inactive', label: 'Inactive' }
123
- ]}
124
- ]
125
- },
126
- icon: 'users'
62
+ // List posts
63
+ const { data, meta } = await client.blog.list({
64
+ page: 1,
65
+ per_page: 10,
66
+ category: 'tech', // Optional filter
67
+ tag: 'laravel', // Optional filter
68
+ search: 'keyword', // Optional search
127
69
  });
128
70
 
129
- // Get entity definition
130
- const entity = await client.entities.get('customers');
131
-
132
- // Update entity definition
133
- await client.entities.update('customers', { name: 'Clients' });
134
-
135
- // Delete entity definition (requires force if records exist)
136
- await client.entities.delete('customers', true);
137
- ```
138
-
139
- ### Record API Path Change
71
+ // Get single post
72
+ const post = await client.blog.get('post-slug');
140
73
 
141
- The record API path has changed:
74
+ // Get featured posts
75
+ const featured = await client.blog.featured(5);
142
76
 
143
- ```typescript
144
- // Before v2.1.0
145
- await client.entities.createRecord('customers', { data: { company: 'ACME' } });
77
+ // Filter by category/tag
78
+ const { data } = await client.blog.byCategory('news');
79
+ const { data } = await client.blog.byTag('featured');
146
80
 
147
- // v2.2.0 and later - no data wrapper needed
148
- await client.entities.createRecord('customers', { company: 'ACME' });
81
+ // Get all categories and tags
82
+ const categories = await client.blog.categories(); // string[]
83
+ const tags = await client.blog.tags(); // string[]
149
84
  ```
150
85
 
151
- ---
152
-
153
- ## v2.0.0 Breaking Changes
154
-
155
- ### API Key Required
156
-
157
- **All API requests now require an API key.** There are no public APIs - this ensures security and proper tenant isolation.
158
-
86
+ **BlogPost Type:**
159
87
  ```typescript
160
- // v1.x - apiKey was optional
161
- const client = new Promptly({
162
- tenantId: 'demo',
163
- });
164
-
165
- // v2.0 - apiKey is REQUIRED
166
- const client = new Promptly({
167
- tenantId: 'demo',
168
- apiKey: 'pky_xxxxxxxxxxxxxxxxxxxxxxxx',
169
- });
170
- ```
171
-
172
- Get your API key from: **Dashboard > Settings > API Tokens**
173
-
174
- The SDK automatically includes the `X-API-Key` header in all requests.
175
-
176
- ---
177
-
178
- ## v1.3.0 Changes
179
-
180
- ### Unified Response Structure
181
-
182
- All list APIs now return a consistent `ListResponse<T>` format:
183
-
184
- ```typescript
185
- interface ListResponse<T> {
186
- data: T[]; // Always an array (never null/undefined)
187
- meta: {
188
- current_page: number;
189
- last_page: number;
190
- per_page: number;
191
- total: number;
192
- from: number | null;
193
- to: number | null;
194
- };
88
+ interface BlogPost {
89
+ id: number;
90
+ title: string;
91
+ slug: string;
92
+ excerpt: string;
93
+ featured_image: string | null;
94
+ category: string | null;
95
+ tags: string[];
96
+ author: string;
97
+ views: number;
98
+ status: string;
99
+ published_at: string | null;
100
+ created_at: string;
101
+ content?: string; // Detail view only
102
+ updated_at?: string; // Detail view only
195
103
  }
196
104
  ```
197
105
 
198
- **No more defensive code needed:**
199
-
200
- ```typescript
201
- // Before (v1.1.0)
202
- const posts = await client.blog.list();
203
- const items = posts?.data ?? []; // Defensive check needed
204
-
205
- // After (v1.3.0)
206
- const { data, meta } = await client.blog.list();
207
- data.map(post => ...); // data is always an array
208
- ```
209
-
210
- ## API Overview
211
-
212
- > **Note:** All APIs require API key (v2.0+). "Auth Required" means additional member login token is needed.
213
-
214
- | Resource | Read Operations | Write Operations (Auth Required) |
215
- |----------|-----------------|----------------------------------|
216
- | **Boards** | list, get, listPosts, getPost | createPost, updatePost, deletePost |
217
- | **Blog** | list, get, featured, byCategory, byTag | - |
218
- | **Comments** | boardPost, blogPost, standalone | createBoardPost, createBlogPost, createStandalone, update, delete, like |
219
- | **Shop** | listProducts, getProduct, listCategories | getCart, addToCart, listOrders, createOrder |
220
- | **Forms** | list, get, submit | mySubmissions |
221
- | **Auth** | login, register | logout, me, updateProfile |
222
- | **Media** | - | upload, list, delete |
223
- | **Entities** | list, get, listRecords, getRecord | create, update, delete, createRecord, updateRecord, deleteRecord |
224
- | **Reservation** | getSettings, listServices, listStaff, getAvailableDates, getAvailableSlots | create, list, get, cancel |
225
-
226
- ## API Reference
227
-
228
106
  ### Boards
229
107
 
230
108
  ```typescript
231
109
  // List boards
232
- const { data: boards, meta } = await client.boards.list();
233
- // Returns: ListResponse<Board>
110
+ const { data: boards } = await client.boards.list();
234
111
 
235
- // Get board details
236
- const board = await client.boards.get('first'); // slug or id
237
- // Returns: Board
112
+ // Get board
113
+ const board = await client.boards.get('board-slug');
238
114
 
239
- // List board posts
240
- const { data: posts, meta } = await client.boards.listPosts('first', {
115
+ // List posts
116
+ const { data: posts } = await client.boards.listPosts('board-slug', {
241
117
  page: 1,
242
118
  per_page: 10,
243
- search: 'keyword', // optional
119
+ search: 'keyword',
244
120
  });
245
- // Returns: ListResponse<BoardPost>
246
-
247
- // Get post details
248
- const post = await client.boards.getPost(1);
249
- // Returns: BoardPost
250
121
 
251
- // List comments
252
- const comments = await client.boards.listComments(1);
253
- // Returns: BoardComment[] (always an array)
254
- ```
122
+ // Get post
123
+ const post = await client.boards.getPost(postId);
255
124
 
256
- ### Posts & Comments - Protected (Login Required)
257
-
258
- ```typescript
259
- // Login first
260
- await client.auth.login({ email: 'user@example.com', password: 'password' });
261
-
262
- // Create post
263
- const newPost = await client.boards.createPost({
125
+ // Create post (auth required)
126
+ await client.boards.createPost({
264
127
  board_id: 1,
265
128
  title: 'Title',
266
129
  content: 'Content',
267
- is_notice: false,
268
- });
269
-
270
- // Update post
271
- await client.boards.updatePost(postId, {
272
- title: 'Updated title',
273
- content: 'Updated content',
274
130
  });
275
131
 
276
- // Delete post
132
+ // Update/Delete post (auth required)
133
+ await client.boards.updatePost(postId, { title: 'New Title' });
277
134
  await client.boards.deletePost(postId);
278
-
279
- // Create comment
280
- await client.boards.createComment(postId, {
281
- content: 'Comment content',
282
- parent_id: null, // Parent comment ID for replies
283
- });
284
-
285
- // Update comment
286
- await client.boards.updateComment(commentId, {
287
- content: 'Updated comment',
288
- });
289
-
290
- // Delete comment
291
- await client.boards.deleteComment(commentId);
292
- ```
293
-
294
- ### Blog
295
-
296
- ```typescript
297
- // List blog posts
298
- const { data: posts, meta } = await client.blog.list({
299
- page: 1,
300
- per_page: 10,
301
- category: 'news', // optional
302
- tag: 'featured', // optional
303
- search: 'keyword', // optional
304
- });
305
- // Returns: ListResponse<BlogPost>
306
-
307
- // Get blog post details
308
- const post = await client.blog.get('post-slug');
309
- // Returns: BlogPost
310
-
311
- // Featured posts
312
- const featured = await client.blog.featured(5);
313
- // Returns: BlogPost[] (always an array)
314
-
315
- // Get posts by category
316
- const { data: newsPosts } = await client.blog.byCategory('news');
317
-
318
- // Get posts by tag
319
- const { data: taggedPosts } = await client.blog.byTag('featured');
320
-
321
- // List categories
322
- const categories = await client.blog.categories();
323
- // Returns: string[] (always an array)
324
-
325
- // List tags
326
- const tags = await client.blog.tags();
327
- // Returns: string[] (always an array)
328
135
  ```
329
136
 
330
137
  ### Comments
331
138
 
332
- Three comment types are supported:
333
- - **Board comments** (`board_post`)
334
- - **Blog comments** (`blog_post`)
335
- - **Guestbook/Standalone comments** (`page`)
139
+ Supports three comment types: board posts, blog posts, and standalone pages (guestbook).
336
140
 
337
141
  ```typescript
338
- // Get board post comments
339
- const { data: comments, meta } = await client.comments.boardPost(postId, {
340
- page: 1,
341
- per_page: 20,
342
- });
343
- // Returns: ListResponse<Comment>
344
-
345
- // Create board post comment
142
+ // Board post comments
143
+ const { data } = await client.comments.boardPost(postId);
346
144
  await client.comments.createBoardPost(postId, {
347
- author_name: 'John Doe',
348
- author_email: 'user@example.com',
349
- content: 'Comment content',
350
- password: '1234', // For guest comments
351
- parent_id: null, // Parent comment ID for replies
352
- is_secret: false, // Secret comment flag
145
+ author_name: 'John',
146
+ content: 'Great post!',
147
+ password: '1234', // For guest comments
353
148
  });
354
149
 
355
- // Get blog post comments
356
- const { data: blogComments } = await client.comments.blogPost('post-slug');
357
- // Returns: ListResponse<Comment>
358
-
359
- // Create blog post comment
150
+ // Blog post comments
151
+ const { data } = await client.comments.blogPost('post-slug');
360
152
  await client.comments.createBlogPost('post-slug', {
361
153
  author_name: 'Jane',
362
- content: 'Great post!',
154
+ content: 'Nice article!',
363
155
  });
364
156
 
365
- // Get guestbook comments (identified by page_slug)
366
- const { data: guestbook } = await client.comments.standalone('guestbook');
367
- // Returns: ListResponse<Comment>
368
-
369
- // Create guestbook comment
157
+ // Standalone comments (guestbook)
158
+ const { data } = await client.comments.standalone('guestbook');
370
159
  await client.comments.createStandalone('guestbook', {
371
160
  author_name: 'Visitor',
372
161
  content: 'Hello!',
373
162
  });
374
163
 
375
- // Update comment
376
- await client.comments.update(commentId, {
377
- content: 'Updated comment',
378
- password: '1234', // Required for guest comments
379
- });
380
-
381
- // Delete comment
382
- await client.comments.delete(commentId, {
383
- password: '1234', // Required for guest comments
384
- });
385
-
386
- // Like comment
387
- const result = await client.comments.like(commentId);
388
- // Returns: { data: { likes: number } }
164
+ // Common operations
165
+ await client.comments.update(commentId, { content: 'Updated' });
166
+ await client.comments.delete(commentId, { password: '1234' });
167
+ await client.comments.like(commentId);
389
168
  ```
390
169
 
391
170
  ### Shop
392
171
 
393
- #### Public (No Login Required)
394
-
395
172
  ```typescript
396
- // List products
397
- const { data: products, meta } = await client.shop.listProducts({
398
- page: 1,
399
- per_page: 10,
400
- category: 'electronics', // optional
401
- is_featured: true, // optional
402
- search: 'keyword', // optional
173
+ // Products
174
+ const { data: products } = await client.shop.listProducts({
175
+ category: 'electronics',
176
+ is_featured: true,
403
177
  });
404
- // Returns: ListResponse<Product>
405
-
406
- // Get product details
407
178
  const product = await client.shop.getProduct('product-slug');
408
- // Returns: Product
409
-
410
- // Featured products
411
179
  const featured = await client.shop.featuredProducts(8);
412
- // Returns: Product[] (always an array)
413
-
414
- // List categories
415
180
  const categories = await client.shop.listCategories();
416
- // Returns: ProductCategory[] (always an array)
417
- ```
418
-
419
- #### Protected (Login Required)
420
181
 
421
- ```typescript
422
- // Get cart
182
+ // Cart (auth required)
423
183
  const cart = await client.shop.getCart();
424
- // Returns: Cart
425
-
426
- // Add to cart
427
- await client.shop.addToCart({
428
- product_id: 1,
429
- quantity: 2,
430
- variant_id: 10, // optional - for variant products
431
- });
432
-
433
- // Update cart item quantity
184
+ await client.shop.addToCart({ product_id: 1, quantity: 2 });
434
185
  await client.shop.updateCartItem(itemId, { quantity: 3 });
435
-
436
- // Remove from cart
437
186
  await client.shop.removeFromCart(itemId);
438
-
439
- // Clear cart
440
187
  await client.shop.clearCart();
441
188
 
442
- // Create order
189
+ // Orders (auth required)
443
190
  const order = await client.shop.createOrder({
444
191
  orderer_name: 'John Doe',
445
192
  orderer_email: 'john@example.com',
446
- orderer_phone: '555-123-4567',
193
+ orderer_phone: '555-1234',
447
194
  shipping_name: 'John Doe',
448
- shipping_phone: '555-123-4567',
195
+ shipping_phone: '555-1234',
449
196
  shipping_zipcode: '12345',
450
197
  shipping_address: '123 Main St',
451
198
  shipping_address_detail: 'Apt 101',
452
- shipping_memo: 'Leave at door',
453
- coupon_code: 'SAVE10', // optional
454
199
  });
455
-
456
- // List orders
457
- const { data: orders, meta } = await client.shop.listOrders();
458
- // Returns: ListResponse<Order>
459
-
460
- // Get order details
461
- const order = await client.shop.getOrder(orderId);
462
- // Returns: Order
463
-
464
- // Cancel order
200
+ const { data: orders } = await client.shop.listOrders();
201
+ const orderDetail = await client.shop.getOrder(orderId);
465
202
  await client.shop.cancelOrder(orderId);
466
203
 
467
- // Validate coupon
204
+ // Coupons
468
205
  const validation = await client.shop.validateCoupon('SAVE10', 50000);
469
- // Returns: { valid: boolean, discount_amount: number, coupon: Coupon }
470
-
471
- // My coupons
472
- const coupons = await client.shop.myCoupons();
473
- // Returns: Coupon[] (always an array)
206
+ const myCoupons = await client.shop.myCoupons();
474
207
  ```
475
208
 
476
- ### Reservation - NEW in v1.3.0
477
-
478
- #### Public (No Login Required)
209
+ ### Reservation
479
210
 
480
211
  ```typescript
481
- // Get reservation settings
212
+ // Public APIs
482
213
  const settings = await client.reservation.getSettings();
483
- // Returns: ReservationSettings
484
-
485
- // List services
486
214
  const services = await client.reservation.listServices();
487
- // Returns: ReservationService[] (always an array)
488
-
489
- // List staff
490
- const staffs = await client.reservation.listStaff();
491
- // Returns: ReservationStaff[] (always an array)
492
-
493
- // Get staff for specific service
494
- const serviceStaffs = await client.reservation.listStaff(serviceId);
215
+ const staff = await client.reservation.listStaff(serviceId);
495
216
 
496
- // Get available dates
217
+ // Get available dates/slots
497
218
  const dates = await client.reservation.getAvailableDates({
498
219
  service_id: 1,
499
- staff_id: 2, // optional
500
- start_date: '2026-01-01', // optional
501
- end_date: '2026-01-31', // optional
220
+ staff_id: 2,
221
+ start_date: '2026-01-01',
222
+ end_date: '2026-01-31',
502
223
  });
503
- // Returns: string[] (YYYY-MM-DD format)
504
-
505
- // Get available time slots
506
224
  const slots = await client.reservation.getAvailableSlots({
507
225
  service_id: 1,
508
226
  date: '2026-01-15',
509
- staff_id: 2, // optional
227
+ staff_id: 2,
510
228
  });
511
- // Returns: ReservationSlot[]
512
- ```
513
229
 
514
- #### Protected (Login Required)
515
-
516
- ```typescript
517
- // Create reservation
230
+ // Create reservation (auth required)
518
231
  const result = await client.reservation.create({
519
232
  service_id: 1,
520
- staff_id: 2, // optional
233
+ staff_id: 2,
521
234
  reservation_date: '2026-01-15',
522
235
  start_time: '14:00',
523
236
  customer_name: 'John Doe',
524
- customer_phone: '555-123-4567', // optional
525
- customer_email: 'john@example.com', // optional
526
- customer_memo: 'Special requests', // optional
527
- });
528
- // Returns: { reservation: Reservation, requires_payment: boolean, deposit: number }
529
-
530
- // List my reservations
531
- const { data: reservations, meta } = await client.reservation.list({
532
- status: 'confirmed', // optional
533
- upcoming: true, // optional
534
- past: false, // optional
237
+ customer_phone: '555-1234',
535
238
  });
536
- // Returns: ListResponse<Reservation>
537
239
 
538
- // Upcoming reservations
240
+ // My reservations (auth required)
241
+ const { data } = await client.reservation.list({ status: 'confirmed' });
539
242
  const upcoming = await client.reservation.upcoming(5);
540
- // Returns: Reservation[] (always an array)
541
-
542
- // Past reservations
543
243
  const past = await client.reservation.past(10);
544
- // Returns: Reservation[] (always an array)
545
-
546
- // Get reservation details
547
- const reservation = await client.reservation.get('RES-20260115-001');
548
- // Returns: Reservation
549
-
550
- // Cancel reservation
551
- const cancelled = await client.reservation.cancel('RES-20260115-001', 'Schedule change');
552
- // Returns: Reservation
553
- ```
554
-
555
- ### Auth (Authentication)
556
-
557
- ```typescript
558
- // Login
559
- const response = await client.auth.login({
560
- email: 'user@example.com',
561
- password: 'password',
562
- });
563
- // Returns: { member: Member, token: string }
564
- // Token is automatically stored
565
-
566
- // Register
567
- await client.auth.register({
568
- name: 'John Doe',
569
- email: 'user@example.com',
570
- password: 'password',
571
- password_confirmation: 'password',
572
- phone: '555-123-4567', // optional
573
- });
574
-
575
- // Logout
576
- await client.auth.logout();
577
-
578
- // Get current user
579
- const me = await client.auth.me();
580
- // Returns: Member
581
-
582
- // Update profile
583
- await client.auth.updateProfile({
584
- name: 'New Name',
585
- phone: '555-999-8888',
586
- });
587
-
588
- // Change password
589
- await client.auth.updateProfile({
590
- current_password: 'current_password',
591
- password: 'new_password',
592
- password_confirmation: 'new_password',
593
- });
594
-
595
- // Check authentication status
596
- client.isAuthenticated(); // true or false
597
-
598
- // Set token directly (e.g., from localStorage)
599
- client.setToken('saved-token');
600
-
601
- // Get token
602
- const token = client.getToken();
603
- ```
604
-
605
- #### Social Login
606
-
607
- ```typescript
608
- // Get social login providers
609
- const providers = await client.auth.getSocialProviders();
610
- // Returns: SocialProvider[]
611
-
612
- // Get social auth URL
613
- const { url } = await client.auth.getSocialAuthUrl('google');
614
- // Redirect to this URL
615
-
616
- // Handle callback (after redirect)
617
- const response = await client.auth.socialCallback('google', code);
618
- // Returns: { member: Member, token: string }
244
+ await client.reservation.cancel('RES-20260115-001', 'Schedule change');
619
245
  ```
620
246
 
621
247
  ### Forms
@@ -623,350 +249,133 @@ const response = await client.auth.socialCallback('google', code);
623
249
  ```typescript
624
250
  // List forms
625
251
  const { data: forms } = await client.forms.list();
626
- // Returns: ListResponse<Form>
627
252
 
628
- // Get form details
253
+ // Get form with fields
629
254
  const form = await client.forms.get('contact');
630
- // Returns: Form (includes field definitions)
631
255
 
632
- // Submit form (no login required)
256
+ // Submit form
633
257
  await client.forms.submit('contact', {
634
258
  name: 'John Doe',
635
- email: 'user@example.com',
636
- message: 'Inquiry content',
259
+ email: 'john@example.com',
260
+ message: 'Hello!',
637
261
  });
638
262
 
639
- // My submissions (login required)
263
+ // My submissions (auth required)
640
264
  const { data: submissions } = await client.forms.mySubmissions();
641
- // Returns: ListResponse<FormSubmission>
642
265
  ```
643
266
 
644
- ### Media - Protected
267
+ ### Media (Auth Required)
645
268
 
646
269
  ```typescript
647
- // Upload file
648
- const media = await client.media.upload(file); // File or Blob
649
- // Returns: Media
650
-
651
- // Upload multiple files
270
+ // Upload
271
+ const media = await client.media.upload(file);
652
272
  const mediaList = await client.media.uploadMultiple([file1, file2]);
653
- // Returns: Media[]
654
-
655
- // List my media
656
- const { data: mediaList, meta } = await client.media.list({
657
- page: 1,
658
- per_page: 20,
659
- type: 'image/jpeg', // optional
660
- });
661
- // Returns: ListResponse<Media>
662
273
 
663
- // Delete media
274
+ // List & Delete
275
+ const { data: myMedia } = await client.media.list({ type: 'image/jpeg' });
664
276
  await client.media.delete(mediaId);
665
277
  ```
666
278
 
667
- ### Entities (Custom Entities) - Dynamic Data Structures
668
-
669
- Create entity definitions and manage data directly from the API/SDK.
279
+ ### Custom Entities
670
280
 
671
- #### Entity Definition CRUD
281
+ Dynamic data structures for custom use cases.
672
282
 
673
283
  ```typescript
674
- // List entities
284
+ // Entity definitions
675
285
  const entities = await client.entities.list();
676
- // Returns: CustomEntity[] (always an array)
286
+ const entity = await client.entities.get('customers');
677
287
 
678
- // Create entity definition
679
- const entity = await client.entities.create({
288
+ await client.entities.create({
680
289
  name: 'Customer',
681
290
  slug: 'customers',
682
- description: 'Customer management',
683
291
  schema: {
684
292
  fields: [
685
- { name: 'company', label: 'Company Name', type: 'text', required: true },
293
+ { name: 'company', label: 'Company', type: 'text', required: true },
686
294
  { name: 'email', label: 'Email', type: 'email', required: true },
687
295
  { name: 'status', label: 'Status', type: 'select', options: [
688
296
  { value: 'active', label: 'Active' },
689
- { value: 'inactive', label: 'Inactive' }
690
- ]}
691
- ]
297
+ { value: 'inactive', label: 'Inactive' },
298
+ ]},
299
+ ],
692
300
  },
693
- icon: 'users'
694
- });
695
-
696
- // Get entity definition (includes schema)
697
- const entity = await client.entities.get('customers');
698
- // Returns: CustomEntity (includes schema)
699
-
700
- // Update entity definition
701
- await client.entities.update('customers', {
702
- name: 'Clients',
703
- description: 'Client management'
704
301
  });
705
302
 
706
- // Delete entity definition (requires force if records exist)
707
- await client.entities.delete('customers'); // When no records exist
708
- await client.entities.delete('customers', true); // Force delete with records
709
- ```
710
-
711
- #### Record CRUD
303
+ await client.entities.update('customers', { name: 'Clients' });
304
+ await client.entities.delete('customers', true); // force delete
712
305
 
713
- ```typescript
714
- // List records
715
- const { data: customers, meta } = await client.entities.listRecords('customers', {
716
- page: 1,
717
- per_page: 20,
718
- search: 'ACME', // Search
719
- sort: 'company', // Sort field
720
- dir: 'asc', // Sort direction
721
- filters: JSON.stringify({ status: 'active' }) // JSON filter
306
+ // Records
307
+ const { data: records } = await client.entities.listRecords('customers', {
308
+ search: 'ACME',
309
+ filters: JSON.stringify({ status: 'active' }),
722
310
  });
723
- // Returns: ListResponse<EntityRecord>
724
-
725
- // Get single record
726
- const customer = await client.entities.getRecord('customers', 1);
727
- // Returns: EntityRecord
728
- console.log(customer.data.company); // 'ABC Corp'
311
+ const record = await client.entities.getRecord('customers', 1);
729
312
 
730
- // Create record (pass schema fields directly)
731
- const newCustomer = await client.entities.createRecord('customers', {
732
- company: 'ABC Corp',
733
- email: 'contact@abc.com',
313
+ await client.entities.createRecord('customers', {
314
+ company: 'ACME Corp',
315
+ email: 'contact@acme.com',
734
316
  status: 'active',
735
317
  });
736
-
737
- // Update record (partial update - merges with existing data)
738
- await client.entities.updateRecord('customers', 1, {
739
- status: 'inactive',
740
- email: 'new@abc.com'
741
- });
742
-
743
- // Delete record
318
+ await client.entities.updateRecord('customers', 1, { status: 'inactive' });
744
319
  await client.entities.deleteRecord('customers', 1);
745
- ```
746
-
747
- #### TypeScript Type Support
748
320
 
749
- ```typescript
750
- // Typed entity accessor
321
+ // TypeScript support
751
322
  interface Customer {
752
323
  company: string;
753
324
  email: string;
754
- tier: 'standard' | 'vip';
325
+ status: 'active' | 'inactive';
755
326
  }
756
-
757
- const customers = client.entities.typed<Customer>('customer');
758
-
759
- // Type inference
760
- const list = await customers.list();
761
- list.data[0].data.company; // string
762
-
763
- const record = await customers.get(1);
764
- record.data.tier; // 'standard' | 'vip'
765
-
766
- // Type checking on create/update
767
- await customers.create({
768
- company: 'New Corp',
769
- email: 'new@corp.com',
770
- tier: 'standard',
771
- });
327
+ const customers = client.entities.typed<Customer>('customers');
328
+ const { data } = await customers.list();
772
329
  ```
773
330
 
774
- ### Site Settings
331
+ ### Auth
775
332
 
776
333
  ```typescript
777
- // Get theme settings
778
- const theme = await client.getTheme();
779
- // Returns: { name, colors, fonts }
334
+ // Register
335
+ await client.auth.register({
336
+ name: 'John Doe',
337
+ email: 'john@example.com',
338
+ password: 'password',
339
+ password_confirmation: 'password',
340
+ });
780
341
 
781
- // Get site settings
782
- const settings = await client.getSettings();
783
- // Returns: Record<string, any>
784
- ```
342
+ // Login/Logout
343
+ const { member, token } = await client.auth.login({
344
+ email: 'john@example.com',
345
+ password: 'password',
346
+ });
347
+ await client.auth.logout();
785
348
 
786
- ## Types
349
+ // Profile
350
+ const me = await client.auth.me();
351
+ await client.auth.updateProfile({ name: 'New Name' });
787
352
 
788
- ### Configuration
353
+ // Token management
354
+ client.setToken('saved-token');
355
+ const token = client.getToken();
356
+ client.isAuthenticated();
789
357
 
790
- ```typescript
791
- interface PromptlyConfig {
792
- /** Tenant ID (subdomain) */
793
- tenantId: string;
794
- /** API key (required) - Get from Dashboard > Settings > API Tokens */
795
- apiKey: string;
796
- /** Base URL of Promptly API (optional) */
797
- baseUrl?: string;
798
- /** Request timeout in milliseconds (optional) */
799
- timeout?: number;
800
- }
358
+ // Social login
359
+ const providers = await client.auth.getSocialProviders();
360
+ const { url } = await client.auth.getSocialAuthUrl('google');
361
+ const response = await client.auth.socialCallback('google', code);
801
362
  ```
802
363
 
803
- ### Common Types
364
+ ## Response Types
804
365
 
805
- ```typescript
806
- // Unified list response type
807
- interface ListResponse<T> {
808
- data: T[];
809
- meta: PaginationMeta;
810
- }
811
-
812
- interface PaginationMeta {
813
- current_page: number;
814
- last_page: number;
815
- per_page: number;
816
- total: number;
817
- from: number | null;
818
- to: number | null;
819
- }
820
- ```
821
-
822
- ### Resource Types
366
+ All list APIs return a consistent format:
823
367
 
824
368
  ```typescript
825
- interface Board {
826
- id: number;
827
- slug: string;
828
- name: string;
829
- description?: string;
830
- is_active: boolean;
831
- created_at: string;
832
- }
833
-
834
- interface BoardPost {
835
- id: number;
836
- board_id: number;
837
- title: string;
838
- content: string;
839
- excerpt?: string;
840
- author: string;
841
- views: number;
842
- is_notice: boolean;
843
- is_secret: boolean;
844
- is_mine: boolean;
845
- is_private: boolean;
846
- comment_count: number;
847
- attachments?: Media[];
848
- created_at: string;
849
- }
850
-
851
- interface BlogPost {
852
- id: number;
853
- slug: string;
854
- title: string;
855
- content: string;
856
- excerpt?: string;
857
- featured_image?: string;
858
- category?: string;
859
- tags?: string[];
860
- author_name?: string;
861
- is_published: boolean;
862
- published_at?: string;
863
- view_count: number;
864
- created_at: string;
865
- }
866
-
867
- interface Comment {
868
- id: number;
869
- type: 'board_post' | 'blog_post' | 'page';
870
- author_name: string;
871
- author_avatar: string | null;
872
- content: string;
873
- is_approved: boolean;
874
- is_pinned: boolean;
875
- is_secret: boolean;
876
- likes: number;
877
- depth: number;
878
- created_at: string;
879
- replies: Comment[];
880
- }
881
-
882
- interface Product {
883
- id: number;
884
- slug: string;
885
- name: string;
886
- description?: string;
887
- price: number;
888
- compare_price?: number;
889
- thumbnail?: string;
890
- images?: string[];
891
- status: 'draft' | 'active' | 'inactive';
892
- is_featured: boolean;
893
- in_stock?: boolean;
894
- discount_percent?: number;
895
- created_at: string;
896
- }
897
-
898
- interface Reservation {
899
- id: number;
900
- reservation_number: string;
901
- status: 'pending' | 'confirmed' | 'completed' | 'cancelled' | 'no_show';
902
- status_label: string;
903
- reservation_date: string;
904
- start_time: string;
905
- end_time: string;
906
- time_range: string;
907
- customer_name: string;
908
- customer_phone: string | null;
909
- customer_email: string | null;
910
- price: number;
911
- deposit: number;
912
- payment_status: 'pending' | 'paid' | 'refunded' | 'partial';
913
- can_cancel: boolean;
914
- service: { id: number; name: string; duration: number; } | null;
915
- staff: { id: number; name: string; avatar: string | null; } | null;
916
- created_at: string;
917
- }
918
-
919
- interface ReservationService {
920
- id: number;
921
- name: string;
922
- slug: string;
923
- description: string | null;
924
- thumbnail: string | null;
925
- duration: number;
926
- price: number;
927
- requires_staff: boolean;
928
- requires_payment: boolean;
929
- deposit: number;
930
- staffs: Array<{ id: number; name: string; avatar: string | null; }>;
931
- }
932
-
933
- interface ReservationSettings {
934
- timezone: string;
935
- slot_interval: number;
936
- min_notice_hours: number;
937
- max_advance_days: number;
938
- cancellation_hours: number;
939
- allow_online_payment: boolean;
940
- bookable_date_range: { start: string; end: string; };
941
- }
942
-
943
- interface Member {
944
- id: number;
945
- name: string;
946
- email: string;
947
- phone?: string;
948
- avatar?: string;
949
- is_active: boolean;
950
- created_at: string;
951
- }
952
-
953
- interface CustomEntity {
954
- id: number;
955
- name: string;
956
- slug: string;
957
- description?: string;
958
- schema: EntitySchema;
959
- is_active: boolean;
960
- created_at: string;
961
- }
962
-
963
- interface EntityRecord {
964
- id: number;
965
- entity_id: number;
966
- data: Record<string, any>;
967
- status: 'active' | 'archived' | 'draft';
968
- created_at: string;
969
- updated_at: string;
369
+ interface ListResponse<T> {
370
+ data: T[]; // Always an array, never null
371
+ meta: {
372
+ current_page: number;
373
+ last_page: number;
374
+ per_page: number;
375
+ total: number;
376
+ from: number | null;
377
+ to: number | null;
378
+ };
970
379
  }
971
380
  ```
972
381
 
@@ -994,23 +403,20 @@ import { Promptly } from '@back23/promptly-sdk';
994
403
 
995
404
  const client = new Promptly({
996
405
  tenantId: 'demo',
997
- apiKey: process.env.NEXT_PUBLIC_PROMPTLY_API_KEY!, // Required
998
- baseUrl: 'https://promptly.webbyon.com',
406
+ apiKey: process.env.NEXT_PUBLIC_PROMPTLY_API_KEY!,
999
407
  });
1000
408
 
1001
- // Blog post list (with pagination)
1002
409
  function BlogList() {
1003
410
  const [posts, setPosts] = useState([]);
1004
- const [meta, setMeta] = useState(null);
1005
- const [page, setPage] = useState(1);
411
+ const [loading, setLoading] = useState(true);
1006
412
 
1007
413
  useEffect(() => {
1008
- client.blog.list({ page, per_page: 10 })
1009
- .then(({ data, meta }) => {
1010
- setPosts(data); // Always an array
1011
- setMeta(meta);
1012
- });
1013
- }, [page]);
414
+ client.blog.list({ per_page: 10 })
415
+ .then(({ data }) => setPosts(data))
416
+ .finally(() => setLoading(false));
417
+ }, []);
418
+
419
+ if (loading) return <div>Loading...</div>;
1014
420
 
1015
421
  return (
1016
422
  <div>
@@ -1018,83 +424,33 @@ function BlogList() {
1018
424
  <article key={post.id}>
1019
425
  <h2>{post.title}</h2>
1020
426
  <p>{post.excerpt}</p>
427
+ <span>{post.category}</span>
428
+ {post.tags.map(tag => <span key={tag}>{tag}</span>)}
1021
429
  </article>
1022
430
  ))}
1023
-
1024
- {meta && (
1025
- <div>
1026
- Page {meta.current_page} of {meta.last_page}
1027
- <button
1028
- onClick={() => setPage(p => p - 1)}
1029
- disabled={page <= 1}
1030
- >
1031
- Previous
1032
- </button>
1033
- <button
1034
- onClick={() => setPage(p => p + 1)}
1035
- disabled={page >= meta.last_page}
1036
- >
1037
- Next
1038
- </button>
1039
- </div>
1040
- )}
1041
431
  </div>
1042
432
  );
1043
433
  }
434
+ ```
1044
435
 
1045
- // Reservation form
1046
- function ReservationForm() {
1047
- const [services, setServices] = useState([]);
1048
- const [selectedService, setSelectedService] = useState(null);
1049
- const [dates, setDates] = useState([]);
1050
- const [slots, setSlots] = useState([]);
436
+ ## Changelog
1051
437
 
1052
- useEffect(() => {
1053
- client.reservation.listServices().then(setServices);
1054
- }, []);
438
+ ### v2.12.0
439
+ - Added `category`, `tags`, `views`, `published_at` fields to BlogPost response
440
+ - Added `GET /blog/categories` and `GET /blog/tags` endpoints
1055
441
 
1056
- useEffect(() => {
1057
- if (selectedService) {
1058
- client.reservation.getAvailableDates({
1059
- service_id: selectedService,
1060
- }).then(setDates);
1061
- }
1062
- }, [selectedService]);
1063
-
1064
- const handleDateSelect = async (date) => {
1065
- const availableSlots = await client.reservation.getAvailableSlots({
1066
- service_id: selectedService,
1067
- date,
1068
- });
1069
- setSlots(availableSlots);
1070
- };
442
+ ### v2.5.0
443
+ - Added `is_secret` and `is_mine` fields to BoardPost
1071
444
 
1072
- return (
1073
- <div>
1074
- <select onChange={e => setSelectedService(Number(e.target.value))}>
1075
- <option>Select a service</option>
1076
- {services.map(s => (
1077
- <option key={s.id} value={s.id}>{s.name}</option>
1078
- ))}
1079
- </select>
1080
-
1081
- <div>
1082
- {dates.map(date => (
1083
- <button key={date} onClick={() => handleDateSelect(date)}>
1084
- {date}
1085
- </button>
1086
- ))}
1087
- </div>
1088
-
1089
- <div>
1090
- {slots.filter(s => s.available).map(slot => (
1091
- <button key={slot.time}>{slot.time}</button>
1092
- ))}
1093
- </div>
1094
- </div>
1095
- );
1096
- }
1097
- ```
445
+ ### v2.3.0
446
+ - Polymorphic comments API (board, blog, standalone)
447
+
448
+ ### v2.0.0
449
+ - **Breaking:** API key required for all requests
450
+
451
+ ### v1.3.0
452
+ - Unified `ListResponse<T>` format for all list APIs
453
+ - Reservation system support
1098
454
 
1099
455
  ## License
1100
456
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@back23/promptly-sdk",
3
- "version": "2.11.0",
3
+ "version": "2.13.0",
4
4
  "description": "Promptly AI CMS SDK for JavaScript/TypeScript",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",