Store
Manage store settings, subscriptions, and team access
The Store module handles store configuration, subscription management, team access, and webhooks.
Get Store
/v1/stores/{storeId} sdk.store.get() Retrieve the current store.
const store = await sdk.store.get({});
console.log(store.key, store.settings);List Stores
/v1/stores 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
/v1/stores 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
/v1/stores/{id} 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
/v1/stores/plans 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
/v1/stores/{storeId}/subscription 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
/v1/stores/{storeId}/subscription 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 |
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
/v1/stores/{storeId}/subscription/portal 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
/v1/stores/{storeId}/members 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
/v1/stores/{storeId}/members/{accountId} 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
/v1/stores/{storeId}/webhooks/test 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
/v1/stores/{id}/media 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
/v1/stores/{storeId}/orders/{orderId}/refunds 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
/v1/stores/{storeId}/build-hooks 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
/v1/stores/{storeId}/build-hooks 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
/v1/stores/{storeId}/build-hooks/{id} 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
/v1/stores/{storeId}/payment-providers sdk.store.paymentProvider.list() const providers = await sdk.store.paymentProvider.list({
store_id: "store_abc123",
});
/v1/stores/{storeId}/payment-providers/stripe/connect 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
/v1/stores/{storeId}/build-hooks/{id} 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
/v1/stores/{storeId}/webhooks sdk.store.webhook.list() const webhooks = await sdk.store.webhook.list({
store_id: "store_abc123",
});
Create Webhook
/v1/stores/{storeId}/webhooks 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
/v1/stores/{storeId}/webhooks/{id} 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
/v1/stores/{storeId}/webhooks/{id} 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 |