E-shop
Products, services, providers, orders, checkout, and payment processing
The E-shop module provides complete commerce functionality including product management, service scheduling, carts, order processing, and checkout.
For storefronts, prefer the high-level store from arky-sdk/storefront. It wraps the lower-level methods on this page and keeps CMS state, e-shop state, contact session, cart, quote, checkout, and scheduled service state in one reactive API.
import { initialize } from "arky-sdk/storefront";
const arky = initialize("arky_pk_...", {
market: "us",
locale: "en",
});
await arky.eshop.cart.load();
await arky.eshop.cart.addProduct(product, variant, 1);
const quote = await arky.eshop.cart.quote();
const order = await arky.eshop.cart.checkout({ payment_method_key: "cash" });
Products
Create Product
/v1/stores/{storeId}/products sdk.eshop.product.create() Create a new product. New products start in the active state.
const result = await sdk.eshop.product.create({
key: 'premium-widget',
slug: { en: 'premium-widget' },
taxonomies: [
{ taxonomy_id: 'tax_category', entry_ids: ['ent_widgets'] }
],
blocks: [
{
key: 'details',
id: 'details',
type: 'localized_text',
properties: {},
value: { en: 'Product specifications...' }
}
],
variants: [
{
sku: 'WIDGET-SM',
prices: [{ market: 'us', currency: 'usd', amount: 2999 }],
inventory: [{ location_id: 'loc_warehouse', available: 100 }],
attributes: [],
weight: 500
},
{
sku: 'WIDGET-LG',
prices: [{ market: 'us', currency: 'usd', amount: 3999 }],
inventory: [{ location_id: 'loc_warehouse', available: 50 }],
attributes: [],
weight: 750
}
]
});Parameters
| Name | Type | Description |
|---|---|---|
key required | string | Unique product key identifier |
slug optional | Record<string, string> | Locale-keyed URL slugs (e.g., {en: 'my-product'}) |
blocks optional | Block[] | Content blocks for product details |
taxonomies optional | TaxonomyEntry[] | Taxonomy entries the product belongs to (category, collection, etc.) |
variants optional | ProductVariant[] | Product variants, prices, inventory, tax, weight, and shipping policy |
Get Product
/v1/stores/{storeId}/products/{id} sdk.eshop.product.get() Retrieve the lean catalog product by ID or slug. Variants include prices and catalog configuration, but not location stock. Fetch inventory explicitly when the view needs it.
// By ID
const result = await sdk.eshop.product.get({
id: "prod_xyz789",
});
// By slug (locale-aware)
const result = await sdk.eshop.product.get({
slug: "premium-widget",
});
Parameters
| Name | Type | Description |
|---|---|---|
id optional | string | Product ID (use this OR slug) |
slug optional | string | Product slug (locale-aware lookup) |
Get Product Inventory
/v1/stores/{storeId}/products/{id}/inventory sdk.eshop.product.getInventory() Inventory changes independently from product content and is therefore a separate resource. Load it only for stock-aware detail, cart, or operator views.
const [product, inventory] = await Promise.all([
sdk.eshop.product.get({ id: "prod_xyz789" }),
sdk.eshop.product.getInventory({ id: "prod_xyz789" }),
]);
const selectedStock = inventory.filter(
(row) => row.variant_id === product.variants[0]?.id,
);
List Products
/v1/stores/{storeId}/products sdk.eshop.product.find() List products with filtering and pagination.
const result = await sdk.eshop.product.find({
ids: ['prod_1', 'prod_2'],
taxonomyQuery: [
{ taxonomy_id: 'tax_category', entry_ids: ['ent_widgets'] }
],
match_all: true,
status: 'active',
query: 'premium',
sort_field: 'created_at',
sort_direction: 'desc',
cursor: null,
limit: 20,
created_at_from: 1704067200,
created_at_to: 1706745600
});
const { items, cursor } = result;
items.forEach(product => {
console.log(product.key, product.variants);
});Parameters
| Name | Type | Description |
|---|---|---|
ids optional | string[] | Filter by specific product IDs |
taxonomy_query optional | TaxonomyQuery[] | Filter by taxonomy entries (category, collection, etc.) |
match_all optional | boolean | If true, match all taxonomy filters (AND); if false, match any (OR) |
status optional | active | draft | archived | Filter by product status |
query optional | string | Search query in product content |
sort_field optional | string | Sort field (createdAt, updatedAt, etc.) |
sort_direction optional | asc | desc | Sort direction |
cursor optional | string | Pagination cursor |
limit optional | number | Items per page (max 100) |
created_at_from optional | number | Filter by creation date (Unix timestamp) |
created_at_to optional | number | Filter by creation date (Unix timestamp) |
Update Product
/v1/stores/{storeId}/products/{id} sdk.eshop.product.update() Update an existing product. Use status to draft, archive, or re-activate a product.
const result = await sdk.eshop.product.update({
id: "prod_xyz789",
key: "updated-widget",
slug: { en: "updated-widget", es: "widget-actualizado" },
taxonomies: [
{ taxonomy_id: "tax_category", entry_ids: ["ent_widgets", "ent_sale"] },
],
blocks: [/* updated blocks */],
variants: [
{
id: "var_existing",
sku: "WIDGET-SM-V2",
prices: [{ market: "us", currency: "usd", amount: 3499 }],
inventory: [{ location_id: "loc_warehouse", available: 150 }],
attributes: [],
weight: 500,
},
],
status: "active",
});
Parameters
| Name | Type | Description |
|---|---|---|
id required | string | Product ID to update |
key optional | string | Product key identifier |
slug optional | Record<string, string> | Locale-keyed URL slugs |
blocks optional | Block[] | Content blocks |
taxonomies optional | TaxonomyEntry[] | Taxonomy entries the product belongs to |
variants optional | ProductVariant[] | Product variants, prices, inventory, tax, weight, and shipping policy |
status optional | active | draft | archived | Product status |
Delete Product
/v1/stores/{storeId}/products/{id} sdk.eshop.product.delete() Delete a product.
await sdk.eshop.product.delete({
id: "prod_xyz789",
});
Services
Services are schedulable catalog items. Providers supply capacity and working rules for those services.
Create Service
/v1/stores/{storeId}/services sdk.eshop.service.create() const service = await sdk.eshop.service.create({
key: "deep-cleaning",
slug: { en: "deep-cleaning" },
blocks: [/* service content */],
});
List Services
/v1/stores/{storeId}/services sdk.eshop.service.find() const { items: services } = await sdk.eshop.service.find({
query: "cleaning",
statuses: ["active"],
limit: 20,
});
Connect Providers
/v1/stores/{storeId}/service-providers sdk.eshop.service.createProvider() await sdk.eshop.service.createProvider({
service_id: service.id,
provider_id: provider.id,
working_days: [
{
day: "monday",
windows: [{ from: "09:00", to: "17:00" }],
},
],
specific_dates: [],
durations: [{ duration: 60 }],
prices: [{ market: "us", currency: "usd", amount: 12000 }],
slot_interval: 30,
});
Providers
Create Provider
/v1/stores/{storeId}/providers sdk.eshop.provider.create() const provider = await sdk.eshop.provider.create({
key: "north-team",
slug: { en: "north-team" },
blocks: [/* provider contact */],
});
List Providers
/v1/stores/{storeId}/providers sdk.eshop.provider.find() const { items: providers } = await sdk.eshop.provider.find({
service_id: service.id,
limit: 20,
});
Orders
Carts
/v1/storefront/carts/current arky.client.eshop.cart.current() Get or create the authenticated contact’s active cart. Orders are created by checking out a cart; every order stores source_cart_id.
const cart = await arky.client.eshop.cart.current();
await arky.client.eshop.cart.update({
id: cart.id,
market: "us",
product_items: [
{
product_id: "prod_xyz789",
variant_id: "var_small",
quantity: 2,
},
],
booking_items: [
{
service_id: "svc_cleaning",
provider_id: "prv_north",
slots: [{ from: 1780300800, to: 1780304400 }],
},
],
shipping_address: {
name: "Jane Doe",
street1: "456 Contact Ave",
city: "New York",
state: "NY",
postal_code: "10001",
country: "US",
},
forms: [
{
key: "shipping-notes",
entries: [{ key: "note", value: "Leave at door" }],
},
],
});
const quote = await arky.client.eshop.cart.quote({ id: cart.id });
const result = await arky.client.eshop.cart.checkout({
id: cart.id,
payment_method_key: "cash",
});
Parameters
| Name | Type | Description |
|---|---|---|
id required | string | Cart ID |
market optional | string | Market key, for example us or eu |
product_items optional | CartProduct[] | Cart product lines with product_id, variant_id, and quantity |
booking_items optional | CartBooking[] | Scheduled-service selections with service_id, provider_id, and slots |
shipping_address optional | Address | Shipping address |
billing_address optional | Address | Billing address |
forms optional | FormEntry[] | Cart form entries (contact info, notes, etc.) |
payment_method_key optional | string | Market payment method key such as 'cash' or 'credit_card', supplied at checkout or saved on the cart |
Get Order
/v1/stores/{storeId}/orders/{id} sdk.eshop.order.get() Retrieve an order by ID.
const order = await sdk.eshop.order.get({
id: "ord_xyz789",
});
const [payment, products, bookings, digitalProducts] = await Promise.all([
sdk.eshop.order.getPayment({ order_id: order.id }),
sdk.eshop.order.getProducts({ id: order.id }),
sdk.eshop.order.getBookings({ id: order.id }),
sdk.eshop.order.getDigitalProducts({ id: order.id }),
]);
console.log(order.status, payment.status, order.fulfillment_status);
Parameters
| Name | Type | Description |
|---|---|---|
id required | string | Order ID |
List Orders
/v1/stores/{storeId}/orders sdk.eshop.order.find() List lean orders with filtering. status is the order lifecycle and fulfillment_status is its
bounded summary. Payment, products, bookings, and digital products are separate resources; request
only the children a detail screen needs.
const result = await sdk.eshop.order.find({
contact_id: 'acc_contact123',
statuses: ['confirmed'],
product_statuses: ['confirmed'],
booking_statuses: ['confirmed'],
product_ids: ['prod_xyz789'],
service_ids: ['svc_consultation'],
provider_ids: ['provider_123'],
query: 'john@example.com',
sort_field: 'created_at',
sort_direction: 'desc',
cursor: null,
limit: 50,
created_at_from: 1704067200,
created_at_to: 1735689600
});
result.items.forEach(order => {
console.log(order.id, order.status, order.payment_id, order.fulfillment_status, order.money.total);
});Parameters
| Name | Type | Description |
|---|---|---|
contact_id optional | string | Filter by contact ID |
statuses optional | string[] | Filter by order lifecycle status: pending, confirmed, partially_cancelled, cancelled |
product_statuses optional | string[] | Filter by product-line status: pending, confirmed, cancelled |
booking_statuses optional | string[] | Filter by booking status: pending, confirmed, completed, no_show, cancelled |
product_ids optional | string[] | Filter by products in order |
service_ids optional | string[] | Filter by scheduled services in order |
provider_ids optional | string[] | Filter by scheduled-service providers in order |
verified optional | boolean | Filter by order verification state |
audience_id optional | string | Filter by audience |
query optional | string | Search query |
sort_field optional | string | Sort field |
sort_direction optional | asc | desc | Sort direction |
cursor optional | string | Pagination cursor |
limit optional | number | Items per page |
created_at_from optional | number | Filter by creation date (Unix timestamp) |
created_at_to optional | number | Filter by creation date (Unix timestamp) |
Update Order
/v1/stores/{storeId}/orders/{id} sdk.eshop.order.update() Update an order’s confirmation/cancellation state, addresses, form entries, products, or bookings.
Direct cancellation is only available while the order has no unreversed payment, refund, fulfillment, or shipment effects. Refund a captured payment through the refund API and reverse any other owning lifecycle before cancelling the order.
// Confirm a pending order
await sdk.eshop.order.update({
id: 'ord_xyz789',
version: 4,
confirm: true
});
// Cancel an order
await sdk.eshop.order.update({
id: 'ord_xyz789',
version: 4,
cancel: true
});
// Update addresses / form entries
await sdk.eshop.order.update({
id: 'ord_xyz789',
version: 4,
shipping_address: {
name: 'Jane Doe',
street1: '456 Contact Ave',
city: 'New York',
state: 'NY',
postal_code: '10001',
country: 'US'
},
forms: [
{ key: 'notes', entries: [{ key: 'note', value: 'Leave at door' }] }
],
product_items: [
{ product_id: 'prod_xyz', variant_id: 'var_1', quantity: 2 }
],
booking_items: [
{
service_id: 'svc_cleaning',
provider_id: 'prv_north',
slots: [{ from: 1780300800, to: 1780304400 }]
}
]
});Parameters
| Name | Type | Description |
|---|---|---|
id required | string | Order ID to update |
version required | number | Current Order version returned by the latest read. A stale version returns HTTP 409. |
confirm optional | boolean | Confirm pending products and bookings when payment rules allow it |
cancel optional | boolean | Cancel an order with no unreversed lifecycle effects and release reserved availability. Captured payments must be refunded first. |
shipping_address optional | Address | null | Shipping address (pass null to clear) |
billing_address optional | Address | null | Billing address (pass null to clear) |
forms optional | FormEntry[] | Order form entries |
product_items optional | TrustedCartProductInput[] | Replacement product lines for the order |
booking_items optional | TrustedCartBookingInput[] | Replacement scheduled-service bookings for the order |
status and fulfillment_status are returned on the lean order. The order’s
payment_id points to its one separate payment; load that payment only when the
view needs payment lifecycle. Fulfillment summarizes physical shipments and
scheduled-service work.
Order Payment and Operations
Use the order-scoped APIs when you need payment or independent operation detail:
| Resource | Route | SDK |
| --- | --- | --- |
| Payment | GET /v1/stores/{storeId}/orders/{orderId}/payment | sdk.eshop.order.getPayment() |
| Refunds | GET /v1/stores/{storeId}/orders/{orderId}/refunds | sdk.eshop.order.getRefunds() |
| Refund | GET /v1/stores/{storeId}/orders/{orderId}/refunds/{refundId} | sdk.eshop.order.getRefund() |
| Disputes | GET /v1/stores/{storeId}/orders/{orderId}/disputes | sdk.eshop.order.getDisputes() |
| Dispute | GET /v1/stores/{storeId}/orders/{orderId}/disputes/{disputeId} | sdk.eshop.order.getDispute() |
Create a partial or full refund with a caller-owned stable ID. Reuse the same ID and exact payload after an interrupted request; never generate a fresh ID in an automatic retry.
const refund = await sdk.eshop.order.createRefund({
order_id: "ord_xyz789",
refund_id: crypto.randomUUID(),
amount: 2500,
});
Payment, refunds, and disputes are not generic transaction-log rows. The one OrderPayment owns checkout and settlement; zero-to-many refunds and disputes own their independent amounts, provider evidence, statuses, errors, and timestamps.
Checkout
Cart Quote
/v1/storefront/carts/{id}/quote arky.client.eshop.cart.quote() Calculate a cart total before checkout. Storefront checkout is cart-backed: update the cart first, then quote or checkout it. The SDK sends the Store’s publishable key and the current market context; callers never supply a Store ID.
const cart = await arky.client.eshop.cart.current();
await arky.client.eshop.cart.update({
id: cart.id,
product_items: [
{ product_id: 'prod_xyz789', variant_id: 'var_small', quantity: 2 }
],
booking_items: [
{
service_id: 'svc_cleaning',
provider_id: 'prv_north',
slots: [{ from: 1780300800, to: 1780304400 }]
}
],
payment_method_key: 'credit_card',
shipping_method_id: 'ship_standard',
promo_code: 'SAVE10',
shipping_address: {
name: 'Jane Doe',
street1: '456 Contact Ave',
city: 'New York',
country: 'US',
state: 'NY',
postal_code: '10001'
}
});
const quote = await arky.client.eshop.cart.quote({ id: cart.id });
console.log('Subtotal:', quote.money.subtotal);
console.log('Discount:', quote.money.discount);
console.log('Shipping:', quote.money.shipping);
console.log('Tax:', quote.money.tax?.amount ?? 0);
console.log('Total:', quote.money.total);OrderQuote returns product_lines, booking_lines, digital_lines,
shipping_lines, available shipping_methods and payment_methods, the selected
payment_method_key, and one authoritative money breakdown. Monetary totals are
always read from quote.money; tax details, when present, are in
quote.money.tax.
Parameters
| Name | Type | Description |
|---|---|---|
id required | string | Cart ID to quote |
Cart Checkout
/v1/storefront/carts/{id}/checkout arky.client.eshop.cart.checkout() Process payment and complete a cart. The SDK resolves the Store from its
publishable key. The checkout response includes order_id, order number, a
payment_action for any required next action, and the payment object.
const cart = await arky.client.eshop.cart.current();
await arky.client.eshop.cart.update({
id: cart.id,
product_items: [
{ product_id: 'prod_xyz789', variant_id: 'var_small', quantity: 2 }
],
shipping_method_id: 'ship_standard',
shipping_address: {
name: 'Jane Doe',
street1: '456 Contact Ave',
city: 'New York',
state: 'NY',
postal_code: '10001',
country: 'US'
},
forms: [
{
key: 'contact-info',
entries: [
{ key: 'email', value: 'contact@example.com' },
{ key: 'firstName', value: 'John' },
{ key: 'lastName', value: 'Doe' }
]
}
],
promo_code: 'SAVE10'
});
const result = await arky.client.eshop.cart.checkout({
id: cart.id,
payment_method_key: 'cash'
});
console.log('Order placed:', result.order_id, result.number);Parameters
| Name | Type | Description |
|---|---|---|
id required | string | Cart ID |
payment_method_key optional | string | Market payment method key such as 'cash' or 'credit_card' |
return_url optional | string | Required for a positive card total; used after embedded checkout completes |
Complete Checkout Flow
import { initialize, mountCheckoutAction } from "arky-sdk/storefront";
const arky = initialize("arky_pk_...", {
market: "us",
locale: "en",
});
async function checkout() {
await arky.eshop.cart.load();
// 1. Quote the backend cart to show prices
const quote = await arky.eshop.cart.quote({
shipping_method_id: selectedShipping,
shipping_address: shippingAddress,
forms: [
{
key: "contact",
entries: [
{ key: "email", value: contactEmail },
{ key: "firstName", value: contactFirstName },
{ key: "lastName", value: contactLastName },
],
},
],
promo_code: promoCodeInput,
});
if (!quote) throw new Error("Cart quote is unavailable");
console.log("Total:", quote.money.total);
// 2. Checkout with the selected market payment method.
const result = await arky.eshop.cart.checkout({
payment_method_key: "credit_card",
return_url: window.location.href,
});
if (result.payment_action.type === "stripe_embedded_checkout") {
const mounted = await mountCheckoutAction(
result.payment_action,
"#stripe-checkout",
);
// Call mounted.destroy() when the checkout container is removed.
}
return result.order_id;
}
Use arky.eshop.cart.quote to show price breakdowns before the contact
commits to purchase. Checkout returns the closed
stripe_embedded_checkout | none action. Mount it with
mountCheckoutAction; it carries the exact connected Stripe account context
and short-lived client secret required by Stripe.js. The completion callback
is presentation state; only a signed Stripe webhook settles payment.
Return URLs must use HTTPS outside localhost or a literal loopback address;
credentials are rejected and fragments are removed.
Availability
/v1/stores/{storeId}/services/availability sdk.eshop.service.getAvailability() Availability belongs to services because the contact is choosing schedulable service capacity.
Reads use the provider’s compact per-local-day ProviderSchedule projection rather than scanning
orders. Checkout transactionally creates authoritative OrderBooking records and reserves the
same schedule buckets; cancellation releases them. The projection is rebuildable from bookings.
const availability = await sdk.eshop.service.getAvailability({
service_id: "svc_cleaning",
provider_id: "prv_north",
from: 1780272000,
to: 1782864000,
});
Parameters
| Name | Type | Description |
|---|---|---|
service_id required | string | Service to schedule |
provider_id optional | string | Provider to check, or omit to include all eligible providers |
from required | number | Start of the availability window as a Unix timestamp |
to required | number | End of the availability window as a Unix timestamp |