Back to Arky

Store

Manage store settings, subscriptions, and team access

The Store module handles store configuration, subscription management, team access, and webhooks.

Get Store

GET /v1/stores/{storeId}
SDK: sdk.store.get()

Retrieve the current store.

const store = await sdk.store.get({});

console.log(store.key, store.settings);

List Stores

GET /v1/stores
SDK: sdk.store.find()

List all stores the current user has access to.

const result = await sdk.store.find({
query: 'store',
isNetwork: false,
status: 'ACTIVE',
limit: 20,
cursor: null,
sort_field: 'created_at',
sort_direction: 'desc'
});

result.items.forEach(store => {
console.log(store.key);
});

Parameters

Name Type Description
query optional string Search query
isNetwork optional boolean Filter by network status
status optional DRAFT | ACTIVE | ARCHIVED Filter by status
limit optional number Items per page
cursor optional string Pagination cursor
sort_field optional string Sort field
sort_direction optional asc | desc Sort direction

Create Store

POST /v1/stores
SDK: sdk.store.create()

Create a new store.

const result = await sdk.store.create({
key: 'my-store',
timezone: 'America/New_York',
languages: ['en'],
emails: {
  billing: 'billing@mystore.com',
  support: 'support@mystore.com'
}
});

Parameters

Name Type Description
key required string Unique store key identifier (min 3 chars, slug-format)
timezone required string IANA timezone, e.g. 'America/New_York'
languages optional string[] Supported language codes, e.g. `['en']`
emails required StoreEmails Billing and support emails

Two default markets are auto-created with every store: us (USD, Exclusive) and eu (EUR, Inclusive). Fetch them with sdk.store.market.list() or create additional markets with sdk.store.market.create().

Update Store

PUT /v1/stores/{id}
SDK: sdk.store.update()

Update store settings.

const result = await sdk.store.update({
id: 'store_abc123',
key: 'my-updated-store',
timezone: 'America/New_York',
languages: ['en'],
emails: {
  billing: 'billing@yourstore.com',
  support: 'support@yourstore.com'
}
});

Parameters

Name Type Description
id required string Store ID
key optional string Store key
timezone optional string Store timezone (IANA format)
languages optional string[] Supported languages
emails optional StoreEmails Billing and support emails

Subscriptions

Get Subscription Plans

GET /v1/stores/plans
SDK: sdk.store.subscription.getPlans()

List available subscription plans.

const { items: plans } = await sdk.store.subscription.getPlans({});

plans.forEach((plan) => {
	console.log(plan.name, plan.amount, plan.features);
});

Get Current Subscription

GET /v1/stores/{storeId}/subscription
SDK: sdk.store.subscription.get()

Read the store’s current subscription state. The response contains the selected plan, lifecycle status, billing market and currency, subscription dates, and any current Stripe Checkout.

const subscription = await sdk.store.subscription.get({
	store_id: "store_abc123",
});

	console.log(
		subscription.plan_id,
		subscription.billing_status,
		subscription.checkout,
	);

Select Subscription Plan

POST /v1/stores/{storeId}/subscription
SDK: sdk.store.subscription.select()

Select a plan synchronously. For a paid plan, Arky creates an embedded Stripe Checkout Session during this request and returns a closed payment_action on the subscription. Mount that action in your own billing page with the shared SDK checkout controller. A plan that does not require Stripe can become active immediately.

import { mountCheckoutAction } from 'arky-sdk';

const subscription = await sdk.store.subscription.select({
store_id: 'store_abc123',
plan_id: 'plan_pro',
return_url: 'https://yourapp.com/settings/billing'
});

if (subscription.payment_action.type === 'stripe_embedded_checkout') {
const mounted = await mountCheckoutAction(
  subscription.payment_action,
  '#stripe-checkout'
);
// Call mounted.destroy() when the checkout container is removed.
}

Parameters

Name Type Description
plan_id required string Plan to select
return_url required URL string Browser destination after Stripe Checkout
Note

This endpoint returns the subscription directly; there is no action resource to poll. If the HTTP result is ambiguous, read the current subscription. Retry the same plan and return_url only when no ready Checkout or active plan was confirmed. A Checkout with status unknown is terminal and requires review. Checkout, Billing Portal, and Connect return URLs must use HTTPS outside localhost or a literal loopback address. Credentials are rejected and fragments are removed.

Create Billing Portal Session

POST /v1/stores/{storeId}/subscription/portal
SDK: sdk.store.subscription.createPortalSession()

Create a Stripe Customer Portal session for subscription management.

const result = await sdk.store.subscription.createPortalSession({
	return_url: "https://yourapp.com/settings/billing",
});

// Redirect to Stripe portal
window.location.href = result.portal_url;

Parameters

Name Type Description
return_url required string URL to return to after portal session

Team Management

Add Member

POST /v1/stores/{storeId}/members
SDK: sdk.store.member.add()

Add a user to the store team immediately. If the account does not exist yet, it is created with the assigned store role. No confirmation email or accept step is sent.

await sdk.store.member.add({
email: 'teammate@example.com',
role: 'admin'  // 'admin' | 'owner' | 'super'
});

Parameters

Name Type Description
email required string Member email address
role optional admin | owner | super Role to assign (defaults to admin)

Remove Member

DELETE /v1/stores/{storeId}/members/{accountId}
SDK: sdk.store.member.remove()

Remove a team member from the store.

await sdk.store.member.remove({
	account_id: "acc_abc123",
});

Parameters

Name Type Description
account_id required string Account ID of the member to remove

Webhooks

Test Webhook

POST /v1/stores/{storeId}/webhooks/test
SDK: sdk.store.webhook.test()

Queue a durable test delivery for an existing enabled webhook. Generate one delivery ID for the test and reuse the same ID with the same webhook ID to read its current delivery state.

import {
clearDurableRequest,
getOrCreateDurableRequest,
withDurableRequestLock
} from 'arky-sdk/utils';

const request = { webhook_id: 'wh_abc123' };
const storageKey = 'arky:webhook-test:store_abc123:wh_abc123';

const delivery = await withDurableRequestLock(storageKey, 'webhook test', async () => {
const durable = getOrCreateDurableRequest(storageKey, request, 'webhook test');
const response = await sdk.store.webhook.test({
delivery_id: durable.id,
...request
});
if (response.delivery_id !== durable.id) {
throw new Error('Webhook response did not match the requested delivery');
}
if (['succeeded', 'rejected', 'failed', 'unknown'].includes(response.status)) {
clearDurableRequest(durable, 'webhook test');
}
return response;
});

console.log(delivery.status, delivery.provider_status_code, delivery.error);

Parameters

Name Type Description
delivery_id required UUID string Stable caller-owned ID for this test delivery
webhook_id required string ID of an existing enabled webhook

The response contains delivery_id, status, optional provider_status_code, and optional error. Status is requested, processing, succeeded, rejected, failed, or unknown. unknown is terminal: Arky cannot prove whether the endpoint received the request and will not automatically send it again.

Media

Get Store Media

GET /v1/stores/{id}/media
SDK: sdk.media.getStoreMedia()

List all media files for a store.

const result = await sdk.media.getStoreMedia({
	id: "store_abc123",
	limit: 50,
	cursor: null,
	query: "product",
	mime_type: "image/jpeg",
	sort_field: "created_at",
	sort_direction: "desc",
});

result.items.forEach((media) => {
	console.log(media.id, media.resolutions.original?.url);
});

Parameters

Name Type Description
id required string Store ID
limit required number Items per page
cursor optional string Pagination cursor
ids optional string[] Filter by specific media IDs
query optional string Search query
mime_type optional string Filter by MIME type
sort_field optional string Sort field
sort_direction optional asc | desc Sort direction

Refunds

Create Refund

POST /v1/stores/{storeId}/orders/{orderId}/refunds
SDK: sdk.eshop.order.createRefund()

Create a full or partial order refund resource. Generate one refund ID before the first request and reuse that same ID with the exact amount when retrying an interrupted create request.

import {
clearDurableRequest,
getOrCreateDurableRequest,
withDurableRequestLock
} from 'arky-sdk/utils';

const request = {
order_id: 'ord_xyz789',
amount: 1999
};
const storageKey = 'arky:order-refund:store_abc123:ord_xyz789';

const result = await withDurableRequestLock(storageKey, 'refund', async () => {
const durable = getOrCreateDurableRequest(storageKey, request, 'refund');
const response = await sdk.eshop.order.createRefund({
...request,
refund_id: durable.id
});
if (response.refund_id !== durable.id) {
throw new Error('Refund response did not match the requested refund');
}
clearDurableRequest(durable, 'refund');
return response;
});

console.log(result.refund_id, result.status);

Parameters

Name Type Description
order_id required string Order ID to refund
refund_id required UUID string Stable caller-owned ID for this refund resource
amount required number Amount in the order currency's minor units

The response status is requested, processing, succeeded, rejected, failed, or unknown. Refunds are standalone resources: use sdk.eshop.order.getRefund() or sdk.eshop.order.getRefunds() instead of reading an embedded order field. A failed refund is a terminal no-call record; after correcting the underlying problem, create a new refund with a new refund_id. An unknown refund is terminal for automatic processing because Arky cannot prove the provider outcome.

Build Hooks

Build hooks are store deploy endpoints. Arky resolves build hook URLs and headers server-side when a workflow uses a deploy_webhook node.

List Build Hooks

GET /v1/stores/{storeId}/build-hooks
SDK: sdk.store.buildHook.list()
const hooks = await sdk.store.buildHook.list({
	store_id: "store_abc123",
});

hooks.forEach((hook) => {
	console.log(hook.key, hook.type, hook.active);
});

Create Build Hook

POST /v1/stores/{storeId}/build-hooks
SDK: sdk.store.buildHook.create()
await sdk.store.buildHook.create({
	store_id: "store_abc123",
	key: "production-deploy",
	type: "vercel",
	url: "https://deploy.example.com/hooks/production",
	active: true,
});

Update Build Hook

PUT /v1/stores/{storeId}/build-hooks/{id}
SDK: sdk.store.buildHook.update()
await sdk.store.buildHook.update({
	store_id: "store_abc123",
	id: "hook_xyz789",
	key: "production-deploy",
	type: "custom",
	url: "https://deploy.example.com/hooks/arky",
});

Build-hook endpoint URLs and custom header values are write-only and redacted in responses. Omit url and headers from an update to preserve their stored values; send either field only when you intend to replace it. Never send the •••••••• response placeholder back as a value.

Payment Providers

GET /v1/stores/{storeId}/payment-providers
SDK: sdk.store.paymentProvider.list()
const providers = await sdk.store.paymentProvider.list({
	store_id: "store_abc123",
});
POST /v1/stores/{storeId}/payment-providers/stripe/connect
SDK: sdk.store.paymentProvider.stripe.connect()
const { onboarding_url } = await sdk.store.paymentProvider.stripe.connect({
	store_id: "store_abc123",
	return_url: "https://admin.example.com/payments?stripe=return",
	refresh_url: "https://admin.example.com/payments?stripe=refresh",
	country: "BA",
	email: "owner@example.com",
});

if (onboarding_url) {
	window.location.href = onboarding_url;
}

onboarding_url is null when the connected account is already ready. A retryable Stripe connection failure returns 503; repeating the same connect() request continues the persisted operation with the same Stripe idempotency key. A successful repeat returns a fresh Account Link. An explicitly supplied connected_account_id is accepted only when the Stripe Account contains the exact Arky Store and payment-provider ownership metadata; a metadata-less account is rejected.

Delete Build Hook

DELETE /v1/stores/{storeId}/build-hooks/{id}
SDK: sdk.store.buildHook.delete()
await sdk.store.buildHook.delete({
	store_id: "store_abc123",
	id: "hook_xyz789",
});

Managed Provider Surfaces

| Surface | SDK | | ------------------- | --------------------------- | | Payments | sdk.store.paymentProvider | | Social connections | sdk.social.connection | | Social publications | sdk.social.publication | | Shipping | sdk.eshop.shipment | | Build hooks | sdk.store.buildHook |


Webhook CRUD

Manage webhook endpoints programmatically.

List Webhooks

GET /v1/stores/{storeId}/webhooks
SDK: sdk.store.webhook.list()
const webhooks = await sdk.store.webhook.list({
	store_id: "store_abc123",
});

Create Webhook

POST /v1/stores/{storeId}/webhooks
SDK: sdk.store.webhook.create()
await sdk.store.webhook.create({
	store_id: "store_abc123",
	key: "order-notifications",
	url: "https://yourapp.com/webhooks/orders",
	events: [{ event: "order.created" }, { event: "order.updated" }],
	headers: { "X-Custom-Header": "value" },
	secret: "whsec_my_secret",
	enabled: true,
});

Parameters

Name Type Description
store_id required string Store ID
key required string Unique webhook key
url required string Webhook endpoint URL
events required WebhookEventSubscription[] Events to subscribe to
headers required Record<string, string> Custom headers sent with each request
secret required string Signing secret for verifying webhook payloads
enabled required boolean Whether the webhook is active

Update Webhook

PUT /v1/stores/{storeId}/webhooks/{id}
SDK: sdk.store.webhook.update()
await sdk.store.webhook.update({
	store_id: "store_abc123",
	id: "wh_xyz789",
	key: "order-notifications",
	url: "https://yourapp.com/webhooks/v2/orders",
	events: [
		{ event: "order.created" },
		{ event: "order.updated" },
		{ event: "order.cancelled" },
	],
	enabled: true,
});

Webhook endpoint URLs, signing secrets, and custom header values are write-only and redacted in responses. Omit url, secret, and headers from an update to preserve their stored values; sending headers replaces the complete header map. Never send the •••••••• response placeholder back as a value.

Delete Webhook

DELETE /v1/stores/{storeId}/webhooks/{id}
SDK: sdk.store.webhook.delete()
await sdk.store.webhook.delete({
	store_id: "store_abc123",
	id: "wh_xyz789",
});

Available Webhook Events

| Event | Description | | ---------------------------- | ---------------------------------------- | | collection.created | CMS collection created | | collection.updated | CMS collection updated | | collection.deleted | CMS collection deleted | | entry.created | CMS entry created | | entry.updated | CMS entry updated | | entry.deleted | CMS entry deleted | | form_submission.created | Form submission received | | order.created | Order created | | order.updated | Order status changed | | order_booking.reminder | Confirmed booking reminder | | product.created | Product created | | product.updated | Product updated | | product.deleted | Product deleted | | audience.member_added | Audience member became active | | audience.member_removed | Audience member was removed | | audience.member_pending | Audience member awaits confirmation | | audience.member_confirmed | Audience member confirmed enrollment | | audience.member_access_cancelled | Audience member access was cancelled | | audience.member_email_unsubscribed | Member opted out of Audience email | | audience.member_email_resubscribed | Member explicitly opted back into email | | account.updated | Store account or team membership updated |