Diffsome diffsome
Sign up

@diffsome/sdk

Official TypeScript SDK for Diffsome - Headless CMS + E-commerce + Reservation Diffsome ๊ณต์‹ TypeScript SDK - ํ—ค๋“œ๋ฆฌ์Šค CMS + ์ด์ปค๋จธ์Šค + ์˜ˆ์•ฝ ์‹œ์Šคํ…œ

Installation

npm install @diffsome/sdk

or with yarn: yarn add @diffsome/sdk | pnpm: pnpm add @diffsome/sdk ๋˜๋Š” yarn: yarn add @diffsome/sdk | pnpm: pnpm add @diffsome/sdk

Quick Start

import { Diffsome } from '@diffsome/sdk'

// Initialize the client
const client = new Diffsome({
  tenantId: 'your-tenant-id',
  apiKey: 'pky_xxxxxxxxxxxxxxxx',  // Dashboard > Settings > API Tokens
  baseUrl: 'https://www.diffsome.com',  // optional

  // Token persistence (auto login)
  persistToken: true,
  storageType: 'localStorage',  // or 'sessionStorage'
  onAuthStateChange: (token, user) => {
    console.log('Auth changed:', user)
  }
})

// Ready to use!
const { data: posts } = await client.blog.list()
const { data: products } = await client.shop.listProducts()

API Key Required: Get your API key from Dashboard > Settings > API Tokens API Key ํ•„์ˆ˜: Dashboard > ์„ค์ • > API ํ† ํฐ์—์„œ API ํ‚ค๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์œผ์„ธ์š”

Available Resources ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฆฌ์†Œ์Šค

client.site

Site info, currency & price formatting ์‚ฌ์ดํŠธ ์ •๋ณด, ํ†ตํ™” & ๊ฐ€๊ฒฉ ํฌ๋งท

getInfo, getCurrency, formatPrice

client.auth

Authentication & member management ์ธ์ฆ & ํšŒ์› ๊ด€๋ฆฌ

login, register, logout, me, social

client.blog

Blog posts with categories & tags ์นดํ…Œ๊ณ ๋ฆฌ & ํƒœ๊ทธ ์ง€์› ๋ธ”๋กœ๊ทธ

list, get, featured, byCategory, byTag

client.boards

Community boards & posts ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ฒŒ์‹œํŒ & ๊ฒŒ์‹œ๊ธ€

list, listPosts, createPost, updatePost

client.shop

E-commerce: products, cart, orders ์ด์ปค๋จธ์Šค: ์ƒํ’ˆ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ, ์ฃผ๋ฌธ

listProducts, getCart, createOrder

client.reservation

Booking system ์˜ˆ์•ฝ ์‹œ์Šคํ…œ

listServices, getAvailableSlots, create

client.forms

Dynamic form submissions ๋™์  ํผ ์ œ์ถœ

list, get, submit, mySubmissions

client.comments

Unified comments (board, blog, standalone) ํ†ตํ•ฉ ๋Œ“๊ธ€ (๊ฒŒ์‹œํŒ, ๋ธ”๋กœ๊ทธ, ๋…๋ฆฝ)

boardPost, blogPost, standalone, like

client.entities

Custom entities (dynamic data) ์ปค์Šคํ…€ ์—”ํ‹ฐํ‹ฐ (๋™์  ๋ฐ์ดํ„ฐ)

list, listRecords, createRecord, typed

client.chat

Real-time live chat with WebSocket WebSocket ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ…

start, connect, send, poll, close

Site

Get site information, settings, and currency configuration ์‚ฌ์ดํŠธ ์ •๋ณด, ์„ค์ •, ํ†ตํ™” ๊ตฌ์„ฑ ์กฐํšŒ

// Get site info & settings
const info = await client.site.getInfo()
console.log(info.name, info.description)

// Get currency settings
const currency = await client.site.getCurrency()

// Format price with currency
const formatted = client.site.formatPrice(50000)
// -> "โ‚ฉ50,000" or "$50.00"

Authentication

Token Persistence ํ† ํฐ ์ž๋™ ์ €์žฅ

With persistToken: true, tokens are automatically saved to localStorage and restored on page reload. persistToken: true ์„ค์ • ์‹œ ํ† ํฐ์ด localStorage์— ์ž๋™ ์ €์žฅ๋˜๊ณ  ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ๋ณต์›๋ฉ๋‹ˆ๋‹ค.

Login / Register

// Register
await client.auth.register({
  name: 'John Doe',
  email: 'john@@example.com',
  password: 'password',
  password_confirmation: 'password'
})

// Login (token auto-saved with persistToken)
const { member, token } = await client.auth.login({
  email: 'john@@example.com',
  password: 'password'
})

// Logout (token auto-deleted)
await client.auth.logout()

// Check auth status
client.auth.isAuthenticated()  // true/false

// Get current user
const me = await client.auth.me()

// Update profile
await client.auth.updateProfile({ name: 'New Name' })

Social Login

// Get available providers
const providers = await client.auth.getSocialProviders()
// ['google', 'kakao', 'naver']

// Get auth URL and redirect
const { url } = await client.auth.getSocialAuthUrl('google')
window.location.href = url

// Handle callback (after redirect back)
const { member, token } = await client.auth.socialCallback('google', code)

Password Reset

// Request password reset email
await client.auth.forgotPassword({ email: 'john@@example.com' })

// Reset password with token
await client.auth.resetPassword({
  token: 'reset-token',
  email: 'john@@example.com',
  password: 'newpassword',
  password_confirmation: 'newpassword'
})

Token Management

// Set token manually
client.auth.setToken('token_string')

// Get current token
const token = client.auth.getToken()

Blog

// List posts with pagination & filters
const { data, meta } = await client.blog.list({
  page: 1,
  per_page: 10,
  category: 'tech',
  tag: 'laravel',
  search: 'keyword'
})

// Get single post by slug
const post = await client.blog.get('my-post-slug')

// Featured posts
const featured = await client.blog.featured(5)

// Filter by category or tag
const { data } = await client.blog.byCategory('news')
const { data } = await client.blog.byTag('featured')

// Get all categories & tags
const categories = await client.blog.categories()  // string[]
const tags = await client.blog.tags()  // string[]

// Get post by ID
const post = await client.blog.getById(123)

// Search posts
const { data } = await client.blog.search('keyword', { per_page: 10 })

Boards

// List boards
const { data: boards } = await client.boards.list()

// Get board by slug
const board = await client.boards.get('notices')

// List posts in a board
const { data: posts } = await client.boards.listPosts('notices', {
  page: 1,
  per_page: 10,
  search: 'keyword'
})

// Get single post
const post = await client.boards.getPost(postId)

// Create post (auth required)
await client.boards.createPost({
  board_id: 1,
  title: 'Post Title',
  content: 'Post content...',
  is_secret: false
})

// Update & delete post
await client.boards.updatePost(postId, { title: 'New Title' })
await client.boards.deletePost(postId)

// Board-level comments
const { data: comments } = await client.boards.listComments(postId)
await client.boards.createComment(postId, { content: 'Nice post!' })
await client.boards.updateComment(commentId, { content: 'Updated' })
await client.boards.deleteComment(commentId)

Comments

Unified comment system supporting board posts, blog posts, and standalone pages (guestbook, feedback, etc.) ๊ฒŒ์‹œํŒ, ๋ธ”๋กœ๊ทธ, ๋…๋ฆฝ ํŽ˜์ด์ง€(๋ฐฉ๋ช…๋ก, ํ”ผ๋“œ๋ฐฑ ๋“ฑ)๋ฅผ ์ง€์›ํ•˜๋Š” ํ†ตํ•ฉ ๋Œ“๊ธ€ ์‹œ์Šคํ…œ

// Board post comments
const { data } = await client.comments.boardPost(postId)
await client.comments.createBoardPost(postId, {
  author_name: 'John',
  content: 'Great post!',
  password: '1234'  // for guest comments
})

// Blog comments
const { data } = await client.comments.blogPost('post-slug')
await client.comments.createBlogPost('post-slug', {
  content: 'Nice article!'
})

// Standalone comments (guestbook)
const { data } = await client.comments.standalone('guestbook')
await client.comments.createStandalone('guestbook', {
  author_name: 'Visitor',
  content: 'Hello!'
})

// Common operations
await client.comments.update(commentId, { content: 'Updated' })
await client.comments.delete(commentId, { password: '1234' })
await client.comments.like(commentId)

Shop - Products

// List products with filters
const { data: products } = await client.shop.listProducts({
  category: 'electronics',
  is_featured: true,
  min_price: 10000,
  max_price: 50000,
  search: 'keyword'
})

// Get product by slug
const product = await client.shop.getProduct('product-slug')

// Featured products
const featured = await client.shop.featuredProducts(8)

// Categories
const categories = await client.shop.listCategories()

// Search products
const { data } = await client.shop.searchProducts('hoodie', { per_page: 10 })

// Product types
const digital = await client.shop.getDigitalProducts()
const subscriptionProducts = await client.shop.getSubscriptionProducts()
const bundles = await client.shop.getBundleProducts()
const items = await client.shop.getBundleItems('bundle-slug')

Product Reviews

// Get reviews with stats
const { data: reviews, stats } = await client.shop.getProductReviews('product-slug', {
  page: 1,
  per_page: 10,
  rating: 5  // filter by rating
})
// stats = { average_rating: 4.5, total_count: 123, rating_counts: { 5: 80, 4: 30, ... } }

// Write review (auth + purchase required)
await client.shop.createReview('product-slug', {
  rating: 5,
  title: 'Great product!',
  content: 'Highly recommended.',
  images: ['https://...']
})

// Update & delete review
await client.shop.updateReview(reviewId, { rating: 4 })
await client.shop.deleteReview(reviewId)

// Mark as helpful
await client.shop.markReviewHelpful(reviewId)

// My reviews
const { data: myReviews } = await client.shop.myReviews()

Cart & Orders

Authentication required for cart and order operations ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐ ์ฃผ๋ฌธ ๊ธฐ๋Šฅ์€ ์ธ์ฆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค

// Cart operations
const cart = await client.shop.getCart()

await client.shop.addToCart({
  product_id: 1,
  quantity: 2,
  variant_id: 3  // optional, for variants
})

await client.shop.updateCartItem(itemId, { quantity: 3 })
await client.shop.removeFromCart(itemId)
await client.shop.clearCart()

// Create order
const order = await client.shop.createOrder({
  orderer_name: 'John Doe',
  orderer_email: 'john@@example.com',
  orderer_phone: '010-1234-5678',
  shipping_name: 'John Doe',
  shipping_phone: '010-1234-5678',
  shipping_zipcode: '12345',
  shipping_address: '์„œ์šธ์‹œ ๊ฐ•๋‚จ๊ตฌ',
  shipping_address_detail: '101๋™ 101ํ˜ธ',
  shipping_memo: '๋ถ€์žฌ์‹œ ๊ฒฝ๋น„์‹ค',
  coupon_code: 'SAVE10'  // optional
})

// Order management
const { data: orders } = await client.shop.listOrders()
const orderDetail = await client.shop.getOrder(orderId)
await client.shop.cancelOrder(orderId)

// Coupon validation
const result = await client.shop.validateCoupon('SAVE10', 50000)
// { valid: true, discount_amount: 5000, coupon: { ... } }

// My coupons
const coupons = await client.shop.myCoupons()

Wishlist

Wishlist management (authentication required) ์œ„์‹œ๋ฆฌ์ŠคํŠธ ๊ด€๋ฆฌ (์ธ์ฆ ํ•„์š”)

// Get wishlist
const { data: wishlist } = await client.shop.getWishlist()

// Add to wishlist
await client.shop.addToWishlist({ product_id: 1, variant_id: 3 })

// Remove from wishlist
await client.shop.removeFromWishlist(wishlistId)

// Toggle wishlist
await client.shop.toggleWishlist(productId, variantId)

// Check if product is in wishlist
const inWishlist = await client.shop.isInWishlist(productId)

// Get wishlist count
const count = await client.shop.getWishlistCount()

// Move wishlist items to cart
await client.shop.moveWishlistToCart([wishlistId1, wishlistId2])

Digital Downloads

Access digital product downloads (authentication required) ๋””์ง€ํ„ธ ์ƒํ’ˆ ๋‹ค์šด๋กœ๋“œ (์ธ์ฆ ํ•„์š”)

// My downloads
const { data: downloads } = await client.shop.getMyDownloads()

// Downloads for a specific order
const { data: orderDownloads } = await client.shop.getOrderDownloads('ORD-123')

// Get download URL
const { url } = await client.shop.getDownloadUrl('download-token')

Subscriptions

Manage product subscriptions (authentication required) ์ƒํ’ˆ ๊ตฌ๋… ๊ด€๋ฆฌ (์ธ์ฆ ํ•„์š”)

// List subscriptions
const { data: subs } = await client.shop.getSubscriptions()

// Get subscription details
const sub = await client.shop.getSubscription(subId)

// Create subscription
await client.shop.createSubscription({ product_id: 1, payment_method: 'stripe' })

// Cancel subscription
await client.shop.cancelSubscription(subId, true)

// Pause & resume
await client.shop.pauseSubscription(subId)
await client.shop.resumeSubscription(subId)

Payments

Supports Toss Payments (Korea), Stripe (Global), and Nice Payments (Korea) ํ† ์ŠคํŽ˜์ด๋จผ์ธ (ํ•œ๊ตญ), Stripe(๊ธ€๋กœ๋ฒŒ), ๋‚˜์ด์ŠคํŽ˜์ด๋จผ์ธ (ํ•œ๊ตญ) ์ง€์›

Toss Payments

// Prepare payment (deprecated - use tossPaymentReady)
const payment = await client.shop.preparePayment({
  order_number: 'ORD-123',
  success_url: 'https://mysite.com/payment/success',
  fail_url: 'https://mysite.com/payment/fail'
})
// payment = { client_key, order_id, order_name, amount, customer_name, ... }

// Confirm payment (deprecated - use tossPaymentConfirm)
await client.shop.confirmPayment({
  payment_key: 'toss_payment_key',
  order_id: 'ORD-123',
  amount: 50000
})

Stripe Payments

// Stripe Checkout
const { url } = await client.shop.stripeCheckout({
  order_number: 'ORD-123',
  success_url: 'https://mysite.com/success',
  cancel_url: 'https://mysite.com/cancel'
})

// Verify payment
await client.shop.stripeVerify({ session_id: 'cs_xxx' })

// Refund
await client.shop.stripeRefund('ORD-123', 'Customer request', 50000)

Nice Payments

// Prepare payment
const payment = await client.shop.nicePaymentReady({
  order_number: 'ORD-123',
  amount: 50000
})

// Confirm payment
await client.shop.nicePaymentConfirm({ tid: 'nice_tid', amount: 50000 })

// Cancel payment
await client.shop.nicePaymentCancel('ORD-123', 'Reason', 50000)

Reservation

// Public APIs
const settings = await client.reservation.getSettings()
const services = await client.reservation.listServices()
const staff = await client.reservation.listStaff(serviceId)

// Check available dates
const dates = await client.reservation.getAvailableDates({
  service_id: 1,
  staff_id: 2,
  start_date: '2026-01-01',
  end_date: '2026-01-31'
})

// Check available time slots
const slots = await client.reservation.getAvailableSlots({
  service_id: 1,
  date: '2026-01-15',
  staff_id: 2
})

// Create reservation (auth required)
await client.reservation.create({
  service_id: 1,
  staff_id: 2,
  reservation_date: '2026-01-15',
  start_time: '14:00',
  customer_name: 'John Doe',
  customer_phone: '010-1234-5678',
  customer_email: 'john@@example.com',
  notes: 'Special request'
})

// Manage reservations
const { data } = await client.reservation.list({ status: 'confirmed' })
const upcoming = await client.reservation.upcoming(5)
const past = await client.reservation.past(10)

// Get reservation details
const reservation = await client.reservation.get('RES-20260115-001')

// Cancel reservation
await client.reservation.cancel('RES-20260115-001', 'Schedule change')

Forms

// List forms
const { data: forms } = await client.forms.list()

// Get form with field definitions
const form = await client.forms.get('contact')

// Submit form
await client.forms.submit('contact', {
  name: 'John Doe',
  email: 'john@@example.com',
  message: 'Hello!'
})

// My submissions (auth required)
const { data: submissions } = await client.forms.mySubmissions()

// Get single submission
const submission = await client.forms.getSubmission(submissionId)

Media

File upload and management (authentication required) ํŒŒ์ผ ์—…๋กœ๋“œ ๋ฐ ๊ด€๋ฆฌ (์ธ์ฆ ํ•„์š”)

// Upload file
const media = await client.media.upload(file)

// Upload multiple files
const mediaList = await client.media.uploadMultiple([file1, file2])

// List my media
const { data: myMedia } = await client.media.list({ type: 'image/jpeg' })

// Delete media
await client.media.delete(mediaId)

Custom Entities

Create and manage dynamic data structures ๋™์  ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌ

// Entity definitions
const entities = await client.entities.list()
const entity = await client.entities.get('customers')

// Create entity
await client.entities.create({
  name: 'Customer',
  slug: 'customers',
  schema: {
    fields: [
      { name: 'company', label: 'Company', type: 'text', required: true },
      { name: 'email', label: 'Email', type: 'email', required: true },
      { name: 'status', label: 'Status', type: 'select', options: [
        { value: 'active', label: 'Active' },
        { value: 'inactive', label: 'Inactive' }
      ]}
    ]
  }
})

// Records CRUD
const { data: records } = await client.entities.listRecords('customers', {
  search: 'ACME',
  filters: JSON.stringify({ status: 'active' })
})

const record = await client.entities.getRecord('customers', 1)

await client.entities.createRecord('customers', {
  company: 'ACME Corp',
  email: 'contact@@acme.com',
  status: 'active'
})

await client.entities.updateRecord('customers', 1, { status: 'inactive' })
await client.entities.deleteRecord('customers', 1)

// Update & delete entity definitions
await client.entities.update('customers', { name: 'Updated Name' })
await client.entities.delete('customers')
await client.entities.delete('customers', true)  // force delete with records

Typed Entities

interface Customer {
  company: string
  email: string
  status: 'active' | 'inactive'
}

const customers = client.entities.typed<Customer>('customers')
const { data } = await customers.list()  // data: Customer[]

TypeScript Support

Full TypeScript support with auto-generated types for all API responses. ๋ชจ๋“  API ์‘๋‹ต์— ๋Œ€ํ•ด ์ž๋™ ์ƒ์„ฑ๋œ ํƒ€์ž…๊ณผ ํ•จ๊ป˜ ์™„์ „ํ•œ TypeScript ์ง€์›.

import {
  Diffsome,
  // Types
  Product,
  BlogPost,
  BoardPost,
  Member,
  Order,
  Reservation,
  ListResponse,
  PromptlyError
} from '@diffsome/sdk'

// All responses are fully typed
const products: Product[] = await client.shop.listProducts()
const posts: BlogPost[] = await client.blog.list()

// List response type
interface ListResponse<T> {
  data: T[]
  meta: {
    current_page: number
    last_page: number
    per_page: number
    total: number
    from: number | null
    to: number | null
  }
}

Error Handling

import { Diffsome, PromptlyError } from '@diffsome/sdk'

try {
  await client.auth.login({
    email: 'wrong@@email.com',
    password: 'wrong'
  })
} catch (error) {
  if (error instanceof PromptlyError) {
    console.log(error.message)  // "Invalid credentials"
    console.log(error.status)   // 401
    console.log(error.errors)   // { email: ["Invalid email or password"] }
  }
}

Chat

Real-time live chat with WebSocket support (falls back to polling) WebSocket ์ง€์› ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ… (ํด๋ง ํด๋ฐฑ)

Available Methods

Method Description
configureWebSocket(config)Configure WebSocket connection
start(data)Start a conversation
list(params)List conversations
get(conversationId)Get conversation details
send(conversationId, data)Send a message
getMessages(conversationId, params)Get messages
poll(conversationId, lastMessageId)Poll for new messages
close(conversationId)Close conversation
sendTyping(conversationId)Send typing indicator
checkAvailability()Check agent availability
connect(conversationId)Real-time connection (WebSocket/polling)
setVisitorId(visitorId)Set visitor ID
getVisitorId()Get visitor ID

Usage Example

// Configure WebSocket (optional - falls back to polling)
client.chat.configureWebSocket({
  host: 'ws.diffsome.com',
  port: 443,
  key: 'diffsome-key',
  scheme: 'wss'
})

// Start a conversation
const { conversation_id } = await client.chat.start({
  visitor_name: 'John',
  visitor_email: '[email protected]',
  initial_message: 'Hello!'
})

// Real-time connection
const chat = client.chat.connect(conversation_id)

chat.onMessage((message) => {
  console.log('New message:', message.content)
})

chat.onTyping((senderType) => {
  console.log(`${senderType} is typing...`)
})

await chat.send('How can I get a refund?')

// Check agent availability
const { available } = await client.chat.checkAvailability()

chat.disconnect()

@diffsome/react

React hooks & components for Diffsome SDK

Installation

npm install @diffsome/sdk @diffsome/react

Peer dependency: react >= 18

Quick Start

import { Diffsome } from '@diffsome/sdk'
import { useProducts, useCart, useAuth, ChatBubble } from '@diffsome/react'
import '@diffsome/react/chat.css'

const client = new Diffsome({
  tenantId: 'my-shop',
  apiKey: 'pky_...'
})

function App() {
  const { products, loading } = useProducts(client)
  const { cart, addToCart } = useCart(client)
  const { user, login, logout } = useAuth(client)

  return (
    <div>
      {products.map(p => (
        <div key={p.id}>
          <h2>{p.name}</h2>
          <button onClick={() => addToCart(p.id)}>Add to Cart</button>
        </div>
      ))}
      <ChatBubble client={client} />
    </div>
  )
}

Hooks Reference

Category Hook Description
SiteuseSiteSite info, settings, theme
useCurrencyCurrency formatting helper
AuthuseAuthLogin, register, logout, current user
useSocialAuthGoogle, Kakao, Naver OAuth login
ShopuseProductsProduct list with filters, pagination
useProductSingle product by slug
useCategoriesProduct categories
useCartCart management (add, remove, update qty)
useWishlistWishlist toggle & list
useOrdersOrder history & details
PaymentusePaymentStatusAvailable payment methods
useTossPaymentToss Payments integration
useNicePaymentNicePay integration
useStripePaymentStripe integration
BloguseBlogBlog posts with pagination
useBlogPostSingle blog post by slug
useBlogCategoriesBlog categories & tags
BoarduseBoardsBoard list
useBoardPostsPosts in a board
useCreateBoardPostCreate new board post
CommentsuseCommentsCRUD for post, blog, page comments
ReviewsuseProductReviewsProduct reviews list
useCreateReviewSubmit a review
useCanReviewCheck if user can review
ReservationuseReservationServicesAvailable services & staff
useAvailableSlotsAvailable time slots
useCreateReservationBook a reservation
FormsuseFormLoad form schema & submit
MediauseMediaUpload & list media files
ChatuseChatLive chat messaging
EntitiesuseEntityRecordsCustom entity record list
useTypedEntityTyped entity with generics
CouponsuseCouponsList & validate coupons
SubscriptionsuseSubscriptionsSubscription plans & management
DownloadsuseDownloadsDigital product downloads

Components

<ChatBubble />

Drop-in live chat widget. Floating bubble with expandable chat window.

import { ChatBubble } from '@diffsome/react'
import '@diffsome/react/chat.css'

<ChatBubble
  client={client}
  position="bottom-right"
  greeting="How can we help?"
/>

<RichContent />

Renders HTML content from blog posts and board posts safely.

import { RichContent } from '@diffsome/react'

<RichContent html={post.content} />

Changelog

v3.4.0

  • Chat resource with WebSocket & polling support
  • Site resource (info, currency, price formatting)
  • Wishlist API (add, remove, toggle, move to cart)

v3.3.0

  • Stripe payments (checkout, verify, refund)
  • Nice payments (ready, confirm, cancel)

v3.2.0

  • Digital downloads API
  • Subscription products management

v3.1.0

  • Product search API
  • Bundle products support

v3.0.0

  • Major release with improved TypeScript types
  • Consistent API naming conventions
  • Enhanced error handling

v2.18.0

  • Product review API
  • Shipping settings API

v2.15.0

  • Toss Payments integration

v2.12.0

  • Blog category/tag filters
  • Added category, tags, views, published_at fields

v2.10.0

  • persistToken option for auto token persistence
  • onAuthStateChange callback
  • storageType option

v2.5.0

  • Board secret post support (is_secret, is_mine)

v2.3.0

  • Polymorphic comments API (board, blog, standalone)

v2.0.0

  • Breaking: API key required