TypeScript
@shopimind/sdk-js is written in TypeScript and ships its .d.ts declarations directly in the package. There is nothing extra to install: no @types/... package, no stubs. As soon as you import it, your IDE offers autocompletion for the resources, methods, and fields of every DTO.
ESM only
The SDK is ESM only (Node.js 18+). Import it with import; require() is not supported. See → Installation.
import {
SpmClient,
SpmCustomers,
type SpmCustomerData,
type SpmEnvelope,
type SpmChunkedEnvelope,
SpmHelpers,
} from '@shopimind/sdk-js';
// The client is authenticated by the shop's API key (spm-api-key header)
const client = SpmClient.getClient('v1', process.env.SHOPIMIND_API_KEY!);
const items: SpmCustomerData[] = [/* … */];
const res: SpmEnvelope | SpmChunkedEnvelope =
await SpmCustomers.bulkSave(client, items, { chunk: true });
if (res.ok) {
const counts = SpmHelpers.extractCounts(res);
console.log(`${counts.sent} sent, ${counts.rejected} rejected`);
}The typed envelope SpmEnvelope<T>
Every SDK method returns the same envelope, on both success and failure — the SDK never throws on an HTTP error. It is generic: SpmEnvelope<T> lets you type the expected response body.
interface SpmEnvelope<T = unknown> {
ok: boolean; // true on HTTP 2xx
statusCode: number; // HTTP status; 0 on network failure
data: T | null; // API body (or null)
error: SpmEnvelopeError | null;
}
interface SpmEnvelopeError {
message: string;
code: string; // 'HTTP_503', 'ETIMEDOUT', 'PARTIAL_FAILURE'…
retryable: boolean;
attempts: number;
details?: unknown;
}Double-nested business payload
The ShopiMind API already wraps its responses in { statusCode, data }. The SDK places this whole body into envelope.data. The business payload is therefore at res.data.data, not at res.data. Keep this in mind when you set the T generic.
Automatic chunking (chunk: true) returns a typed variant of the envelope, SpmChunkedEnvelope, whose data aggregates the counters of all batches:
type SpmChunkedEnvelope = SpmEnvelope<SpmChunkedData>;
interface SpmChunkedData {
sent_count: number;
rejected_count: number;
failed_count: number;
rejected_items: unknown[];
chunks: SpmChunkSummary[];
}The client options, typed
SpmClient.getClient(apiVersion, apiKey, options) accepts a third typed argument SpmClientOptions:
interface SpmClientOptions {
headers?: Record<string, string>;
baseUrl?: string; // default https://core.shopimind.com (or env SHOPIMIND_CORE_API_BASE)
timeout?: number; // default 30000 (ms)
labelSource?: string | null; // default 'web'; null disables label_source injection
retry?: SpmRetryOptions | false; // default 3 retries; false disables
}Real DTOs (data interfaces)
Each syncable entity exposes a dedicated interface, exported by the SDK. The field names match exactly the body expected by the API. All these interfaces extend SpmDataSourceAttributes (id_data_source?, source_label?) to attach a piece of data to a source.
interface SpmDataSourceAttributes {
id_data_source?: number;
source_label?: string;
}SpmCustomerData
interface SpmCustomerData extends SpmDataSourceAttributes {
customer_id: string;
shop_id?: string | null;
email: string;
phone_number?: string | null;
first_name: string;
last_name: string;
birth_date?: string | null;
is_opt_in: boolean;
is_newsletter_subscribed: boolean;
lang: string;
group_ids?: string[] | null;
is_active: boolean;
created_at: string;
updated_at: string;
}SpmProductData
interface SpmProductData extends SpmDataSourceAttributes {
product_id: number;
shop_id?: string | null;
lang: string;
name: string;
reference?: string | null;
ean13?: string | null;
description: string;
description_short?: string | null;
link: string;
image_link?: string | null;
category_ids?: number[] | null;
manufacturer_id?: string | null;
currency: string;
price: number;
price_discount?: number | null;
quantity_remaining: number;
is_active: boolean;
created_at: string;
updated_at: string;
}SpmOrderData
An order embeds its customer (SpmOrderCustomer) and its lines (SpmOrderProduct[]):
interface SpmOrderData extends SpmDataSourceAttributes {
order_id: string;
shop_id?: string | null;
lang: string;
reference?: string | null;
carrier_id?: string | null;
status_id: string;
address_delivery_id?: string | null;
address_invoice_id?: string | null;
customer: SpmOrderCustomer;
products: SpmOrderProduct[];
cart_id: string;
cart_updated_at: string;
amount: number;
amount_without_tax: number;
shipping_costs: number;
shipping_costs_without_tax: number;
shipping_number?: string | null;
currency: string;
voucher_used?: string | null;
voucher_value?: string | null;
is_confirmed: boolean;
created_at: string;
updated_at: string;
}
interface SpmOrderCustomer {
customer_id: string;
email: string;
created_at: string;
}
interface SpmOrderProduct {
product_id: number;
product_variation_id?: number | null;
price: number;
price_without_tax: number;
manufacturer_id?: string | null;
quantity: number;
}Other exported interfaces
The SDK exports one DTO per entity. They are all importable as types from @shopimind/sdk-js:
| Domain | Interfaces |
|---|---|
| Shop | SpmShopConnectionData |
| Customers | SpmCustomerData, SpmCustomerAddressData, SpmBulkCustomerAddressData, SpmCustomerGroupData |
| Products | SpmProductData, SpmProductCategoryData, SpmProductImageData, SpmBulkProductImageData, SpmProductManufacturerData, SpmProductVariationData, SpmBulkProductVariationData |
| Orders | SpmOrderData, SpmOrderCustomer, SpmOrderProduct, SpmOrderCarrierData, SpmOrderStatusData |
| Vouchers | SpmVoucherData |
| Data sources | SpmDataSourceData, SpmCreateDataSourceData |
| Custom data | SpmCustomDataFieldDef, SpmCustomDataRelationshipDef, SpmCreateCustomDataDefinitionData, SpmExtendCustomDataDefinitionData, SpmCustomDataRecord |
| Events | SpmCreateEventData, SpmUpdateEventData, SpmTriggerEventPayload |
| Lists / Contacts | SpmCreateListData, SpmUpdateListData, SpmContactGetParams |
| SDK infra | SpmEnvelope<T>, SpmEnvelopeError, SpmChunkedEnvelope, SpmChunkedData, SpmClientOptions, SpmBulkOptions, SpmRetryOptions, SpmHttpClient, SpmApiError |
| Security | SpmWebhookSignature (result SpmWebhookVerifyResult), SpmRequestValidator (result SpmValidationResult) |
→ The exhaustive list of resources and their methods: Resources.
Helpers and exception mode (unwrapOrThrow)
SpmHelpers.unwrapOrThrow is an opt-in exception mode: it unwraps the double-nesting (res.data.data) and throws SpmApiError when the envelope is not ok. SpmApiError carries statusCode, code, and the original envelope (envelope), and is thrown only by unwrapOrThrow.
import { SpmClient, SpmCustomers, SpmHelpers, SpmApiError } from '@shopimind/sdk-js';
const client = SpmClient.getClient('v1', process.env.SHOPIMIND_API_KEY!);
try {
// unwrapOrThrow unwraps res.data.data and throws SpmApiError when !ok
const customer = SpmHelpers.unwrapOrThrow(await SpmCustomers.get(client, 'cust_1'));
console.log(customer.email);
} catch (err) {
if (err instanceof SpmApiError) {
console.error(err.statusCode, err.code, err.envelope);
}
}Security primitives, typed
The SDK exposes two signature-verification primitives, each with its own result type:
SpmWebhookSignature— integration channel: timestamped HMAC-SHA256 with a replay window (x-shopimind-timestamp/x-shopimind-signatureheaders,verifyFromHeadershelper). Returns anSpmWebhookVerifyResult.SpmRequestValidator— connector channel: sorted "imploded" body + HMAC-SHA256 with constant-time comparison (Shopimind-Token/Shopimind-Client-Identifiantheaders), used by the CMS connectors. Returns anSpmValidationResult.
End-to-end typed example
The compiler validates the shape of each order, the API key is read from the environment, and the result is typed via SpmChunkedEnvelope:
import {
SpmClient,
SpmOrders,
type SpmOrderData,
type SpmChunkedEnvelope,
} from '@shopimind/sdk-js';
const client = SpmClient.getClient('v1', process.env.SHOPIMIND_API_KEY!, {
retry: { maxRetries: 5 },
});
const orders: SpmOrderData[] = [
{
order_id: 'ord_1',
lang: 'fr',
reference: 'CMD-2026-0001',
status_id: 'paid',
customer: { customer_id: 'cust_1', email: 'client@example.com', created_at: '2026-01-01T09:00:00Z' },
products: [
{ product_id: 42, price: 19.9, price_without_tax: 16.58, quantity: 2 },
],
cart_id: 'cart_1',
cart_updated_at: '2026-01-01T09:00:00Z',
amount: 39.8,
amount_without_tax: 33.16,
shipping_costs: 4.9,
shipping_costs_without_tax: 4.08,
currency: 'EUR',
is_confirmed: true,
created_at: '2026-01-01T09:05:00Z',
updated_at: '2026-01-01T09:05:00Z',
},
];
const res: SpmChunkedEnvelope = await SpmOrders.bulkSave(client, orders, { chunk: true });
if (!res.ok) {
throw new Error(res.error!.message); // nothing is thrown by the SDK: test res.ok
}
console.log(`Orders sent: ${res.data!.sent_count}`);TypeScript configuration
Since the SDK is ESM, use "module": "NodeNext" (or ESNext) and "moduleResolution": "NodeNext" in your tsconfig.json. The .d.ts declarations are resolved automatically via the package's exports field.
Next: Scope and versioning.