tailorade-sdk
v1.0.1
Published
Official TypeScript SDK for the Tailorade API — customers, products, patterns, embroidery, and salwar master data.
Maintainers
Readme
tailorade-sdk
Official TypeScript / Node.js SDK for the Tailorade API.
Features
- Full TypeScript types for every resource
- Guest (unauthenticated) browsing via a Public API Key
- Unified sign-in / first-time registration via WhatsApp OTP
- Server-side logout with token blacklisting
- Client-side session management (
SessionManager) - E.164 phone sanitisation utility (
sanitizePhone) - OTP resend cooldown tracker (
OtpCooldown) - Appointment Slots & Bookings resources
- HSO Orders (Home Service Orders) — read-only
- Auto-pagination helper
- Filter builder helper
- Automatic retry with exponential back-off (5xx / network errors)
- Per-request timeout
Installation
npm install tailorade-sdkQuick Start
import { TailoradeClient, sanitizePhone } from 'tailorade-sdk';
// Initialize with your Public API Key for guest access.
const client = new TailoradeClient({
apiKey: process.env.PUBLIC_API_KEY,
});
// Browse the catalog without authentication.
const products = await client.products.list();
const slots = await client.appointmentSlots.list();Authentication
Authentication Modes
| Mode | How to set | What it sends | Access |
|---|---|---|---|
| Guest | new TailoradeClient({ apiKey: '...' }) | x-api-key: <key> | Public read-only endpoints |
| Authenticated | After auth.verifyOtp() | Authorization: Bearer <jwt> | All endpoints |
When a JWT is present, the API key is suppressed. The SDK always prefers the JWT.
Step 1 — Initiate sign-in (POST /auth/sign-in)
import { sanitizePhone } from 'tailorade-sdk';
const mobile = sanitizePhone('+91 98765 43210'); // → 9876543210
const { isNewCustomer } = await client.auth.signIn({ mobileNumber: mobile });
// isNewCustomer: true if a new account was created, false for existing customers.Step 2 — Verify OTP (POST /auth/verify-otp)
const { token, customer } = await client.auth.verifyOtp({
mobileNumber: 9876543210,
otp: '123456',
});
// JWT is stored automatically — no extra setup needed.
console.log(client.isAuthenticated); // true
console.log(client.customerId); // 'customer-uuid'Logout (POST /auth/logout)
await client.logout();
// Token is blacklisted server-side and cleared client-side.
console.log(client.isAuthenticated); // false
// A new session ID is generated for the guest session.Session Management
const client = new TailoradeClient({ apiKey: process.env.PUBLIC_API_KEY });
console.log(client.sessionId); // Auto-generated UUID, useful for logging / analytics
console.log(client.isAuthenticated); // false (guest)
await client.auth.signIn({ mobileNumber: 9876543210 });
await client.auth.verifyOtp({ mobileNumber: 9876543210, otp: '123456' });
console.log(client.isAuthenticated); // true
console.log(client.customerId); // 'cust-uuid'Phone Sanitization
import { sanitizePhone } from 'tailorade-sdk';
sanitizePhone('+91 98765 43210') // → 9876543210
sanitizePhone('0091-9876543210') // → 9876543210
sanitizePhone('9876543210') // → 9876543210
sanitizePhone('919876543210') // → 9876543210
// Throws on invalid input:
sanitizePhone('1234') // Error: "Invalid phone number..."OTP Cooldown Tracker
The backend enforces a 30-second resend cooldown. Mirror it on the client for a smooth UI:
import { OtpCooldown } from 'tailorade-sdk';
const cooldown = new OtpCooldown();
await client.auth.signIn({ mobileNumber: 9876543210 });
cooldown.start();
// In your UI tick / reactive effect:
setInterval(() => {
if (cooldown.isActive) {
setButtonLabel(`Resend in ${cooldown.remainingLabel}`); // e.g. "Resend in 29s"
} else {
setButtonLabel('Resend OTP');
}
}, 1000);Resources
Public Resources (API key — guest mode)
| Resource | Property | Endpoints |
|---|---|---|
| Products | client.products | list, get, create, update, delete, bulkDelete, export, import |
| Pattern Catalogs | client.patternCatalogs | list, get, create, update, delete |
| Pattern Designs | client.patternDesigns | list, get, create, update, delete |
| Embroidery Catalogs | client.embroideryCatalogs | list, get, create, update, delete |
| Salwars | client.salwars | list, get, create, update, delete |
| Appointment Slots | client.appointmentSlots | list, get (public) · create, update, delete (admin) |
| HSO Orders | client.orders | list, get (read-only) |
Authenticated Resources (JWT required)
| Resource | Property | Key Methods |
|---|---|---|
| Customers | client.customers | list, get, findByMobile, create, update, delete |
| Appointment Bookings | client.appointmentBookings | list, get, listByCustomer, create, update, updateStatus, delete |
Appointment Slots
// Guest browsing
const slots = await client.appointmentSlots.list({
filters: JSON.stringify({ isActive: true }),
});
slots.items.forEach((slot) => {
console.log(slot.name, slot.time, `${slot.booked}/${slot.limit} booked`);
});Appointment Bookings
// Create a booking (requires authentication)
const booking = await client.appointmentBookings.create({
slotId: 'slot-uuid',
customerId: client.customerId!,
appointmentDateTime: '2026-07-07T10:00:00.000Z',
paymentAmount: 450,
discount: 50,
freeBooking: false,
});
// List my bookings
const myBookings = await client.appointmentBookings.listByCustomer(client.customerId!);
// Cancel a booking
await client.appointmentBookings.updateStatus(booking.id, { status: 'Cancelled' });HSO Orders (Home Service Orders)
// View order history (public — no login required)
const orders = await client.orders.list({
filters: JSON.stringify({ 'customer.id': client.customerId }),
});
orders.items.forEach((order) => {
console.log(order.hsoId, order.status, order.appointmentDate);
});
// Get a single order
const order = await client.orders.get('hso-uuid');Configuration
const client = new TailoradeClient({
baseUrl: 'https://backend.tailorade.in/api/v1', // default
apiKey: process.env.PUBLIC_API_KEY, // public read-only key
token: storedJwt, // pre-load a JWT from previous session
timeout: 10_000, // ms, default 10s
retries: 3, // default 3 (exponential back-off)
});Error Handling
All methods throw typed errors from tailorade-sdk/errors:
| Error class | HTTP status |
|---|---|
| ValidationError | 400 |
| AuthenticationError | 401 |
| AuthorizationError | 403 |
| NotFoundError | 404 |
| ConflictError | 409 |
| RateLimitError | 429 |
| ServerError | 500 |
| ServiceUnavailableError | 503 |
| NetworkError | network/timeout |
import { RateLimitError, AuthenticationError } from 'tailorade-sdk';
try {
await client.auth.signIn({ mobileNumber: 9876543210 });
} catch (err) {
if (err instanceof RateLimitError) {
// Daily limit reached or 30-second cooldown active.
console.error('Too many OTP requests:', err.message);
} else if (err instanceof AuthenticationError) {
// Invalid OTP.
}
}Rate Limits
| Limit | Scope | Error |
|---|---|---|
| 30-second cooldown | Per mobile number | RateLimitError (429) |
| 10 OTPs per 24 hours | Per mobile number | RateLimitError (429) |
Use OtpCooldown on the client to prevent unnecessary API calls during the 30-second window.
Environment Variables
Add these to your .env:
# Provided by Tailorade — used by the customer-facing app for guest access.
PUBLIC_API_KEY=your_public_api_key_hereDevelopment
npm install
npm run build
npm test