@taddoo/sdk 1.0.0 → 1.1.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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Official SDK for the **Taddoo public website API**. Connect a tattoo artist's or tattoo studio's own website to [Taddoo](https://taddoo.com), the app tattoo artists use for requests, bookings, deposits, consent forms and bookkeeping.
4
4
 
5
- With a few lines the website shows the artist's **flash** (designs, sizes, prices, free slots, booking with deposit via Stripe), their **tattoo request form** (every question exactly as configured, with reference images), **fixed services** like a booking menu (tattoo services such as "small tattoo, 1 h, CHF 250" or "medium tattoo, 4 h, 300 to 500", piercing, tooth gems, each with price, duration, deposit and reference images), **availability** (free days, guest spots), **waitlists** and **gift vouchers**, all in the website's own design. Everything a client sends lands in the artist's Taddoo app on iPhone, iPad, Android and Mac: requests as projects with a push notification, bookings in the calendar.
5
+ With a few lines the website shows the artist's **flash** (designs, sizes, prices, free slots, booking with deposit via Stripe), their **tattoo request form** (every question exactly as configured, with reference images), **fixed services** like a booking menu (tattoo services such as "small tattoo, 1 h, CHF 250" or "medium tattoo, 4 h, 300 to 500", piercing, tooth gems, each with price, duration, deposit and reference images), **availability** (free days, guest spots), **waitlists**, **gift vouchers** and the **merch shop** (shirts, hoodies, prints with sizes, stock, shipping per country and checkout via Stripe), all in the website's own design. Everything a client sends lands in the artist's Taddoo app on iPhone, iPad, Android and Mac: requests as projects with a push notification, bookings in the calendar.
6
6
 
7
7
  - Zero dependencies, ESM, TypeScript types included
8
8
  - Browser, Node 18+, Deno, Bun, edge runtimes
@@ -77,6 +77,19 @@ if (res.status === "payment_required") window.location.href = res.checkoutUrl; /
77
77
 
78
78
  Designs with `bookingMode: "request"` use `taddoo.flash.request(id, { variantId, name, email, wishDate })` instead.
79
79
 
80
+ ## Merch shop
81
+
82
+ ```js
83
+ const shop = await taddoo.shop.get(); // products, variants, stock, shipping profiles
84
+ const cart = [{ variantId: shop.products[0].variants[0].id, quantity: 1 }];
85
+ const res = await taddoo.shop.checkout({ items: cart, country: "CH" });
86
+ localStorage.setItem("lastOrder", res.order.token);
87
+ window.location.href = res.checkoutUrl; // Stripe: address, phone, payment
88
+ // later: await taddoo.shop.order(token) -> { status: "paid" | "shipped" | ..., number }
89
+ ```
90
+
91
+ Shipping works like on Etsy: each product has a shipping profile with rates per country (`"EU"` and `"*"` for the rest of the world), the piece with the highest first-item price pays it once, every further piece its additional price. The checkout computes the real totals and reserves the stock for 30 minutes; `out_of_stock` (409) tells you which `variantId` has how many pieces `available`.
92
+
80
93
  ## Questions and answers
81
94
 
82
95
  Every question has `id`, `label`, `required` and `input.type`:
@@ -95,7 +108,7 @@ await taddoo.studio.submit(answers, { artist: "any" });
95
108
 
96
109
  ## Errors
97
110
 
98
- Failed calls throw `TaddooError` with `code`, `status` and `message`. For `code === "rejected"` the message is already translated for the visitor: show it as is. Other codes: `card_required`, `account_inactive`, `page_not_live`, `origin_not_allowed`, `session_required`, `books_closed`, `rate_limited`, `network_error` and more, see the docs.
111
+ Failed calls throw `TaddooError` with `code`, `status` and `message`. For `code === "rejected"` the message is already translated for the visitor: show it as is. Other codes: `card_required`, `account_inactive`, `page_not_live`, `origin_not_allowed`, `session_required`, `books_closed`, `out_of_stock`, `cant_ship`, `rate_limited`, `network_error` and more, see the docs.
99
112
 
100
113
  ## Server-side use
101
114
 
package/index.d.ts CHANGED
@@ -34,7 +34,7 @@ export interface Theme { preset: string; accent: string; background: string; mod
34
34
 
35
35
  export interface Block {
36
36
  id: string;
37
- kind: "link" | "text" | "social" | "heading" | "address" | "form" | "flash" | "availability" | "waitlist" | "voucher" | "consultation" | "touchup" | "treatment";
37
+ kind: "link" | "text" | "social" | "heading" | "address" | "form" | "flash" | "availability" | "waitlist" | "voucher" | "shop" | "consultation" | "touchup" | "treatment";
38
38
  title: string;
39
39
  url: string | null;
40
40
  imageUrl: string | null;
@@ -44,6 +44,7 @@ export interface Block {
44
44
  handles?: Record<string, string | null>;
45
45
  formId?: string;
46
46
  amounts?: number[];
47
+ customAmount?: { min: number; max: number } | null;
47
48
  currency?: string;
48
49
  /** API endpoint that serves this block's content, if any. */
49
50
  api?: string;
@@ -117,12 +118,15 @@ export interface Question { id: string; kind: string; label: string; hint: strin
117
118
  export interface Form { id: string; title: string; url: string | null; intro: string | null; thanks: string | null; acceptsRequests: boolean; questions: Question[] }
118
119
  export type Answers = Record<string, string | number | boolean | Array<string | number>>;
119
120
 
121
+ export interface FreeTime { full: boolean; windows: Array<{ from: string; to: string }> }
120
122
  export interface Availability {
121
123
  books: Books;
122
124
  timezone: string;
123
125
  horizonDays: number;
124
126
  freeDays: string[];
125
- studios: Array<{ id: string; name: string; address: string | null; freeDays: string[] }>;
127
+ /** Free hours per free day in local time; empty when the artist hides them. */
128
+ freeTimes: Record<string, FreeTime>;
129
+ studios: Array<{ id: string; name: string; address: string | null; freeDays: string[]; freeTimes: Record<string, FreeTime> }>;
126
130
  guestSpots: Array<{ id: string; name: string; address: string | null; from: string; to: string }>;
127
131
  timeOff: Array<{ from: string; to: string }>;
128
132
  }
@@ -137,7 +141,52 @@ export interface Waitlist {
137
141
  fields: Record<string, unknown>;
138
142
  sizeUnit: "cm" | "in";
139
143
  }
140
- export interface Vouchers { available: boolean; url: string | null; amounts: number[]; currency: string; validityMonths: number | null; intro: string | null; terms: string | null }
144
+ export interface Vouchers { available: boolean; url: string | null; amounts: number[]; /** Own whole amount allowed between min and max, or null. */ customAmount: { min: number; max: number } | null; currency: string; validityMonths: number | null; intro: string | null; terms: string | null }
145
+
146
+ export interface ShopVariant {
147
+ id: string;
148
+ /** e.g. "M" or "Black / M"; empty for products without options. */
149
+ name: string;
150
+ price: number;
151
+ /** Pieces left; null = unlimited. */
152
+ stock: number | null;
153
+ available: boolean;
154
+ }
155
+ export interface ShopProduct {
156
+ id: string;
157
+ slug: string;
158
+ /** Product page on taddoo.com. */
159
+ url: string;
160
+ title: string;
161
+ description: string | null;
162
+ categoryId: string | null;
163
+ /** Name of the option, e.g. "Size"; null = no options. */
164
+ optionName: string | null;
165
+ priceMin: number;
166
+ priceMax: number;
167
+ soldOut: boolean;
168
+ shippingProfileId: string;
169
+ /** The first image is the cover. */
170
+ images: Array<{ url: string; cardUrl: string; width: number | null; height: number | null }>;
171
+ variants: ShopVariant[];
172
+ }
173
+ /** Shipping like on Etsy: country is an ISO code, "EU" or "*" (rest of the world); the most specific rate applies. The piece with the highest first price pays first once, every other piece its additional. */
174
+ export interface ShopRate { country: string; first: number; additional: number }
175
+ export interface ShopShippingProfile { id: string; deliveryNote: string | null; rates: ShopRate[] }
176
+ export interface Shop {
177
+ /** false: no shop, no product yet or online payments not set up. */
178
+ available: boolean;
179
+ url: string | null;
180
+ currency: string;
181
+ intro: string | null;
182
+ terms: string | null;
183
+ /** Fee the customer pays on top of items plus shipping (percent as a fraction, e.g. 0.049); null when the artist pays it. */
184
+ surcharge: { percent: number; fixed: number } | null;
185
+ categories: Array<{ id: string; name: string }>;
186
+ products: ShopProduct[];
187
+ shippingProfiles: ShopShippingProfile[];
188
+ }
189
+ export interface ShopOrderStatus { status: "pending" | "paid" | "shipped" | "canceled" | "refunded"; number: number; email: string | null }
141
190
 
142
191
  export interface Studio {
143
192
  type: "studio";
@@ -206,11 +255,18 @@ export interface TaddooClient {
206
255
  join(id: string, data: { name: string; email: string; phone?: string; instagram?: string; preferredContact?: "email" | "instagram" | "whatsapp" | "sms"; wish?: string; bodyPart?: string; size?: number; sizeSecondary?: number }, opts?: { images?: File[] }): Promise<{ status: "joined" }>;
207
256
  };
208
257
  /** Fixed services: tattoo services, piercing, tooth gems, custom. */
209
- services(): Promise<{ services: Service[] }>;
258
+ services(opts?: { locale?: string }): Promise<{ services: Service[] }>;
210
259
  vouchers: {
211
260
  get(): Promise<Vouchers>;
212
261
  checkout(data: { amount: number; name: string; email: string; recipient?: string; message?: string }): Promise<{ checkoutUrl: string }>;
213
262
  };
263
+ /** Merch shop: products, cart checkout via Stripe, order status. */
264
+ shop: {
265
+ get(opts?: CallOptions): Promise<Shop>;
266
+ /** country: ISO code of the delivery address. Errors: out_of_stock / changed (409, with variantId and available), cant_ship (422). */
267
+ checkout(data: { items: Array<{ variantId: string; quantity: number }>; country: string }, opts?: CallOptions): Promise<{ checkoutUrl: string; order: { number: number; token: string } }>;
268
+ order(token: string, opts?: CallOptions): Promise<ShopOrderStatus>;
269
+ };
214
270
  studio: {
215
271
  get(): Promise<Studio>;
216
272
  form(): Promise<StudioForm>;
package/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /*!
2
- * @taddoo/sdk v1.0.0 - official client for the Taddoo public website API.
2
+ * @taddoo/sdk v1.1.0 - official client for the Taddoo public website API.
3
3
  * Connect a tattoo artist's or tattoo studio's own website to Taddoo:
4
- * flash designs, fixed services, request forms, availability, waitlists and
5
- * gift vouchers.
4
+ * flash designs, fixed services, request forms, availability, waitlists,
5
+ * gift vouchers and the merch shop.
6
6
  * Requests and bookings land in the artist's Taddoo app.
7
7
  * Docs: https://taddoo.com/developers · Guide for coding agents: https://taddoo.com/developers/llms.txt
8
8
  * No dependencies. Works in browsers, Node 18+, Deno, Bun and edge runtimes.
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  export const DEFAULT_BASE_URL = "https://taddoo.com/api/v1";
14
- export const SDK_VERSION = "1.0.0";
14
+ export const SDK_VERSION = "1.1.0";
15
15
 
16
16
  /** Error with the API's error code (e.g. "rejected", "card_required") and HTTP status. */
17
17
  export class TaddooError extends Error {
@@ -219,6 +219,17 @@ export function createTaddoo(options = {}) {
219
219
  /** { amount, name, email, recipient?, message? } -> { checkoutUrl } */
220
220
  checkout: (data, o = {}) => request("POST", "/vouchers/checkout", { ...o, json: data }),
221
221
  },
222
+ shop: {
223
+ /** Merch shop: { available, url, currency, intro, terms, surcharge, categories, products, shippingProfiles }. */
224
+ get: (o = {}) => request("GET", "/shop", o),
225
+ /**
226
+ * Pay a cart: { items: [{ variantId, quantity }], country } ->
227
+ * { checkoutUrl, order: { number, token } }. Redirect to checkoutUrl (Stripe).
228
+ */
229
+ checkout: (data, o = {}) => request("POST", "/shop/checkout", { ...o, json: data }),
230
+ /** Status of an order from checkout: { status, number, email }. */
231
+ order: (token, o = {}) => request("GET", `/shop/orders/${encodeURIComponent(token)}`, o),
232
+ },
222
233
  studio: {
223
234
  /** Studio key only: studio page with artists, opening hours and flash. */
224
235
  get: () => request("GET", "/studio", { artist: null }),
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@taddoo/sdk",
3
- "version": "1.0.0",
4
- "description": "Official Taddoo SDK: connect a tattoo artist's or tattoo studio's own website (built with Lovable, Bolt, v0, Cursor, Framer, Next.js, Astro...) to Taddoo. Flash gallery with booking and deposit, fixed services (tattoo services, piercing, tooth gems) with prices, tattoo request form, availability, waitlists and gift vouchers; requests land in the artist's Taddoo app.",
5
- "keywords": ["tattoo", "tattoo artist", "tattoo studio", "tattoo booking", "flash", "tattoo request form", "booking", "deposit", "link in bio", "taddoo", "website", "vibe coding", "lovable", "bolt", "v0", "cursor"],
3
+ "version": "1.1.0",
4
+ "description": "Official Taddoo SDK: connect a tattoo artist's or tattoo studio's own website (built with Lovable, Bolt, v0, Cursor, Framer, Next.js, Astro...) to Taddoo. Flash gallery with booking and deposit, fixed services (tattoo services, piercing, tooth gems) with prices, tattoo request form, availability, waitlists, gift vouchers and the merch shop; requests and orders land in the artist's Taddoo app.",
5
+ "keywords": ["tattoo", "tattoo artist", "tattoo studio", "tattoo booking", "flash", "tattoo request form", "booking", "deposit", "link in bio", "merch shop", "taddoo", "website", "vibe coding", "lovable", "bolt", "v0", "cursor"],
6
6
  "homepage": "https://taddoo.com/developers",
7
7
  "license": "MIT",
8
8
  "author": "Taddoo AG <hello@tattoomii.com> (https://taddoo.com)",