Skip to content

SDK Usage Guide

The Arky SDK (arky-sdk) is a TypeScript client library that simplifies integration with Arky’s APIs. It handles authentication, token refresh, API key management, and provides typed interfaces for all endpoints.

Terminal window
npm install arky-sdk

import { createArkySDK } from 'arky-sdk';
const sdk = createArkySDK({
baseUrl: 'https://api.arky.io',
storageUrl: 'https://storage.arky.io/prod',
businessId: 'your_business_id',
market: 'us',
getToken: async () => {
// Return stored tokens (from localStorage, cookies, etc.)
return {
accessToken: localStorage.getItem('accessToken') || '',
refreshToken: localStorage.getItem('refreshToken') || '',
provider: localStorage.getItem('provider') || 'GUEST',
expiresAt: parseInt(localStorage.getItem('expiresAt') || '0'),
};
},
setToken: (tokens) => {
// Store tokens securely
localStorage.setItem('accessToken', tokens.accessToken);
if (tokens.refreshToken) {
localStorage.setItem('refreshToken', tokens.refreshToken);
}
if (tokens.provider) {
localStorage.setItem('provider', tokens.provider);
}
if (tokens.expiresAt) {
localStorage.setItem('expiresAt', tokens.expiresAt.toString());
}
},
logout: () => {
// Clear tokens on logout
localStorage.clear();
window.location.href = '/login';
},
autoGuest: true, // Automatically create guest token if none exists
isAuthenticated: () => {
return !!localStorage.getItem('accessToken');
},
});
OptionTypeRequiredDefaultDescription
baseUrlstringAPI base URL (e.g., https://api.arky.io)
storageUrlstringhttps://storage.arky.io/devCDN base URL for media files
businessIdstringYour business ID
marketstringMarket/zone code (e.g., us, eu, global)
getToken() => Promise<AuthTokens>Function to retrieve stored tokens
setToken(tokens) => voidFunction to persist tokens
logout() => voidFunction to clear auth and redirect
autoGuestbooleantrueAuto-create guest token on init
isAuthenticated() => boolean() => falseCheck if user is authenticated

You can pass callbacks to handle success/error locally for mutating requests. GET requests do not invoke callbacks.

await sdk.user.updateUser({ name: 'Alice' }, {
onSuccess: ({ data }) => toast('Profile updated'),
onError: ({ error }) => toast(error.message || 'Update failed'),
});

If autoGuest: true (default), the SDK automatically creates a guest token on initialization if no tokens exist.

const sdk = createArkySDK({
// ... config
autoGuest: true, // Guest token created automatically
});
// No manual login needed for browsing products, CMS content, etc.
const products = await sdk.eshop.products.list({ page: 1, limit: 20 });
const result = await sdk.user.login({
email: 'user@example.com',
password: 'secure_password',
provider: 'EMAIL',
});
// Tokens are automatically stored via setToken()
console.log(result.user); // User object
// Redirect user to Google OAuth
window.location.href = `https://api.arky.io/v1/users/oauth/google?businessId=your_business_id&redirectUrl=${encodeURIComponent(window.location.origin + '/auth/callback')}`;
// On callback page, extract code from query params
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
if (code) {
const result = await sdk.user.loginWithOAuth({
provider: 'GOOGLE',
code,
});
// Tokens stored, user logged in
}

The SDK automatically refreshes tokens when the access token expires (using expiresAt timestamp).

// No manual refresh needed — SDK handles it internally
const profile = await sdk.user.getProfile(); // Automatically refreshes if expired
// Clear tokens and redirect (via logout callback)
sdk.logout();

The SDK is organized into domain-specific modules:

sdk.user // User auth, profile, phone verification
sdk.business // Business CRUD, subscriptions, invitations
sdk.media // File upload, media library
sdk.role // Role/permission management
sdk.notification // Notifications, email tracking
sdk.promoCode // Promo codes CRUD
sdk.analytics // Metrics query, health check
sdk.cms // Collections, entries, AI blocks, newsletter subscriptions
sdk.eshop // Products, orders, checkout
sdk.reservation // Services, providers, reservations
sdk.payment // Quote engine, markets, Stripe webhooks

// List collections
const collections = await sdk.cms.collections.list({
page: 1,
limit: 50,
});
// Get a collection
const collection = await sdk.cms.collections.get('collection_id');
// List entries
const entries = await sdk.cms.entries.list('collection_id', {
page: 1,
limit: 20,
hydrate: true, // Hydrate relationships
});
// Get single entry
const entry = await sdk.cms.entries.get('collection_id', 'entry_id', {
hydrate: true,
});
// List products
const products = await sdk.eshop.products.list({
page: 1,
limit: 20,
search: 'laptop',
categoryId: 'cat_123',
});
// Get product with pricing
const product = await sdk.eshop.products.get('prod_123');
// Create order (checkout)
const order = await sdk.eshop.orders.create({
marketId: 'market_us',
parts: [
{
productId: 'prod_123',
quantity: 2,
blocks: [], // Optional custom fields
},
],
blocks: [], // Customer info blocks
payment: {
provider: 'STRIPE',
currency: 'usd',
amount: 9999, // In cents
},
});
// List user orders
const orders = await sdk.eshop.orders.list({ page: 1, limit: 10 });
// List services
const services = await sdk.reservation.services.list({ page: 1, limit: 20 });
// Get available slots
const slots = await sdk.reservation.slots.getAvailable({
serviceId: 'service_123',
providerId: 'provider_456',
date: '2025-02-01',
});
// Create reservation
const reservation = await sdk.reservation.reservations.create({
serviceId: 'service_123',
providerId: 'provider_456',
startTime: 1706803200,
endTime: 1706806800,
marketId: 'market_us',
parts: [{ serviceId: 'service_123', quantity: 1, blocks: [] }],
blocks: [], // Customer info
payment: {
provider: 'STRIPE',
currency: 'usd',
amount: 5000,
},
});

Newsletters are Collections with type: 'NEWSLETTER'. See CMS API - Newsletters for full details.

// Subscribe to newsletter (requires planId)
await sdk.cms.subscribeToCollection({
collectionId: 'newsletter_123',
email: 'user@example.com',
planId: 'plan_free', // Required - specify which plan
});
// Get subscribers
const subscribers = await sdk.cms.getCollectionSubscribers({
id: 'newsletter_123',
});
// Unsubscribe via token (from email link)
await sdk.cms.unsubscribeFromCollection({
token: 'unsubscribe_token_xyz',
});

The SDK includes utility functions for common operations:

import { getBlockValue, getBlockTextValue, extractBlockValues } from 'arky-sdk/utils/blocks';
const entry = await sdk.cms.entries.get('posts', 'post_123');
// Extract single block value
const title = getBlockValue(entry, 'title'); // Returns first value
// Extract localized text
const description = getBlockTextValue(
entry.blocks.find(b => b.key === 'description'),
'en'
);
// Extract all block values as object
const values = extractBlockValues(entry.blocks);
// { title: "...", body: "...", featured: true }

See Blocks Guide for full block utilities documentation.

const product = await sdk.eshop.products.get('prod_123');
// Get price for current market
const price = sdk.utils.getMarketPrice(product.prices, sdk.getMarket());
console.log(price.currency, price.amount); // 'usd', 2999
// Format price (cents to dollars)
const formatted = sdk.utils.formatMinor(price.amount, price.currency);
console.log(formatted); // "$29.99"
// Get currency symbol
const symbol = sdk.utils.getCurrencySymbol('eur');
console.log(symbol); // "€"
// Slugify (URL-safe strings)
const slug = sdk.utils.slugify('Hello World! 123');
console.log(slug); // "hello-world-123"
// Humanize (convert snake_case to Title Case)
const label = sdk.utils.humanize('product_name');
console.log(label); // "Product Name"
// Format date
const formatted = sdk.utils.formatDate(1706803200, 'en-US');
console.log(formatted); // "Feb 1, 2025"
// Get timezone groups (for select dropdowns)
const tzGroups = sdk.utils.tzGroups;
console.log(tzGroups[0]); // { label: "United States", zones: [...] }
// Find timezone by city/country
const tz = sdk.utils.findTimeZone('New York');
console.log(tz); // "America/New_York"
// Validate phone number (E.164 format)
const isValid = sdk.utils.validatePhoneNumber('+12345678901');
console.log(isValid); // true or false

  • Used for user-facing apps (React, Vue, Svelte, mobile)
  • Stored in localStorage / cookies
  • Automatically refreshed via refreshToken
  • Sent as Authorization: Bearer <token> header
  • Used for server-side integrations (Astro SSR, Node.js backends)
  • Stored securely in .env or secrets manager
  • Never expire (rotate manually)
  • Sent as X-API-Key: <key> header

Example: Server-side SDK with API Key

const sdk = createArkySDK({
baseUrl: process.env.API_URL,
businessId: process.env.BUSINESS_ID,
market: 'us',
getToken: async () => ({
accessToken: process.env.ARKY_API_KEY,
provider: 'API',
}),
setToken: () => {},
logout: () => {},
autoGuest: false,
});
const products = await sdk.eshop.products.list({ page: 1, limit: 50 });

All SDK methods can throw errors with the following structure:

try {
const product = await sdk.eshop.products.get('invalid_id');
} catch (error: any) {
console.error('Error name:', error.name); // "ApiError", "NetworkError", "ParseError"
console.error('Status code:', error.statusCode); // 404, 401, 500, etc.
console.error('Message:', error.message); // Human-readable error
console.error('Request ID:', error.requestId); // For debugging with support
console.error('Validation errors:', error.validationErrors); // Field-level errors
}
Error NameDescription
ApiErrorServer returned an error (4xx/5xx)
NetworkErrorNetwork failure (timeout, no connection)
ParseErrorFailed to parse JSON response

When the API returns validation errors (e.g., invalid email format), they’re available in error.validationErrors:

try {
await sdk.user.register({ email: 'invalid', password: '123' });
} catch (error: any) {
console.log(error.validationErrors);
// {
// email: ["Invalid email format"],
// password: ["Password must be at least 8 characters"]
// }
}

  • Browser apps: Use localStorage or sessionStorage (simplest, suitable for most cases)
  • Server apps: Use environment variables or secrets manager
  • Mobile apps: Use secure storage (Keychain, Keystore)

If your app supports multiple markets/regions:

// Switch market dynamically
sdk.setMarket('eu');
// Fetch products with new market pricing
const products = await sdk.eshop.products.list({ page: 1, limit: 20 });

If your app serves multiple businesses:

// Switch business dynamically
sdk.setBusinessId('another_business_id');
// Fetch collections for new business
const collections = await sdk.cms.collections.list({ page: 1, limit: 50 });

Always handle pagination for large datasets:

let page = 1;
let allProducts = [];
let hasMore = true;
while (hasMore) {
const products = await sdk.eshop.products.list({ page, limit: 100 });
allProducts = [...allProducts, ...products];
hasMore = products.length === 100;
page++;
}

The SDK is fully typed. Import types directly:

import type { Business, Product, Order, Reservation, Entry } from 'arky-sdk';
const product: Product = await sdk.eshop.products.get('prod_123');
const order: Order = await sdk.eshop.orders.create({ ... });