@ab-ecosystem/aistore-admin
v0.1.0
Published
Headless ecommerce admin API SDK for AI Store - a typed TypeScript REST client for products, orders, customers, inventory, shipping, taxes, themes and webhooks, over the SSO /dashboard and api-key /api/dashboard endpoints.
Maintainers
Keywords
Readme
@ab-ecosystem/aistore-admin
TypeScript SDK and REST API client for AI Store — the headless ecommerce admin API. Manage products, orders, customers, inventory, pricing, shipping, taxes, themes and webhooks from Node.js, the browser or an edge runtime, with full type safety and zero runtime dependencies.
Built for headless commerce: a merchant dashboard, a back-office tool, a storefront build pipeline, a WhatsApp or voice-AI sales bot, or any server-to-server ecommerce integration can drive the same typed admin API.
client.dashboard.*→ the/dashboardrouter, authenticated with an SSO access token (JWT). This is the merchant dashboard surface.client.api.*→ the/api/dashboardrouter, authenticated with a store API key. This is the integration and automation surface.
Table of contents
- Features
- Installation
- Quick start
- Configuration options
- Authentication
- API reference
- Response envelope and pagination
- Per-call options
- File uploads and CSV import
- Error handling
- Calling unwrapped endpoints
- Use cases
- Requirements and compatibility
- FAQ
- Known server quirks
Features
- Complete admin API coverage — every client-facing route on both admin routers: catalog, orders, carts, customers, segments, pricing rules, taxes, shipping zones, custom fields, warehouses, themes, webhooks, media uploads, payment methods and plans.
- Typed end to end — TypeScript definitions for every method, with generics so you can type each response payload:
admin.api.products.fetch<MyProduct>(). - Dual authentication — SSO JWT for dashboard routes and store API key for integration routes, resolved per request from a string or an async token provider.
- Zero runtime dependencies — nothing but the platform
fetch. Nothing to audit, nothing to patch. - ESM and CommonJS — dual build with
importandrequireentry points, plussideEffects: falsefor tree-shaking. - Multi-channel catalog sync — pull from WooCommerce, Shopify, Meta and Zid; push catalogs back to Meta.
- Typed errors — distinguish API, configuration, network and timeout failures without string matching.
- Cancellation and timeouts — per-client and per-call
timeoutMs, plusAbortSignalsupport on every method. - Pluggable fetch — inject
undici, a polyfill, or a wrapper that adds logging, tracing or retries.
Installation
npm install @ab-ecosystem/aistore-adminpnpm add @ab-ecosystem/aistore-adminyarn add @ab-ecosystem/aistore-adminQuick start
import { AIStoreAdmin, AIStoreApiError } from "@ab-ecosystem/aistore-admin";
const admin = new AIStoreAdmin({
baseUrl: "https://api.your-domain.com",
accessToken: () => getSsoToken(), // string or (async) function → used by client.dashboard.*
apiKey: process.env.AISTORE_API_KEY, // string or (async) function → used by client.api.*
timeoutMs: 15_000,
});
// Merchant dashboard (SSO token)
const { data: products, total } = await admin.dashboard.products.fetch({ status: "publish", page: 1, size: 20 });
await admin.dashboard.orders.update({ orderId, order_status: "shipped" });
// Integrations and automations (API key)
const orders = await admin.api.orders.fetch({ page: 1, size: 50 });
await admin.api.products.create({ name: "Mug", price: 299, quantity: 10, image_urls: [url] });
try {
await admin.dashboard.store.fetch();
} catch (err) {
if (err instanceof AIStoreApiError) console.error(err.status, err.code, err.message, err.body);
}CommonJS works the same way:
const { AIStoreAdmin } = require("@ab-ecosystem/aistore-admin");Configuration options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| baseUrl | string | — | Required. Server origin, e.g. https://api.example.com. Router prefixes are added by the SDK. |
| accessToken | string or token provider | — | SSO access token (JWT) for /dashboard routes. A function is resolved on every request, so token refresh needs no client rebuild. |
| apiKey | string or token provider | — | Store API key for /api/dashboard routes. |
| bearerPrefix | boolean | false | Prefix the Authorization value with Bearer . Off by default because the server verifies the raw header value as the JWT. |
| fetch | typeof fetch | global fetch | Custom fetch implementation for logging, tracing, retries or polyfills. |
| timeoutMs | number | none | Default per-request timeout. Overridable per call. |
| headers | Record<string, string> | — | Extra headers sent with every request. |
A token provider is any () => string | Promise<string>. Read the current origin with admin.baseUrl.
Authentication
| Namespace | Header | Option | Notes |
| --- | --- | --- | --- |
| client.dashboard.* | Authorization: <jwt> | accessToken | Raw JWT, no Bearer prefix. Set bearerPrefix: true if your deployment expects one. |
| client.api.* | api-key: <key> | apiKey | Issue a key with dashboard.account.refreshApiKey(); rotating invalidates the old one. |
Both credentials accept an async provider, which is the recommended pattern for short-lived SSO tokens:
const admin = new AIStoreAdmin({
baseUrl,
accessToken: async () => (await session.getValidToken()).jwt,
});Swap credentials at runtime without constructing a new client:
admin.setAccessToken(newJwt);
admin.setApiKey(rotatedKey);A few /dashboard routes take the API key instead of the SSO token; they are marked api-key in their JSDoc (restaurant.createConfig, restaurant.fetchAvailability, restaurant.createBooking, restaurant.updateBooking). Public routes send no credential at all (account.searchAppCode, account.addWhitelistUrl, theme.fetchCss, theme.fetchStaticFile, theme.fetchDefaultThemes, theme.fetchAgencyTheme).
Calling a method without the credential it needs throws AIStoreConfigError before any request leaves the process.
API reference
client.dashboard — merchant dashboard endpoints (/dashboard)
| Resource | Methods |
| --- | --- |
| account | overview, refreshApiKey, createNewStore, searchAppCode, addWhitelistUrl |
| warehouse | create, fetch, searchLocation, updateShippingCost |
| pushTokens | register, remove |
| products | fetch, create, update, delete, fetchVariants, createFromCsv, downloadFullCsv, fetchForBulkEdit, syncHistory |
| categories | fetch, create, update, delete, moveAndDelete |
| options | fetch, create, update, delete |
| orders | fetch, getById, delete, update; orders.notes.{fetch,create,delete}; orders.disputes.{fetchByOrder,update,fetchPaymentStatus,updatePayment} |
| carts | fetch |
| store | create, fetch, update, loadDetails |
| storeSettings | fetch, update, generateMcpApiKey, disableMcpApiKey |
| theme | fetch, update, fetchSkeleton, fetchStaticFile, fetchCss, fetchAdminSkeleton, updateAiFeatures, fetchAgencyTheme, fetchSkeletonThemes, saveAsNewTheme, activate, fetchDefaultThemes |
| webhooks | create, fetch, delete, updateStatus |
| customers | fetch, create, uploadCsv, assignSegment; customers.segments.{create,fetch,edit}; customers.pricingRules.{create,update,fetch,fetchForProduct} |
| taxes | create, fetch, edit, delete |
| shipping | shipping.zones.{create,fetch,getById,edit,delete}; shipping.classes.{create,fetch,update} |
| customFields | create, fetch, edit, delete; customFields.values.{create,fetch,edit,delete} |
| location | getStreetAddress |
| media | getPresignedUrl |
| quickFilters | create, update, delete, fetch, fetchProductsForPicker, fetchProductsByFilters |
| productSync | pullFromWooCommerce, pullFromMeta, pushToMeta, initShopifySync, pullFromZid |
| connectors | fetch, connect, disconnect; connectors.meta.{getBusinessProfiles,getCatalogueLists,saveCatalogue}; connectors.shopify.{getAuthUrl,connect,subscribeWebhook,fetchDetails,update,deleteStore}; connectors.zid.redirect |
| plans | upgrade |
| paymentMethods | connect, fetch, updateStatus |
| restaurant | createConfig, updateConfig, fetchConfig, fetchDailySlots, updateDailySlots, fetchAvailability, createBooking, updateBooking, fetchBookedTables |
| aiChat | callLlm, fetchChats, fetchMessage |
client.api — integration endpoints (/api/dashboard)
| Resource | Methods |
| --- | --- |
| products | fetch, create, update, delete, fetchVariants, fetchByCategory, createFromCsvFile (multipart) |
| carts | fetch |
| categories | fetch, create, update, delete, moveAndDelete |
| options | fetch, create, update, delete |
| orders | fetch, getById, delete, create, update |
| store | create, fetch, update, loadDetails |
| theme | fetch, update, fetchSkeleton, fetchStaticFile, fetchCss |
| webhooks | create, fetch, delete, updateStatus |
| automation | imageSearch, textSearch |
| media | paymentDisputePresignedUrl |
Each method's JSDoc names the exact server path, including the server's own spelling quirks such as /update-shpping-cost.
Response envelope and pagination
Every method returns the server's JSON envelope, typically { status, message?, data?, total? }. Pass a type argument to type data:
const { data, total } = await admin.api.products.fetch<MyProduct[]>({ page: 1, size: 50 });List endpoints take page and size and report total, so paging is a plain loop:
const size = 100;
for (let page = 1; ; page++) {
const { data, total } = await admin.api.orders.fetch({ page, size });
if (!data?.length) break;
await handle(data);
if (page * size >= (total ?? 0)) break;
}Per-call options
Every method accepts a trailing options object:
await admin.dashboard.products.fetch({ page: 1 }, {
signal: controller.signal, // AbortSignal
timeoutMs: 5_000, // overrides the client default
headers: { "x-request-id": id }, // extra headers
responseType: "text", // "auto" (default) | "json" | "text" | "blob"
});responseType: "blob" is the one you want for theme assets and CSV downloads such as dashboard.products.downloadFullCsv().
File uploads and CSV import
// Browser
await admin.api.products.createFromCsvFile(fileInput.files[0]);
// Node 18+
import { openAsBlob } from "node:fs";
await admin.api.products.createFromCsvFile(await openAsBlob("./products.csv"), { fileName: "products.csv" });For images and other media, request a presigned URL and upload directly to storage:
const { data } = await admin.dashboard.media.getPresignedUrl({ fileName: "hero.jpg", fileType: "image/jpeg" });Error handling
| Class | When it is thrown |
| --- | --- |
| AIStoreApiError | Non-2xx response. Carries status, code (e.g. SESSION_EXPIRED, invalid_token), body, headers and request. |
| AIStoreConfigError | Missing baseUrl, or a credential the route needs. Thrown before the request is sent. |
| AIStoreNetworkError | Fetch itself failed — DNS, CORS, offline. |
| AIStoreTimeoutError | timeoutMs elapsed. |
All four extend AIStoreError, so a single instanceof check catches everything this SDK throws:
import { AIStoreError, AIStoreApiError, AIStoreTimeoutError } from "@ab-ecosystem/aistore-admin";
try {
await admin.dashboard.orders.fetch({ page: 1 });
} catch (err) {
if (err instanceof AIStoreApiError && err.status === 401) return refreshSsoAndRetry();
if (err instanceof AIStoreTimeoutError) return queueForLater();
if (err instanceof AIStoreError) return report(err);
throw err;
}Calling unwrapped endpoints
New server routes are reachable before the SDK wraps them. path is relative to baseUrl and must include the router prefix:
await admin.request("POST", "/dashboard/some-new-route", { body: payload, auth: "sso" });
await admin.request("GET", "/api/dashboard/some-new-route", { auth: "apiKey" });Use cases
- Headless ecommerce storefronts — fetch catalog, categories and theme assets at build time or per request for Next.js, Nuxt, Astro, SvelteKit or a static site generator.
- Custom merchant dashboards — build an admin panel on the SSO-authenticated
/dashboardsurface, the same API the first-party dashboard uses. - Conversational and voice commerce — back a WhatsApp catalog, chatbot or voice-AI agent with live products, carts and order creation.
- ERP, POS and accounting sync — reconcile orders, inventory and customers from a scheduled job using a store API key.
- Platform migration and multi-channel listing — import an existing catalog from WooCommerce, Shopify or Zid, and publish it to Meta commerce catalogs.
- Bulk catalog operations — CSV import and export, bulk edit, quick filters and product variant management.
- Restaurant and appointment booking — table configuration, daily slots, availability checks and reservations.
- AI-assisted retail — image and text product search, AI chat, and MCP API keys for agent-driven store access.
Requirements and compatibility
| | |
| --- | --- |
| Node.js | 18 or newer (needs the built-in fetch) |
| Browsers | Any modern browser; CORS must allow your origin |
| Edge and serverless | Any runtime providing the WHATWG fetch, Headers and AbortSignal |
| Module formats | ESM (import) and CommonJS (require) |
| TypeScript | Types bundled; no @types package needed |
| Runtime dependencies | None |
On Node 16 or an older browser target, pass a polyfill through the fetch option.
FAQ
Is this a headless ecommerce SDK? Yes. The storefront and the admin surface are fully decoupled from the API, so you can build any frontend — web, mobile, chat or voice — against these typed endpoints.
What is the difference between client.dashboard and client.api?
client.dashboard maps to /dashboard and authenticates a logged-in merchant with an SSO JWT; use it for interactive dashboards. client.api maps to /api/dashboard and authenticates a store with a long-lived API key; use it for servers, cron jobs and third-party integrations.
Can I use it in the browser?
Yes, and that is the intended setup for a dashboard UI. Keep the store API key out of browser bundles — it is a long-lived store-wide credential, so confine client.api to server-side code.
Does it retry failed requests?
No. Retry policy is deliberately left to you, because the right behavior differs between an interactive dashboard and a background sync. Wrap the fetch option to add retries and backoff in one place.
How do I handle an expired SSO token?
Pass accessToken as an async function. It is resolved on every request, so returning a freshly refreshed JWT is enough. Alternatively call setAccessToken() after a refresh.
How do I rotate the store API key?
Call dashboard.account.refreshApiKey(), then admin.setApiKey(newKey). The previous key stops working immediately.
Is TypeScript required? No. The package ships compiled JavaScript for both ESM and CommonJS; the types are a bonus if you use them.
How do I type a response?
Pass a type argument to any method: admin.api.orders.fetch<MyOrder[]>({ page: 1 }).
Known server quirks
dashboard.customFields.fetch({ type })— the server readstypefrom the request body on a GET. The SDK sends it as a query parameter, so the list stays unfiltered until the server reads the query.dashboard.products.fetchForBulkEdithas no auth middleware on the server.
License
ISC
