@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)
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()
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 |
|---|---|---|
| Site | useSite | Site info, settings, theme |
useCurrency | Currency formatting helper | |
| Auth | useAuth | Login, register, logout, current user |
useSocialAuth | Google, Kakao, Naver OAuth login | |
| Shop | useProducts | Product list with filters, pagination |
useProduct | Single product by slug | |
useCategories | Product categories | |
useCart | Cart management (add, remove, update qty) | |
useWishlist | Wishlist toggle & list | |
useOrders | Order history & details | |
| Payment | usePaymentStatus | Available payment methods |
useTossPayment | Toss Payments integration | |
useNicePayment | NicePay integration | |
useStripePayment | Stripe integration | |
| Blog | useBlog | Blog posts with pagination |
useBlogPost | Single blog post by slug | |
useBlogCategories | Blog categories & tags | |
| Board | useBoards | Board list |
useBoardPosts | Posts in a board | |
useCreateBoardPost | Create new board post | |
| Comments | useComments | CRUD for post, blog, page comments |
| Reviews | useProductReviews | Product reviews list |
useCreateReview | Submit a review | |
useCanReview | Check if user can review | |
| Reservation | useReservationServices | Available services & staff |
useAvailableSlots | Available time slots | |
useCreateReservation | Book a reservation | |
| Forms | useForm | Load form schema & submit |
| Media | useMedia | Upload & list media files |
| Chat | useChat | Live chat messaging |
| Entities | useEntityRecords | Custom entity record list |
useTypedEntity | Typed entity with generics | |
| Coupons | useCoupons | List & validate coupons |
| Subscriptions | useSubscriptions | Subscription plans & management |
| Downloads | useDownloads | Digital 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_atfields
v2.10.0
persistTokenoption for auto token persistenceonAuthStateChangecallbackstorageTypeoption
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
Comments
Unified comment system supporting board posts, blog posts, and standalone pages (guestbook, feedback, etc.) ๊ฒ์ํ, ๋ธ๋ก๊ทธ, ๋ ๋ฆฝ ํ์ด์ง(๋ฐฉ๋ช ๋ก, ํผ๋๋ฐฑ ๋ฑ)๋ฅผ ์ง์ํ๋ ํตํฉ ๋๊ธ ์์คํ