Alter Product Public API integration Public API is designed for server-to-server integrations with storefronts, commerce backends, WordPress/WooCommerce plugins and external production workflows.
Table of Content https://alterproduct.com/public-api/v1
Create API credentials in the e-commerce settings panel. The Access Token is shown once, so store it immediately in your backend secret storage.
Keep the Access Key and Access Token on your server. Authenticated endpoints reject browser-origin calls that include Origin or Referer headers.
Credentials can be scoped. Use GET /auth/check to verify the active storefront, plan capabilities and scopes returned for the credential.
x-alter-access-key: YOUR_API_KEY
x-alter-access-token: YOUR_API_TOKENParameter Required Details x-alter-access-keyyes Public credential identifier. x-alter-access-tokenyes Secret token paired with the access key. x-alter-client-fingerprintno Optional stable fingerprint for embed session rate limiting. Authorizationruntime only Bearer token returned by POST /embed/session, used by /runtime/bootstrap.
Use the auth check endpoint before enabling synchronization or embed features in a live integration.
GET https://alterproduct.com/public-api/v1/auth/check Example request (fetch)
const response = await fetch ( 'https://alterproduct.com/public-api/v1/auth/check' , {
method : 'GET' ,
headers : {
'x-alter-access-key' : process . env . ALTER_ACCESS_KEY ,
'x-alter-access-token' : process . env . ALTER_ACCESS_TOKEN
}
} ) ;
const payload = await response . json ( ) ;
if ( ! response . ok ) {
throw new Error ( payload ?. code || payload ?. error || ` Alter API ${ response . status } ` ) ;
}
console . log ( payload ) ; Example response
{
"ok" : true ,
"message" : "success" ,
"storefrontId" : 12 ,
"userOwnerId" : 34 ,
"credentialId" : 56 ,
"scopes" : [ "orders:read" , "orders:write" , "products:read" ] ,
"plan" : {
"requiredPlan" : "Business" ,
"currentPlanName" : "Business" ,
"eligible" : true ,
"runtimeFlags" : {
"viewer" : true ,
"configurator" : true ,
"customizer" : true
} ,
"limits" : {
"activeRuntimeBindingsLimit" : 100 ,
"monthlyReassignmentLimit" : 1000 ,
"monthlyEmbedTokenLimit" : 50000
}
}
} The helper below is used by the remaining examples. It is plain fetch and can run in Node.js 18+ or any server runtime that provides fetch.
const ALTER_API_BASE = 'https://alterproduct.com/public-api/v1' ;
const authHeaders = {
'x-alter-access-key' : process . env . ALTER_ACCESS_KEY ,
'x-alter-access-token' : process . env . ALTER_ACCESS_TOKEN
} ;
async function alterFetch ( path , options = { } ) {
const response = await fetch ( ` ${ ALTER_API_BASE } ${ path } ` , {
... options ,
headers : {
... authHeaders ,
... ( options . body ? { 'Content-Type' : 'application/json' } : { } ) ,
... options . headers
}
} ) ;
const payload = await response . json ( ) . catch ( ( ) => null ) ;
if ( ! response . ok ) {
throw new Error ( payload ?. code || payload ?. error || ` Alter API ${ response . status } ` ) ;
}
return payload ;
} The table below mirrors the public routes mounted in backend-public-api/app.js. Paths are shown with the public proxy prefix used by external integrations.
Method Endpoint Description Access GET/public-api/healthzService health check. publicGET/public-api/v1/auth/checkValidates credentials and returns storefront, scopes and plan capabilities. any authenticated credentialGET/public-api/v1/customer-ordersReturns a paginated and filterable list of customer orders. orders:readGET/public-api/v1/customer-orders/:idReturns a single customer order with configured product items. orders:readPOST/public-api/v1/customer-orders/batchReturns up to 100 orders by ID. orders:readPATCH/public-api/v1/customer-orders/:id/statusUpdates the order status. orders:writePATCH/public-api/v1/customer-orders/:orderId/quantityUpdates selected order detail quantities. orders:writePATCH/public-api/v1/customer-orders/:orderId/quantity/allSets one quantity for every item in an order. orders:writeDELETE/public-api/v1/customer-orders/:idDeletes a customer order owned by the storefront owner. orders:writeGET/public-api/v1/productsReturns storefront products/designs with embed availability and media URLs. products:readGET/public-api/v1/products/:idReturns one storefront product/design. products:readPOST/public-api/v1/embed/sessionIssues a short-lived JWT for designer/viewer/configurator/customizer runtimes. embed:session:createGET/public-api/v1/runtime/bootstrapResolves runtime context from an embed JWT. Bearer embed tokenGET/public-api/v1/assetsLists asset catalog items for the requested type. any authenticated credentialGET/public-api/v1/assets/:type/:assetIdReturns an asset manifest with downloadable file roles. any authenticated credentialGET/public-api/v1/assets/:type/:assetId/files/:roleDownloads an asset file by role. any authenticated credentialGET/public-api/v1/design-importsLists importable Alter-hosted designs. authenticated credential, Business plan requiredGET/public-api/v1/design-imports/:idReturns a design import payload and file descriptors. authenticated credential, Business plan requiredGET/public-api/v1/design-imports/:id/files/:fileIdDownloads a file from a design import descriptor. authenticated credential, Business plan requiredGET/public-api/v1/file/public/products/:productId/:sizeReturns a public product preview. Size must be small.png, medium.png or big.png. publicGET/public-api/v1/file/protected/:keyReturns a protected file by storage key. signed URL or files:readGET/public-api/v1/fontsReturns all available fonts. publicGET/public-api/v1/currenciesReturns all currencies. publicPOST/public-api/v1/runtime-bindings/sync-from-wordpressCreates or updates runtime bindings from WordPress product mappings. any authenticated credentialPATCH/public-api/v1/runtime-bindings/:idPatches a runtime binding. any authenticated credentialPOST/public-api/v1/runtime-bindings/:id/activateActivates a runtime binding. any authenticated credentialPOST/public-api/v1/runtime-bindings/:id/deactivateDeactivates a runtime binding. any authenticated credentialPOST/public-api/v1/wp-connect/exchangeExchanges a WordPress auto-connect handoff code for API credentials. one-time handoff code
Customer order endpoints let an external store read configured line items, update quantities, move an order through fulfillment statuses and remove abandoned orders.
Parameter Required Details namenoSearches design name and numeric order ID. category_idnoFilters by product category ID. order_statusnoOne of the allowed order statuses. offsetnoDefault 0. Must be >= 0. limitnoDefault 9 for this controller, maximum 50. order_bynoid, created_at or design_name. directionnoASC or DESC.
Example request (fetch)
const params = new URLSearchParams ( {
limit : '20' ,
offset : '0' ,
order_status : 'shopping_cart' ,
order_by : 'created_at' ,
direction : 'DESC'
} ) ;
const orders = await alterFetch ( ` /customer-orders? ${ params . toString ( ) } ` ) ;
const order = await alterFetch ( '/customer-orders/123' ) ;
const batch = await alterFetch ( '/customer-orders/batch' , {
method : 'POST' ,
body : JSON . stringify ( {
customerOrderIds : [ 123 , 124 , 125 ]
} )
} ) ; Allowed values
Status Description shopping_cartCart flow; customer can still edit the configuration. editableOrder remains editable by the customer. paidOrder is paid and ready for fulfillment. processingOrder is in fulfillment. completedOrder has been fulfilled. cancelledOrder was cancelled.
Example request (fetch)
await alterFetch ( '/customer-orders/123/status' , {
method : 'PATCH' ,
body : JSON . stringify ( {
status : 'processing'
} )
} ) ;
await alterFetch ( '/customer-orders/123/quantity' , {
method : 'PATCH' ,
body : JSON . stringify ( {
items : [
{ orderDetailId : 987 , quantity : 3 }
]
} )
} ) ;
await alterFetch ( '/customer-orders/123/quantity/all' , {
method : 'PATCH' ,
body : JSON . stringify ( {
quantity : 2
} )
} ) ;
await alterFetch ( '/customer-orders/123' , {
method : 'DELETE'
} ) ; Example response
{
"order" : {
"id" : 123 ,
"customizerId" : 381 ,
"orderStatus" : "shopping_cart" ,
"createdAt" : "2026-05-28T10:15:00.000Z" ,
"customizerOrderURL" : "https://alterproduct.com/app/customizer/381/123" ,
"productItems" : [
{
"id" : 987 ,
"model3d" : { "id" : 381 } ,
"size" : {
"id" : 395 ,
"name" : { "pl" : "M" , "en" : "M" } ,
"measureSize" : null
} ,
"material" : {
"id" : 2 ,
"name" : { "pl" : "Bawełna" , "en" : "Cotton" }
} ,
"printType" : {
"id" : 1 ,
"name" : { "pl" : "DTG" , "en" : "DTG" }
} ,
"color" : {
"id" : 418 ,
"name" : { "pl" : "Domyślny" , "en" : "Default" } ,
"hex" : "#ffffff"
} ,
"variant" : {
"id" : 531 ,
"metadata" : null ,
"stockQuantity" : 25
} ,
"unitPrice" : { "value" : 12.5 , "currency" : "EUR" } ,
"totalPrice" : { "value" : 37.5 , "currency" : "EUR" } ,
"quantity" : 3
}
] ,
"customizerName" : "Men's T-Shirt" ,
"productGroup" : {
"id" : 4 ,
"name" : { "pl" : "Koszulka" , "en" : "T-Shirt" }
} ,
"totalPrice" : { "value" : 37.5 , "currency" : "EUR" }
}
} Product endpoints return storefront designs that can be embedded as viewer, configurator or customizer experiences.
Parameter Required Details namenoSearches product/design name. customizernotrue or false. offsetnoDefault 0. Must be >= 0. limitnoDefault 9, maximum 50. order_bynoid, name or created_at. directionnoASC or DESC.
Example request (fetch)
const params = new URLSearchParams ( {
limit : '20' ,
offset : '0' ,
name : 't-shirt' ,
customizer : 'true' ,
order_by : 'created_at' ,
direction : 'DESC'
} ) ;
const products = await alterFetch ( ` /products? ${ params . toString ( ) } ` ) ;
const product = await alterFetch ( '/products/381' ) ; Example response
{
"products" : {
"items" : [
{
"id" : 381 ,
"name" : "Men's T-Shirt" ,
"createdAt" : "2026-01-03T23:55:05.000Z" ,
"productId" : 4 ,
"media" : {
"img" : {
"big" : "https://alterproduct.com/public-api/v1/file/public/products/4/big.png" ,
"medium" : "https://alterproduct.com/public-api/v1/file/public/products/4/medium.png" ,
"small" : "https://alterproduct.com/public-api/v1/file/public/products/4/small.png"
} ,
"mockups" : [ ]
} ,
"storefrontProduct" : {
"id" : 89 ,
"idUserDesign" : 381 ,
"shareAccess" : "public" ,
"isCustomizer" : 1
} ,
"runtimeBindings" : [
{
"id" : 42 ,
"runtimeType" : "customizer" ,
"status" : "active" ,
"externalProductId" : "wc_123"
}
] ,
"embeddable" : {
"viewer" : true ,
"configurator" : true ,
"customizer" : true
}
}
] ,
"total" : 1
}
} Create a short-lived embed token from your server, pass it to the iframe/runtime, then let the runtime call bootstrap with a Bearer token.
Parameter Required Details runtimeBindingIdrecommendedPreferred identifier for active runtime bindings. toolrequired without runtimeBindingIddesigner, viewer, configurator or customizer. originyesOrigin where the embed is rendered, for example https://yourstore.com. designIdone identifierAlter Product design ID. Do not combine with orderId. orderIdone identifierCustomizer order ID. Only valid for customizer. cartKey + cartModenoCustomizer-only cart context. cartMode is view or edit.
Example request (fetch)
const session = await alterFetch ( '/embed/session' , {
method : 'POST' ,
headers : {
'x-alter-client-fingerprint' : '9f1b7a5e4b3c2d1f9f1b7a5e4b3c2d1f'
} ,
body : JSON . stringify ( {
runtimeBindingId : 42 ,
origin : 'https://yourstore.com'
} )
} ) ;
const bootstrapResponse = await fetch ( 'https://alterproduct.com/public-api/v1/runtime/bootstrap' , {
method : 'GET' ,
headers : {
Authorization : ` Bearer ${ session . token } `
}
} ) ;
const bootstrap = await bootstrapResponse . json ( ) ;
console . log ( { session , bootstrap } ) ; Notes
await alterFetch ( '/embed/session' , {
method : 'POST' ,
body : JSON . stringify ( {
tool : 'customizer' ,
origin : 'https://yourstore.com' ,
orderId : 123
} )
} ) ;
await alterFetch ( '/embed/session' , {
method : 'POST' ,
body : JSON . stringify ( {
tool : 'viewer' ,
origin : 'https://yourstore.com' ,
designId : 381
} )
} ) ; Example response
{
"token" : "eyJhbGciOiJIUzI1NiIsImtpZCI6IjEifQ..." ,
"expiresIn" : 900 ,
"kid" : "1" ,
"mode" : "design" ,
"runtimeBindingId" : 42 ,
"runtimeType" : "customizer"
} Runtime bootstrap
{
"runtimeBindingId" : 42 ,
"designId" : 381 ,
"productId" : "wc_123" ,
"runtimeType" : "customizer" ,
"storageMode" : "wordpress_local" ,
"manifestUrl" : "https://yourstore.com/wp-content/uploads/alter/381/manifest.json" ,
"assetBaseUrl" : "https://yourstore.com/wp-content/uploads/alter/381/" ,
"manifestHash" : "a3b1..." ,
"planCapabilities" : {
"viewer" : true ,
"configurator" : true ,
"customizer" : true
} ,
"cartKey" : null ,
"cartMode" : null ,
"orderId" : null
} The asset catalog exposes product source assets, backgrounds, environments, graphic library items, design templates and mockup assets. List endpoints return lightweight descriptors; detail endpoints include file manifests.
Type Description productsBase product assets, previews, 3D models, material and texture descriptors. backgroundsStatic viewer backgrounds. environmentsEnvironment maps and preview images. image_libraryGraphic library assets, including storefront-scoped graphics. design_templatesDesign template previews and layer file references. Supports product_id filter. mockupsMockup generator assets, backgrounds and overlay maps. Supports product_id filter.
Example request (fetch)
const assets = await alterFetch ( '/assets?' + new URLSearchParams ( {
type : 'products' ,
limit : '20' ,
offset : '0' ,
search : 'mug'
} ) ) ;
const details = await alterFetch ( '/assets/products/4' ) ;
const fileResponse = await fetch (
'https://alterproduct.com/public-api/v1/assets/products/4/files/preview_medium' ,
{
headers : authHeaders
}
) ;
const fileBlob = await fileResponse . blob ( ) ; Example response
{
"type" : "products" ,
"items" : [
{
"assetType" : "products" ,
"assetId" : "4" ,
"title" : "Mug 450ml" ,
"slug" : "product-4" ,
"description" : "Base product 4" ,
"primaryRole" : "preview_big" ,
"fileCount" : 8 ,
"remoteVersion" : "1.0" ,
"thumbnail" : {
"role" : "preview_small" ,
"fileName" : "product-4-preview-small.png" ,
"mime" : "image/png" ,
"downloadPath" : "/v1/assets/products/4/files/preview_small"
} ,
"metadata" : {
"productCategoryId" : 2 ,
"productModelCount" : 1 ,
"isDedicated" : false
}
}
] ,
"total" : 1 ,
"limit" : 20 ,
"offset" : 0
} Design imports expose Alter-hosted designs and their files for external production or migration flows. Business plan eligibility is checked by the API.
Parameter Required Details searchnoSearches design title or ID. offsetnoDefault 0. limitnoDefault 20, maximum 100.
Example request (fetch)
const imports = await alterFetch ( '/design-imports?' + new URLSearchParams ( {
limit : '20' ,
offset : '0' ,
search : 'mug'
} ) ) ;
const details = await alterFetch ( '/design-imports/381' ) ;
const fileId = details . files [ 0 ] . id ;
const fileResponse = await fetch (
` https://alterproduct.com/public-api/v1/design-imports/381/files/ ${ fileId } ` ,
{
headers : authHeaders
}
) ;
const fileBlob = await fileResponse . blob ( ) ; Example response
{
"eligible" : true ,
"requiredPlan" : "Business" ,
"currentPlanName" : "Business" ,
"designs" : [
{
"id" : 381 ,
"title" : "Men's T-Shirt" ,
"createdAt" : "2026-01-03T23:55:05.000Z" ,
"sourceStorefrontId" : 12 ,
"productId" : 4 ,
"productName" : {
"pl" : "Koszulka" ,
"en" : "T-Shirt"
} ,
"storageMode" : "alter" ,
"runtimeStatus" : {
"designer" : true ,
"viewer" : true ,
"configurator" : true ,
"customizer" : true
} ,
"thumbnail" : {
"kind" : "design-mockup" ,
"fileId" : "7df7..." ,
"downloadPath" : "/v1/design-imports/381/files/7df7..."
}
}
] ,
"total" : 1
} Public preview files are browser-friendly. Protected files require a signed URL or an API credential with files:read. Fonts and currencies are public read endpoints.
Endpoint Access Details /file/public/products/:productId/small.pngpublicSmall product preview. /file/public/products/:productId/medium.pngpublicMedium product preview. /file/public/products/:productId/big.pngpublicLarge product preview. /file/protected/:keysigned URL or files:readProtected object storage file. /fontspublicArray of font records. /currenciespublicArray of currency records.
Example request (fetch)
const publicPreview = await fetch (
'https://alterproduct.com/public-api/v1/file/public/products/4/medium.png'
) ;
const protectedFile = await fetch (
'https://alterproduct.com/public-api/v1/file/protected/user_34/381/design/mockup-large.webp' ,
{
headers : authHeaders
}
) ;
const fonts = await fetch ( 'https://alterproduct.com/public-api/v1/fonts' ) . then ( ( res ) => res . json ( ) ) ;
const currencies = await fetch ( 'https://alterproduct.com/public-api/v1/currencies' ) . then ( ( res ) => res . json ( ) ) ; Example response
[
{
"id" : 1 ,
"family" : "Inter" ,
"source" : "google" ,
"category" : "sans-serif" ,
"variants" : [ "regular" , "600" , "700" ] ,
"subsets" : [ "latin" ] ,
"version" : "v19" ,
"menu" : "Inter" ,
"files" : {
"regular" : "https://..."
}
}
] [
{
"id" : 1 ,
"code" : "EUR" ,
"name" : "Euro" ,
"symbol" : "€" ,
"decimalPlaces" : 2
}
] Runtime bindings connect external commerce products to Alter Product designs and runtime types. They are primarily used by WordPress/WooCommerce integrations and advanced storefront backends.
Parameter Required Details designIdnoAlter Product design ID owned by the storefront. externalProductIdyes for syncExternal product ID, for example a WooCommerce product ID. runtimeTypeyes for syncviewer, configurator or customizer. statusnodraft, active, inactive, archived or legacy_active. legacyStorefrontProductIdnoOptional legacy mapping ID. legacyBindingMetanoOptional JSON metadata, for example manifestHash.
Example request (fetch)
await alterFetch ( '/runtime-bindings/sync-from-wordpress' , {
method : 'POST' ,
body : JSON . stringify ( {
bindings : [
{
externalProductId : 'wc_123' ,
runtimeType : 'customizer' ,
status : 'active' ,
designId : 381 ,
legacyBindingMeta : {
manifestHash : 'a3b1...'
}
}
]
} )
} ) ;
await alterFetch ( '/runtime-bindings/42' , {
method : 'PATCH' ,
body : JSON . stringify ( {
status : 'inactive'
} )
} ) ;
await alterFetch ( '/runtime-bindings/42/activate' , { method : 'POST' } ) ;
await alterFetch ( '/runtime-bindings/42/deactivate' , { method : 'POST' } ) ; wordpress_local
await alterFetch ( '/runtime-bindings/sync-from-wordpress' , {
method : 'POST' ,
body : JSON . stringify ( {
bindings : [
{
externalProductId : 'wc_123' ,
runtimeType : 'viewer' ,
status : 'active' ,
externalDesign : {
externalDesignKey : 'wp-design-381' ,
productId : 4 ,
title : 'WooCommerce local design' ,
manifestUrl : 'https://yourstore.com/wp-content/uploads/alter/381/manifest.json' ,
assetBaseUrl : 'https://yourstore.com/wp-content/uploads/alter/381/' ,
manifestHash : 'a3b1...' ,
sourceMeta : {
pluginVersion : '1.2.0'
}
}
}
]
} )
} ) ; Example response
{
"message" : "runtimeBinding.syncCompleted" ,
"runtimeBindings" : [
{
"id" : 42 ,
"designId" : 381 ,
"externalProductId" : "wc_123" ,
"runtimeType" : "customizer" ,
"status" : "active"
}
]
} The WordPress connect exchange endpoint consumes a one-time handoff code and returns API credentials to the plugin. It is not a general-purpose credential creation endpoint.
Example request (fetch)
const response = await fetch ( 'https://alterproduct.com/public-api/v1/wp-connect/exchange' , {
method : 'POST' ,
headers : {
'Content-Type' : 'application/json'
} ,
body : JSON . stringify ( {
code : 'ONE_TIME_HANDOFF_CODE' ,
storeUrl : 'https://yourstore.com/' ,
siteOrigin : 'https://yourstore.com' ,
codeVerifier : 'PKCE_CODE_VERIFIER_32_TO_128_CHARS'
} )
} ) ;
const credentials = await response . json ( ) ; Example response
{
"message" : "wpConnect.exchange.ok" ,
"accessKey" : "generated-access-key" ,
"accessToken" : "generated-access-token" ,
"storefrontId" : 12
} Most controller errors are normalized to a code response. Authentication middleware and rate limiters can return an error response instead.
// Controller error
{
"code" : "assetCatalog.invalidType"
}
// Auth middleware or rate limit
{
"error" : "Unauthorized"
}
{
"error" : "Too Many Requests"
} Type Limit Window Global600 requests 60 seconds GET /auth/check60 requests 60 seconds Orders read/products read300 requests 60 seconds Orders write/embed sessions/runtime bindings120 requests 60 seconds Assets/design imports read180 requests 60 seconds Fonts300 requests 60 seconds WP connect exchange30 requests 60 seconds