Appearance
App Proxy API
SaveLayer uses Shopify app proxy for the Online Store theme channel.
For method-by-method request/response examples, schemas, and error envelopes, see the SaveLayer marketing documentation API reference at /docs/api-reference (Documentation sidebar).
Route policy
The merchant-facing proxy URL is configured through Shopify app proxy settings. Internally, SaveLayer keeps all proxy handlers under one dedicated route namespace.
Operations:
POST /save— Persists a saved item (product, variant, collection, etc.) to a list. Createssave_listandsave_itemmetaobjects. ReturnsDUPLICATE_SAVE(409) when the item is already saved.POST /remove— Soft-deletes a saved item (setsstatustoremoved). ReturnsITEM_NOT_FOUND(404) if the list or item does not exist.POST /toggle— Removes if saved, saves if not. Returns the resulting state.GET /list— Lists saved items for a context (e.g. wishlist). Supportscontext,limit,cursorquery params. Returns{ items, pageInfo }.POST /batch— Up to 50 operations per request (BatchRequestSchema). The success payload isBatchResultSchema:total,succeeded,failed, andresults(same order asoperations). Each entry is eitherok: truewith aSaveOperationResultorok: falsewith aProxyErrorCode, optionalmessage, andretryable. Partial failures still return HTTP 200 withok: trueat the envelope level; inspectresultsor counts to reconcile UI. Transient Shopify throttling is retried server-side within the batch.GET /is-saved— Returns whether an item is actively saved for a context.
Request validation
All proxy routes are expected to:
- authenticate with Shopify's app proxy validation
- parse input with shared Zod schemas
- return a standard JSON success or error envelope
Error model
The API is documented around structured error codes instead of ad hoc strings. This keeps storefront integrations predictable and allows agencies to handle blocked states consistently.
- AUTH_REQUIRED (401) — Returned when the Shopify app proxy signature is valid but no logged-in storefront customer is present (missing
logged_in_customer_idon the signed URL). Use the default message or route-specific copy viaauthRequiredMessageon the server. Integrations that previously branched onCUSTOMER_REQUIREDfor proxy-only flows should useAUTH_REQUIREDinstead. - CUSTOMER_REQUIRED — Still defined in
@savelayer/contractsfor a possible future semantic distinct fromAUTH_REQUIRED; SaveLayer proxy routes do not emit it today.