SDK
Use the TypeScript SDK for the default implementation path.
SDK
Use the SDK when you are building a frontend, server route, or workflow that should stay aligned with 01.software conventions.
Setup Flow
Install the package
Add @01.software/sdk to the project package manager already used by the repository.
Create the browser client
Use the Publishable Key only for browser-safe reads and only from approved origins.
Create the server client
Use the Secret Key only in trusted server code for writes and privileged workflows.
Verify the slice
Run the target project's typecheck, lint, tests, and build before handoff.
Client Choice
| Context | Client | Notes |
|---|---|---|
| browser UI | browser client | reads only, origin-limited |
| server route | server client | import from @01.software/sdk/server for writes and privileged operations |
| React data fetching | query helpers | keep cache and loading states explicit |
| AI-generated code | existing project patterns | avoid adding framework layers |
Import Boundaries
Use @01.software/sdk for the browser-safe client, commerce helpers, and core
types. Use @01.software/sdk/server for createServerClient, and use
@01.software/sdk/query only when your app needs React Query helpers.
Rows marked none do not need any extra packages beyond @01.software/sdk.
| Import | Feature(s) | Install when used |
|---|---|---|
@01.software/sdk/query | React Query hooks, cache helpers, getQueryClient | @tanstack/react-query, react, react-dom |
@01.software/sdk/realtime | useRealtimeQuery | @tanstack/react-query |
@01.software/sdk/storefront-cache | product storefront cache resource helpers | none |
@01.software/sdk/analytics/react | <Analytics /> | react, react-dom |
@01.software/sdk/ui/rich-text | RichTextContent, StyledRichTextContent | @payloadcms/richtext-lexical |
@01.software/sdk/ui/form | FormRenderer | none |
@01.software/sdk/ui/code-block | CodeBlock, highlight | shiki, hast-util-to-jsx-runtime |
@01.software/sdk/ui/canvas | CanvasRenderer, CanvasFrame, useCanvas, prefetchCanvas | @tanstack/react-query, @xyflow/react, quickjs-emscripten, postcss, sucrase |
@01.software/sdk/ui/canvas/server | canvas server helpers | none |
@01.software/sdk/ui/video | VideoPlayer | @mux/mux-player-react |
@01.software/sdk/ui/image | Image | none |
If a feature is not listed here, it does not need a separate peer install. For the full component-to-peer mapping, see the SDK package README.
Storefront Cache Resources
Shaped storefront reads use framework-neutral resource names so adapters can map
the same invalidation contract to Next.js cache tags, CDN tags, or another cache
layer without baking framework APIs into the core SDK. In this section,
storefront means public tenant-facing reads for a website, app, docs site, or
commerce frontend; it is not limited to ecommerce product pages.
import { storefrontCacheResources } from '@01.software/sdk/storefront-cache'
const cacheScope = {
tenantId: tenant.id,
publishableKeyScope: 'default', // app-defined, short, non-secret scope
}
const listingTags = [storefrontCacheResources.productListing(cacheScope)]
const detailTags = [
storefrontCacheResources.productDetail(cacheScope, { slug: productSlug }),
]Resource names use this shape:
storefront:v1:tenant:<tenantId>:key:<publishableKeyScope>:resource:<collection>:list
storefront:v1:tenant:<tenantId>:key:<publishableKeyScope>:resource:<collection>:detail:<id|slug>:<identity>The SDK intentionally exposes public helpers only for product shaped reads today. The other rows reserve the same resource-family grammar for first-party adapters that own those shaped reads; do not add public helpers until the shaped adapter surface exists.
| Read surface | Resource family | Public SDK helper |
|---|---|---|
| product listing / product detail | products | productListing(scope), productDetail(scope, { id }) or { slug } |
| links | links | reserved family; no public helper yet |
| documents | documents | reserved family; no public helper yet |
| gallery items | gallery-items | reserved family; no public helper yet |
| playlists | playlists | reserved family; no public helper yet |
| tracks | tracks | reserved family; no public helper yet |
| media/images | images | reserved family; no public helper yet |
| shipping policies | shipping-policies | reserved family; no public helper yet |
Use list resources for list pages, search pages, and shaped reads that aggregate
multiple documents. Use detail resources only when the adapter already has the
same public identity it used for the cache tag. Slug-based product pages can use
{ slug } before fetching the shaped detail response, so they do not need a raw
collection pre-read just to discover the product ID.
publishableKeyScope should be a stable, short, non-secret key id or
fingerprint. Use the default scope only when every publishable key for the tenant
sees the same public cache view. If a cache entry is tagged with a key-specific
scope, webhook revalidation must revalidate that same scope; for tenant-wide
mutations, revalidate every affected public key scope.
Preview, draft, customer-token, and server-credential reads must stay outside
the shared storefront cache. Error responses, permission failures, and
{ found: false } reads should be treated as no-store unless the adapter owns
a separate negative-cache policy with its own short TTL.
Next.js Mapping
In a Next.js adapter, pass these names to cached fetch calls with next.tags.
The current Next.js revalidateTag API requires a second argument; use "max"
for stale-while-revalidate behavior. Tags only participate in revalidation when
the response is stored in the Data Cache, so opt into caching for SSG/ISR reads
and keep preview, draft, and other dynamic reads as no-store. Next.js cache
tags are case-sensitive, must be 256 characters or shorter, and each fetch can
carry at most 128 tags; keep tenant/key scopes short and non-secret.
import { storefrontCacheResources } from '@01.software/sdk/storefront-cache'
export async function fetchProductDetail(productSlug: string) {
const scope = {
tenantId: process.env.APP_TENANT_ID!,
publishableKeyScope: process.env.STOREFRONT_CACHE_KEY_SCOPE ?? 'default',
}
const tags = [
storefrontCacheResources.productDetail(scope, { slug: productSlug }),
]
const params = new URLSearchParams({ slug: productSlug })
return fetch(
`${process.env.SOFTWARE_API_URL}/api/products/detail?${params}`,
{
cache: 'force-cache',
headers: {
'X-Publishable-Key': process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
},
next: { tags },
},
)
}Webhook route handlers should revalidate the same resource names from an app-owned invalidation payload, not by issuing raw collection reads. This example assumes your route has normalized the platform webhook data into the product identity and public cache scopes your app uses:
import { revalidateTag } from 'next/cache'
import { storefrontCacheResources } from '@01.software/sdk/storefront-cache'
type ProductInvalidation = {
collection: 'products'
tenantId: string
publishableKeyScopes?: string[]
productId?: string
currentSlug?: string
previousSlug?: string
invalidateListing?: boolean
}
export async function POST(request: Request) {
const invalidation = (await request.json()) as ProductInvalidation
const scopes = (invalidation.publishableKeyScopes ?? ['default']).map(
(publishableKeyScope: string) => ({
tenantId: invalidation.tenantId,
publishableKeyScope,
}),
)
if (invalidation.collection === 'products') {
for (const scope of scopes) {
if (invalidation.invalidateListing) {
revalidateTag(storefrontCacheResources.productListing(scope), 'max')
}
if (invalidation.productId) {
revalidateTag(
storefrontCacheResources.productDetail(scope, {
id: invalidation.productId,
}),
'max',
)
}
for (const slug of [
invalidation.currentSlug,
invalidation.previousSlug,
]) {
if (!slug) continue
revalidateTag(
storefrontCacheResources.productDetail(scope, { slug }),
'max',
)
}
}
}
return Response.json({ ok: true })
}The invalidation payload does not have to perform raw collection reads in the
adapter, but it must carry the public identity needed for the tags it wants to
invalidate. For slug changes, include the previous slug too. If your app cannot
track previous slugs yet, use productListing(scope) as a coarse fallback and
only tag product detail pages with the listing resource when you deliberately
accept that broader invalidation.
Before Production
- Confirm collection and feature availability for the workspace plan.
- Confirm key storage and rotation ownership.
- Confirm failure handling for customer-facing flows.
Do not put Secret Key values in browser bundles, public env vars, analytics events, or generated examples.
Next Actions
- Need exact HTTP contracts: open API.
- Need agent setup: open MCP.
- Need event delivery: open Webhooks.
- Need ready-made commerce flows? See Commerce helpers.