Back to Arky

Core Concepts

Understand the Arky domain model before wiring a custom website.

Overview

Arky is a modular backend for custom websites. Storefronts usually use initialize, a framework-agnostic reactive layer over the lower-level SDK.

import { initialize } from 'arky-sdk/storefront';

const arky = initialize({
  baseUrl: 'https://api.arky.io',
  storeId: 'store_abc123',
  market: 'us',
  locale: 'en',
});

Store

A Store is the top-level entity in Arky. Everything—products, scheduled services, content, users—belongs to a store.

const store = await arky.store.getStore();

Store Hierarchy

Stores can have parent-child relationships, enabling:

  • Multi-location setups
  • Franchise models
  • White-label solutions
const store = await arky.store.getStore();

Modules

Arky is organized into domain-specific modules. Use only the modules the website needs:

ModulePurpose
UserAuthentication, contacts, permissions
StoreSettings, subscriptions, team management
E-shopProducts, services, providers, orders, checkout
CMSCollections, entries, media fields, and forms
MediaFile uploads, image management
NotificationEmail notifications and delivery tracking
ActionFirst-party event keys, contact timelines, analytics, and experiment goals
Promo CodesDiscounts, campaigns
RolesPermissions, access control
DatabaseKey-value storage
NetworkCross-store search
LocationGeographic data

Action Keys

An action key names something meaningful that happened on a storefront: page.view, product.view, hero.cta.clicked, lead.submitted, or any custom action you care about. Actions are tied to the current contact session and can appear in contact timelines, analytics, reports, and experiments.

await arky.action.track({
  key: 'lead.submitted',
  payload: { source: 'homepage' }
});

Experiments

An experiment adds sticky variant assignment beside action tracking. Create an experiment with an experiment key, variants, weights, and a goal action key. When the storefront calls arky.experiments.use('homepage_hero'), Arky assigns the current contact a stable variant and records that the variant was shown.

The goal action key decides success. If the experiment goal is hero.cta.clicked, that action is what ClickHouse uses to calculate shown, wins, rate, and current winner.

See Action Keys and Experiments for the storefront pattern.

Resources

Most Arky entities follow a consistent pattern:

// Read
const product = await arky.eshop.product.loadDetail({
  id: 'prod_abc'
});

// List
const products = await arky.eshop.product.list({
  cursor: null,
  limit: 20
});

// Cart-backed checkout
await arky.eshop.cart.addProduct(product, product.variants[0], 1);
const quote = await arky.eshop.cart.quote();
const order = await arky.eshop.cart.checkout({
  payment_method_key: 'cash'
});

Pagination

List endpoints use cursor-based pagination:

let cursor = null;
const allProducts = [];

do {
  const result = await arky.eshop.product.list({
    cursor,
    limit: 50
  });

  allProducts.push(...result.items);
  cursor = result.cursor;
} while (cursor);

Identifiers

Arky uses prefixed IDs for clarity:

PrefixEntity
store_Store
usr_User
prod_Product
ord_Order
svc_Service
prv_Provider
col_CMS Collection
entry_CMS Entry
media_Media file
role_Role
promo_Promo code

Slugs

Many resources support slugs for SEO-friendly URLs:

// Create with slug
await sdk.eshop.product.create({
  key: 'premium-widget',
  slug: { en: 'premium-widget' },
  variants: []
});

// Fetch by slug
const product = await arky.eshop.product.loadDetail({
  slug: 'premium-widget'
});
Tip

Slugs are automatically generated from names if not provided. They must be unique within a store.

Timestamps

All timestamps are Unix timestamps in seconds:

const product = await sdk.eshop.product.get({ storeId, id });

console.log(product.val.created_at); // 1704067200
console.log(new Date(product.val.created_at * 1000)); // 2024-01-01T00:00:00.000Z

Money

All monetary values are in minor units (cents):

// Create a $19.99 product
await sdk.eshop.product.create({
  storeId: 'store_123',
  name: 'Widget',
  price: 1999 // $19.99
});

// Display formatted price
import { formatPrice } from 'arky-sdk/utils';

const display = formatPrice(1999, 'USD'); // "$19.99"

Result Type

All SDK methods return a Result type for explicit error handling:

const result = await sdk.eshop.product.get({
  storeId: 'store_123',
  id: 'prod_abc'
});

if (result.ok) {
  // Success - access data
  const product = result.val;
  console.log(product.name);
} else {
  // Error - handle failure
  const error = result.val;
  console.error(error.message);
}

Pattern Matching

import { match } from 'ts-results-es';

const productName = match(result)
  .ok(product => product.name)
  .err(error => 'Unknown Product')
  .value;

Permissions

Access is controlled through roles and permissions:

// Check if user has admin role for a store
const user = await sdk.account.getMe({});
const membership = user.memberships?.find(m => m.store_id === 'store_123');
const isAdmin = membership?.role === 'Admin' || membership?.role === 'Owner';

Permission Scopes

ScopeDescription
products:readView products
products:writeCreate/edit products
orders:readView orders
orders:writeProcess orders
services:readView services and providers
services:writeManage services and providers
content:readView CMS content
content:writeEdit CMS content
users:readView users
users:writeManage users
settings:writeModify store settings

Webhooks

Receive real-time updates when events occur:

// Configure webhook in store settings
await sdk.store.updateStore({
  id: 'store_123',
  webhook_url: 'https://yourapp.com/webhooks/arky',
  webhook_events: [
    'order.created',
    'order.payment_received',
    'order.reminder'
  ]
});

See Webhooks Guide for complete documentation.

Next Steps