TypeScript Types
Complete type reference for the SDK
TypeScript Types
Complete reference of all types exported by @behio/storefront-sdk. All types are fully exported and can be imported directly:
import type { ProductDetail, Cart, CheckoutInput } from "@behio/storefront-sdk";Types match the API exactly (SDK 2.14.0)
Every type is checked against the public API schema on each backend change, and responses are checked against it at runtime. Since 2.14.0 a field the API returns as null is typed | null (for example imageUrl, description, gtin, compareAtPrice, Cart.discount). The API sent these nulls before as well; only the types now say so. If your code passes such a value where string | undefined is expected, write value ?? undefined.
Configuration
BehioStorefrontConfig
interface BehioStorefrontConfig {
/** API key (public: pk_live_xxx or private: sk_live_xxx) */
apiKey: string;
/** Backend base URL. Default: https://be.behio.com */
baseUrl?: string;
/** Default locale for catalog requests. Default: none (uses shop default) */
locale?: string;
/** Default currency for price resolution. Default: none (uses shop default) */
currency?: string;
/** Custom fetch implementation (for Node.js < 18 or testing) */
fetch?: typeof fetch;
/** Request timeout in milliseconds. Default: 30000 (30s) */
timeout?: number;
/** Number of retries on network/5xx errors. Default: 1 */
retries?: number;
/** Base delay between retries in ms (multiplied by attempt). Default: 1000 */
retryDelay?: number;
/**
* Real visitor IP for server-side rendering, sent as X-Behio-Visitor-Ip so
* the backend rate-limits per visitor instead of per rendering server. The
* Next.js adapter fills this automatically from the request headers; only
* trusted hosting IPs may set it, a browser sending it gains nothing.
*/
visitorIp?: string | (() => string | null | undefined | Promise<string | null | undefined>);
}Products
ProductListItem
Represents a product in list/collection views.
Popisy produktu jsou HTML
shortDescription i longDescription píše obchodník v rich text editoru v administraci,
takže API vrací HTML (<h3>...</h3><p>...</p>), nikdy holý text. Když je vypíšete
jako {product.shortDescription}, React je zaescapuje a návštěvník uvidí doslova značky.
API obsah sanitizuje při čtení (allowlist: nadpisy, seznamy, odkazy, obrázky, tabulky;
skripty, event handlery a javascript: odkazy jdou pryč), takže se dá vložit přes
dangerouslySetInnerHTML. Šablony Behio na to mají komponentu RichText. Do meta
description, JSON-LD, alt textů a jednořádkových popisků na kartách patří čistý text,
ne HTML.
interface ProductListItem {
id: string;
slug: string;
name: string;
/** HTML z rich text editoru (sanitizované), ne holý text. */
shortDescription?: string;
sku: string;
gtin?: string;
price: ProductPrice;
inStock: boolean;
stockQuantity?: number;
/** "Only X left". Non-null ONLY when the merchant enabled the nudge AND 0 < stock <= threshold. */
lowStockRemaining?: number | null;
/** Resolved availability (merchant preset or derived in-stock / sold-out), label localized. */
availability: ProductAvailability;
image?: string | null;
labels: ProductLabel[];
isFeatured: boolean;
}ProductAvailability
interface ProductAvailability {
/** Stable machine code: preset slug or derived 'in-stock' / 'sold-out'. */
code: string;
/** Localized display label, render as-is. */
label: string;
/** Optional merchant hex badge color. */
color?: string | null;
/** "Available from" date (epoch ms) for preorder-style states. */
restockAt?: number | null;
}ProductDetail
Full product detail, extends ProductListItem.
interface ProductDetail extends ProductListItem {
/** HTML z rich text editoru (sanitizované), ne holý text. */
longDescription?: string;
images: Array<{ url: string; alt?: string | null; order: number; role?: string }>;
categories: Array<{ id: string; slug: string; name: string }>;
variants: ProductVariant[];
volumePricing: ProductVolumePrice[];
/** Curated spec table. Array order is the display order; empty = render nothing. */
parameterGroups: ProductParameterGroup[];
seo: {
title?: string | null;
description?: string | null;
keywords?: string | null;
};
productGroups?: Array<{
slug: string;
name: string;
products: ProductListItem[];
}>;
/** Product weight (from warehouse item) */
weight?: number | null;
/** Weight unit: GRAM, KILOGRAM, TONNE */
weightUnit?: string | null;
}ProductPrice
interface ProductPrice {
amount: number;
currency: string;
compareAtPrice?: number | null;
}ProductVolumePrice
interface ProductVolumePrice {
minQuantity: number;
price: number;
currency?: string;
}ProductVariant
interface ProductVariant {
id: string;
sku: string;
/** Localized name of this variant. */
name: string;
/**
* Deep-link token, NOT a URL: a variant has no page of its own, so it lives at
* `/product/{product.slug}?variant={variantSlug}`. Since SDK 1.1.0.
*/
variantSlug: string;
/** Own texts; null = inherit the parent's. HTML. Since SDK 1.1.0. */
shortDescription?: string | null;
longDescription?: string | null;
/** Own gallery; empty = keep showing the parent's. Since SDK 1.1.0. */
images: Array<{url: string; alt?: string | null; order: number; role?: string}>;
/** Own SEO/OG overrides, null when none. Since SDK 1.1.0. */
seo?: {
title?: string | null;
description?: string | null;
keywords?: string | null;
ogTitle?: string | null;
ogDescription?: string | null;
ogImage?: string | null;
} | null;
/** Axis name/value pairs, e.g. [{name: 'Barva', value: 'modrá'}]. */
attributes: Array<{name: string; value: string}>;
price: ProductPrice | null;
inStock: boolean;
stockQuantity?: number;
/** "Only X left" for this variant, same contract as ProductListItem. */
lowStockRemaining?: number | null;
/** Most pieces the cart accepts for this variant; null = uncapped or hidden stock. SDK 2.13.0. */
maxPurchasableQuantity?: number | null;
/** Variant's own spec table: own value where it has one, parent's elsewhere. */
parameterGroups: ProductParameterGroup[];
}ProductParameterGroup
One block of the merchant's curated spec table. Groups and parameters arrive in display order, there is nothing to sort by.
interface ProductParameterGroup {
/** Derived from the merchant's own group name, stable, usable in URLs. */
slug: string;
/** Localized group heading, render as-is. */
name: string;
parameters: ProductParameter[];
}ProductParameter
interface ProductParameter {
/** Merchant-written label in the requested language. */
label: string;
/** Preformatted value. null ONLY for boolean parameters. */
value: string | null;
/** Non-null ONLY for boolean parameters, and then `value` is null. */
booleanValue: boolean | null;
/** Display unit: "cm", "g", a currency code, "%". null when the parameter has none. */
unit: string | null;
}/** Response of catalog.getProductParameters(slug, {locale}). */
interface ProductParametersResponse {
groups: ProductParameterGroup[];
}Parameters without a value are omitted server-side, so the array never contains an empty row. Warehouse data groups are not part of the public surface: no data-group id, no field key and no warehouse field type ever appears in a payload.
ProductLabel
interface ProductLabel {
id: string;
slug: string;
name: string;
color?: string;
}ProductsQuery
Query parameters for listing products.
interface ProductsQuery {
page?: number;
limit?: number;
/** Single category slug (OR logic with categories array) */
category?: string;
/** Multiple category slugs (OR logic) */
categories?: string[];
/** Single label slug */
label?: string;
priceMin?: number;
priceMax?: number;
currency?: string;
locale?: string;
sort?: ProductSortValue;
inStock?: boolean;
search?: string;
/**
* Parameter filters, keyed by parameter slug (FilterField.key / Facet.key).
* Array = multi-select, OR within the key. Ranges use `<slug>_min` / `<slug>_max`.
* Only parameters the merchant marked filterable are accepted.
* e.g. { material: 'bavlna', barva: ['cerna', 'bila'], hmotnost_min: 100 }
*/
parameters?: Record<string, unknown>;
/**
* Slug-based facet selection for SEO URLs: parameter slug -> value slugs,
* e.g. { barva: ['cerna'] }. Resolved server-side to the underlying values.
*/
facets?: Record<string, string[]>;
/** Filter by specific product IDs */
ids?: string[];
/** Filter by specific product slugs */
slugs?: string[];
/** Multiple labels (AND logic, product must have ALL) */
labels?: string[];
/** Exclude specific product IDs */
excludeIds?: string[];
/** Exclude products in specific categories */
excludeCategories?: string[];
/** Only products with compareAtPrice (on sale) */
hasDiscount?: boolean;
/** Only featured products */
isFeatured?: boolean;
/** Products created after timestamp (epoch ms) */
createdAfter?: number;
}ProductSort
Available sort values (const object for runtime + autocomplete):
const ProductSort = {
PRICE_ASC: "price_asc",
PRICE_DESC: "price_desc",
NAME_ASC: "name_asc",
NAME_DESC: "name_desc",
NEWEST: "newest",
FEATURED: "featured",
} as const;
type ProductSortValue = (typeof ProductSort)[keyof typeof ProductSort];FilterField
One filterable parameter, as returned by getFilters({locale}).
interface FilterField {
/** Parameter slug. The key you send back in ProductsQuery.parameters. */
key: string;
/** Localized parameter label. */
name: string;
/** How to render the control, not how the value is stored. */
type: "enum" | "range" | "boolean";
/** Parameter group the field belongs to (same grouping as the spec table). */
groupSlug: string;
/** Localized group name. */
groupName: string;
/** Display unit next to the input: "cm", "g", "%", a currency code. */
unit?: string | null;
/** enum only: the selectable values, localized. */
values?: string[];
}Only parameters the merchant explicitly marked filterable are ever returned, so a visitor cannot filter by a field they cannot see on the product.
Categories
Category
interface Category {
id: string;
slug: string;
name: string;
description?: string;
imageUrl?: string;
children: Category[];
productCount?: number;
}CategoryDetail
The category page. It carries SEO fields and no children: read subcategories
from catalog.getCategories().
interface CategoryDetail extends Omit<Category, "children"> {
/** @deprecated Never sent on the detail. */
children?: Category[];
/** Null when the category has no text in the requested language. */
seo?: {
title?: string | null;
description?: string | null;
/** Always null for categories. */
keywords?: string | null;
ogTitle?: string | null;
ogDescription?: string | null;
ogImage?: string | null;
} | null;
}Cart
Cart
interface Cart {
id: string;
sessionToken?: string;
items: CartItem[];
/** Lines at catalog prices (VAT in or out, per the shop's price mode). */
subtotal: number;
discountTotal: number;
discount?: CartDiscount;
/** The payable amount for the goods, VAT included, after discounts. */
grandTotal: number;
/** Goods VAT before the order discount (for the goods subtotal). */
taxTotal: number;
/** VAT contained in grandTotal, after the discount, as on the invoice. SDK 2.12.0. */
vatTotal?: number;
/** VAT of the payable amount by rate, sums to vatTotal. */
taxBreakdown: TaxBreakdownLine[];
currency: string;
itemCount: number;
}grandTotal is what the customer pays for the cart lines, VAT included.
Never add taxTotal to it, that double counts the VAT. Shipping and the
payment-method fee are chosen at checkout and appear only in
order.grandTotal. The same holds in both price modes: with INCL_VAT the
VAT is extracted out of the catalog price, with EXCL_VAT it is added on
top, and grandTotal already reflects whichever applies.
Discounts are always taken from the VAT-inclusive amount, in both price
modes and for every country, and they lower the base and the VAT. Render the
"VAT 21 %: X" rows from taxBreakdown (it sums to vatTotal, the VAT the
customer actually pays, identical to the invoice). taxTotal is the goods
VAT before the order discount and only belongs in a goods subtotal.
CartItem
interface CartItem {
id: string;
product: CartItemProduct;
quantity: number;
unitPrice: number;
totalPrice: number;
priceChanged: boolean;
volumePriceApplied: boolean;
}CartItemProduct
interface CartItemProduct {
slug: string;
name: string;
sku: string;
imageUrl?: string;
inStock: boolean;
currentPrice: number;
/** "Only X left" on the cart line, same contract as ProductListItem. SDK 2.13.0. */
lowStockRemaining?: number | null;
}CartDiscount
interface CartDiscount {
code: string;
type: string;
value: number;
}AddToCartInput
interface AddToCartInput {
/** Product ID (eshop product ID or warehouse item ID) */
productId: string;
quantity: number;
}Checkout
CheckoutInput
interface CheckoutInput {
shippingAddress: CheckoutAddress;
billingAddress: CheckoutAddress;
email: string;
phone?: string;
customerNote?: string;
}CheckoutAddress
interface CheckoutAddress {
firstName: string;
lastName: string;
company?: string;
street: string;
city: string;
zip: string;
country: string;
phone?: string;
}OrderDetail
Full order detail returned after checkout, extends OrderListItem.
interface OrderDetail extends OrderListItem {
items: OrderItem[];
shippingAddress: CheckoutAddress;
billingAddress: CheckoutAddress;
email: string;
phone?: string | null;
customerNote?: string | null;
subtotal: number;
/** Goods VAT before order discounts. */
taxTotal: number;
/** VAT contained in grandTotal (after discounts, incl. shipping), as on the invoice. SDK 2.12.0. */
vatTotal?: number;
/** VAT of the amount paid by rate, sums to vatTotal. */
taxBreakdown: TaxBreakdownLine[];
shippingTotal: number;
discountTotal: number;
fulfillmentStatus: FulfillmentStatus;
statusHistory: OrderStatusHistory[];
/** Discount codes applied to the order. SDK 2.14.0. */
discounts?: OrderDiscount[];
/** @deprecated Not sent on the detail; `checkout.createOrder()` returns it on `CheckoutResult`. */
trackingToken?: string;
/** Bank transfer details while unpaid, null otherwise. SDK 2.13.0. */
paymentInstructions?: OrderPaymentInstructions | null;
}
interface OrderPaymentInstructions {
accountNumber: string | null;
iban: string | null;
bic: string | null;
bankName: string | null;
/** Digits only, at most 10. */
variableSymbol: string | null;
amount: number;
currency: string;
instructions: string | null;
}Orders
OrderListItem
interface OrderListItem {
id: string;
orderNumber: string;
status: OrderStatus;
paymentStatus: PaymentStatus;
grandTotal: number;
currency: string;
itemCount: number;
createdAt: number;
}OrderItem
interface OrderItem {
/** Order line id. SDK 2.14.0. */
id?: string;
productName: string;
sku: string;
imageUrl?: string | null;
/** Product still in the shop, for a link back (SDK 2.8.0). Null = removed. */
productId?: string | null;
productSlug?: string | null;
quantity: number;
unitPrice: number;
/** VAT rate in percent, e.g. 21. SDK 2.14.0. */
taxRate?: number;
/** VAT of the whole line. SDK 2.14.0. */
taxAmount?: number;
totalPrice: number;
totalPriceWithTax: number;
}OrderDiscount
interface OrderDiscount {
code: string;
/** PERCENTAGE, FIXED_AMOUNT or FREE_SHIPPING. */
type: string;
value: number;
/** Money the code took off this order. */
appliedAmount: number;
}OrderStatusHistory
interface OrderStatusHistory {
/** Null for the first entry. */
fromStatus?: OrderStatus | null;
toStatus: OrderStatus;
note?: string | null;
/** @deprecated Never sent: who changed the status stays inside the shop. */
changedBy?: string;
createdAt: number;
}Status enums
const OrderStatuses = {
PENDING: "PENDING",
CONFIRMED: "CONFIRMED",
PROCESSING: "PROCESSING",
SHIPPED: "SHIPPED",
DELIVERED: "DELIVERED",
CANCELLED: "CANCELLED",
REFUNDED: "REFUNDED",
} as const;
type OrderStatus = (typeof OrderStatuses)[keyof typeof OrderStatuses];
const PaymentStatuses = {
UNPAID: "UNPAID",
PAID: "PAID",
PARTIALLY_REFUNDED: "PARTIALLY_REFUNDED",
REFUNDED: "REFUNDED",
} as const;
type PaymentStatus = (typeof PaymentStatuses)[keyof typeof PaymentStatuses];
const FulfillmentStatuses = {
UNFULFILLED: "UNFULFILLED",
PARTIALLY_FULFILLED: "PARTIALLY_FULFILLED",
FULFILLED: "FULFILLED",
} as const;
type FulfillmentStatus = (typeof FulfillmentStatuses)[keyof typeof FulfillmentStatuses];Auth
AuthTokens
interface AuthTokens {
accessToken: string;
refreshToken: string;
}RegisterInput
interface RegisterInput {
email: string;
password: string;
firstName?: string;
lastName?: string;
}LoginInput
interface LoginInput {
email: string;
password: string;
}Customer
CustomerProfile
interface CustomerProfile {
id: string;
email: string;
firstName?: string | null;
lastName?: string | null;
phone?: string | null;
emailVerified: boolean;
isApproved: boolean;
/** Registration time (Unix ms). SDK 2.14.0. */
createdAt?: number;
}updateProfile accepts null for firstName, lastName and phone and
clears the value; a missing key leaves it unchanged.
CustomerAddress
interface CustomerAddress {
id: string;
type: AddressType;
isDefault: boolean;
firstName: string;
lastName: string;
company?: string | null;
street: string;
city: string;
zip: string;
country: string;
state?: string | null;
companyId?: string | null;
vatId?: string | null;
/** VIES result at save time, read-only. */
vatIdValid?: boolean | null;
vatIdCheckedAt?: number | null;
phone?: string | null;
}createAddress and updateAddress accept null for company, phone,
state, companyId and vatId, so an address read from the API can be sent
back as it is. On updateAddress, null clears the value and a missing key
leaves it unchanged (SDK 2.14.0; older backends answered 400).
AddressType
const AddressTypes = {
SHIPPING: "SHIPPING",
BILLING: "BILLING",
} as const;
type AddressType = (typeof AddressTypes)[keyof typeof AddressTypes];Bundles
Bundle
interface Bundle {
id: string;
slug: string;
name: string;
description: string | null;
bundlePrice: number;
currency: string;
coverImage: string | null;
endsAt: number | null;
itemsSum: number;
savings: number;
savingsPercent: number;
items: BundleItem[];
}BundleItem
interface BundleItem {
productId: string;
slug: string | null;
name: string;
sku: string;
quantity: number;
imageUrl: string | null;
defaultPrice: number | null;
}Cross-sell
CrossSellItem
interface CrossSellItem {
productId: string;
slug: string | null;
name: string; // localized
sku: string;
price: number | null; // in `currency`, incl. customer price-list override
compareAtPrice: number | null; // original price when on sale
currency: string;
imageUrl: string | null;
stockCached: number;
inStock: boolean;
}Promotions
ActivePromotion
interface ActivePromotion {
id: string;
name: string;
slug: string;
type: string;
discountType: string;
discountValue: number;
startsAt: number;
endsAt: number | null;
badgeText: string | null;
badgeColor: string | null;
showCountdown: boolean;
couponRequired: boolean;
}Gift Cards
GiftCardBalance
interface GiftCardBalance {
valid: boolean;
balance: number;
currency: string;
}Wishlist
WishlistItem
interface WishlistItem {
id: string;
productId: string;
productName: string;
productSku: string;
productSlug: string | null;
imageUrl: string | null;
price: number | null;
stockCached: number;
createdAt: number;
}Reviews
ProductReviewsResponse
interface ProductReviewsResponse {
items: ProductReview[];
averageRating: number;
reviewCount: number;
page: number;
totalPages: number;
}ProductReview
interface ProductReview {
id: string;
authorName: string;
rating: number;
title: string | null;
content: string | null;
imageUrls: string[];
isVerifiedPurchase: boolean;
helpfulCount: number;
unhelpfulCount: number;
replyContent: string | null;
createdAt: number;
}SubmitReviewInput
interface SubmitReviewInput {
productId: string;
rating: number;
title?: string;
content?: string;
authorName: string;
authorEmail?: string;
imageUrls?: string[];
}Returns
ReturnRequest
interface ReturnRequest {
id: string;
orderId: string;
status: string;
reason: string;
customerNote: string | null;
refundAmount: number | null;
refundMethod: string | null;
createdAt: number;
}SubmitReturnInput
interface SubmitReturnInput {
orderId: string;
reason: string;
customerNote?: string;
items: {
orderItemId: string;
productName: string;
quantity: number;
reason?: string;
imageUrls?: string[];
}[];
}Cookie Consent
CookieConsent
interface CookieConsent {
necessary: boolean;
analytics: boolean;
marketing: boolean;
preferences: boolean;
consentedAt: number;
}CookieConsentInput
interface CookieConsentInput {
visitorId: string;
analytics: boolean;
marketing: boolean;
preferences: boolean;
}Quotes (B2B)
QuoteRequest
interface QuoteRequest {
id: string;
status: string;
contactName: string;
contactEmail: string;
companyName: string | null;
quotedTotal: number | null;
createdAt: number;
}SubmitQuoteInput
interface SubmitQuoteInput {
contactName: string;
contactEmail: string;
contactPhone?: string;
companyName?: string;
companyIco?: string;
message?: string;
items: {
productId: string;
quantity: number;
requestedPrice?: number;
}[];
}Pages
Page
interface Page {
slug: string;
title: string;
isActive: boolean;
/** Last change (epoch ms), sitemap lastmod */
updatedAt?: number;
}PageDetail
interface PageDetail {
slug: string;
title: string;
/** Ready, sanitised HTML of the body (SDK 2.20.0). Render it with <RichText html={...} /> */
contentHtml: string;
/** @deprecated Read contentHtml. The body as a string: sanitised HTML, or markdown on older pages */
content: string;
seoTitle?: string;
seoDescription?: string;
ogTitle?: string;
ogDescription?: string;
ogImage?: string;
/** Downloadable files (e.g. legal form PDFs), render as a download list */
attachments: PageAttachment[];
}
interface PageAttachment {
name: string;
description: string | null;
url: string;
mimeType: string;
fileSize: number;
}Blog
interface Blog {
/** URL segment: /blog/{handle} */
handle: string;
name: string;
description: string | null;
postCount: number;
settings: {
postsPerPage?: number;
showAuthor?: boolean;
showReadingTime?: boolean;
showTags?: boolean;
layout?: "grid" | "list" | "magazine";
};
/** Latest publish time (epoch ms), for sitemaps */
updatedAt: number | null;
}
interface BlogTag {
slug: string;
name: string;
}
interface BlogPostListItem {
slug: string;
title: string;
excerpt: string | null;
coverUrl: string | null;
publishedAt: number | null;
readingTimeMin: number | null;
authorName: string | null;
isFeatured: boolean;
tags: BlogTag[];
}
interface BlogPostsPage {
items: BlogPostListItem[];
total: number;
page: number;
limit: number;
totalPages: number;
/** Tags of the blog (for a filter) */
tags: BlogTag[];
}
interface BlogPostDetail {
slug: string;
blogHandle: string;
blogName: string;
title: string;
excerpt: string | null;
/** {format: "html", html} sanitised by the API, render with RichText */
content: {format?: string; html?: string};
coverUrl: string | null;
publishedAt: number | null;
updatedAt: number | null;
readingTimeMin: number | null;
authorName: string | null;
tags: BlogTag[];
seoTitle: string | null;
seoDescription: string | null;
ogImage: string | null;
/** Other posts of the same blog */
related: BlogPostListItem[];
}Forms
type SiteFormFieldType =
| "text" | "email" | "phone" | "textarea" | "number"
| "select" | "radio" | "checkbox" | "multiselect"
| "date" | "time" | "datetime" | "url" | "hidden";
interface SiteFormField {
/** Key in the submitted `data` object */
key: string;
type: SiteFormFieldType;
label: string;
placeholder?: string;
help?: string;
required: boolean;
/** select / radio / multiselect */
options?: {value: string; label: string}[];
/** Number: min/max value. Text: min/max length */
min?: number;
max?: number;
/** Regular expression for text fields (without slashes) */
pattern?: string;
width?: "full" | "half";
/** Hidden field value, or a prefill */
defaultValue?: string;
}
interface StorefrontForm {
slug: string;
name: string;
description: string | null;
fields: SiteFormField[];
settings: {
submitLabel?: string;
successMessage?: string;
redirectUrl?: string;
consentText?: string;
requireConsent?: boolean;
};
}
interface StorefrontFormSubmitInput {
data: Record<string, unknown>;
locale?: string;
page?: string;
consent?: boolean;
/** Honeypot, pass the hidden input's value through */
website?: string;
}
interface StorefrontFormSubmitResult {
ok: boolean;
id: string | null;
message: string | null;
redirectUrl: string | null;
}
interface StorefrontFormFieldError {
key: string;
code: "required" | "invalid" | "tooShort" | "tooLong" | "min" | "max" | "notOption" | "pattern";
}Shop
ShopInfo
interface ShopInfo {
id: string;
name: string;
domain: string;
logo?: string;
favicon?: string;
isActive: boolean;
defaultLanguage: string;
supportedLanguages: string[];
defaultCurrency: string;
supportedCurrencies: string[];
metaTitle?: string;
metaDescription?: string;
allowGuestCheckout: boolean;
/** B2B-only shop: guests must sign in to see prices and purchase. */
b2bMode: boolean;
/** Whether quote requests make sense for this shop (currently follows B2B mode). */
quotesEnabled: boolean;
/**
* Countries the shop delivers to (ISO 3166-1 alpha-2), derived from the enabled
* shipping methods. `null` = no restriction, `[]` = no enabled shipping method.
* Limit the checkout country select to this list. (SDK 1.15.0)
*/
shippingCountries?: string[] | null;
}ShopSeo
interface ShopSeo {
locale: string;
title: string | null;
description: string | null;
keywords: string | null;
ogTitle: string | null;
ogDescription: string | null;
ogImage: string | null;
}Errors
BehioApiError
Thrown for HTTP error responses from the API.
class BehioApiError extends Error {
readonly code: BehioErrorCode;
readonly status: number;
readonly body: unknown;
readonly isRetryable: boolean;
constructor(status: number, body: unknown, message?: string);
/** Check if this is a specific error type */
is(code: BehioErrorCode): boolean;
}BehioNetworkError
Thrown for network failures and timeouts.
class BehioNetworkError extends Error {
readonly code: "NETWORK_ERROR" | "TIMEOUT";
readonly isRetryable: true;
constructor(message: string, isTimeout?: boolean);
}BehioErrorCode
type BehioErrorCode =
| "UNAUTHORIZED"
| "FORBIDDEN"
| "NOT_FOUND"
| "VALIDATION_ERROR"
| "CONFLICT"
| "RATE_LIMITED"
| "CART_EMPTY"
| "PRODUCT_NOT_FOUND"
| "INVALID_CREDENTIALS"
| "INVALID_DISCOUNT"
| "DISCOUNT_EXPIRED"
| "TOKEN_EXPIRED"
| "TOKEN_INVALID"
| "EMAIL_ALREADY_EXISTS"
| "ORDER_NOT_CANCELLABLE"
| "INTERNAL_ERROR"
| "NETWORK_ERROR"
| "TIMEOUT"
| "UNKNOWN";Events
BehioEventType
type BehioEventType =
| "auth:login"
| "auth:logout"
| "auth:token-refresh"
| "auth:token-refresh-failed"
| "cart:updated"
| "cart:cleared"
| "order:created"
| "error"
| "request"
| "response"
| "rate-limit-warning";BehioEventHandler
type BehioEventHandler = (data?: unknown) => void;Interceptors
RequestInterceptor
interface RequestInterceptorConfig {
url: string;
method: string;
headers: Record<string, string>;
body?: string;
}
type RequestInterceptor = (
config: RequestInterceptorConfig,
) => RequestInterceptorConfig | Promise<RequestInterceptorConfig>;ResponseInterceptor
interface ResponseInterceptorData {
status: number;
data: unknown;
headers: Headers;
}
type ResponseInterceptor = (
response: ResponseInterceptorData,
) => void | Promise<void>;Pagination
PaginatedResponse
Generic wrapper for paginated list endpoints.
interface PaginatedResponse<T> {
items: T[];
total: number;
page: number;
limit: number;
totalPages: number;
}MessageResponse
interface MessageResponse {
message: string;
}SDK 1.20 additions
The SDK exports CheckoutPreview and CheckoutResult; checkout creation
returns the latter and includes the permanent trackingToken (SDK 2.7.0) plus an optional short-lived orderAccessToken for
a guest's private receipt. Do not put that token in URLs or browser-visible
props. CheckoutInput.previewToken binds the displayed review to its current
cart and choices. CheckoutInput.shippingAddress is optional only when all
cart products are configured without physical delivery.
Cart.requiresShipping includes bundle contents, and Cart.checkoutLimits
contains minimum/maximum amounts in cart currency. OrderDetail includes
requiresShipping and nullable historical financial fields paymentFee,
roundingAdjustment, loyaltyDiscount and giftCardDeducted. Null means an
older order did not snapshot that component; do not replace it with current
merchant settings.
ShopInfo includes optional appearance, maintenance, commerce, timezone,
account and currencyDisplayFormat. ShopInfo.checkout includes checkoutLayout,
showCartPriceBreakdown, showEstimatedDelivery and orderConfirmationText.
See merchant settings for their behavior.
DigitalDownload.isRevoked optionally reports an inactive asset or an order
that no longer grants access. Disable the download action for revoked, expired
or exhausted grants. The server rechecks these conditions atomically.