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