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:
| Module | Purpose |
|---|---|
| User | Authentication, contacts, permissions |
| Store | Settings, subscriptions, team management |
| E-shop | Products, services, providers, orders, checkout |
| CMS | Collections, entries, media fields, and forms |
| Media | File uploads, image management |
| Notification | Email notifications and delivery tracking |
| Action | First-party event keys, contact timelines, analytics, and experiment goals |
| Promo Codes | Discounts, campaigns |
| Roles | Permissions, access control |
| Database | Key-value storage |
| Network | Cross-store search |
| Location | Geographic 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:
| Prefix | Entity |
|---|---|
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'
});
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
| Scope | Description |
|---|---|
products:read | View products |
products:write | Create/edit products |
orders:read | View orders |
orders:write | Process orders |
services:read | View services and providers |
services:write | Manage services and providers |
content:read | View CMS content |
content:write | Edit CMS content |
users:read | View users |
users:write | Manage users |
settings:write | Modify 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
- E-commerce Guide - Build a storefront
- Scheduled Services Guide - Create a service scheduling flow
- API Reference - Explore all endpoints