Skip to content

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. Creates save_list and save_item metaobjects. Returns DUPLICATE_SAVE (409) when the item is already saved.
  • POST /remove — Soft-deletes a saved item (sets status to removed). Returns ITEM_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). Supports context, limit, cursor query params. Returns { items, pageInfo }.
  • POST /batch — Up to 50 operations per request (BatchRequestSchema). The success payload is BatchResultSchema: total, succeeded, failed, and results (same order as operations). Each entry is either ok: true with a SaveOperationResult or ok: false with a ProxyErrorCode, optional message, and retryable. Partial failures still return HTTP 200 with ok: true at the envelope level; inspect results or 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_id on the signed URL). Use the default message or route-specific copy via authRequiredMessage on the server. Integrations that previously branched on CUSTOMER_REQUIRED for proxy-only flows should use AUTH_REQUIRED instead.
  • CUSTOMER_REQUIRED — Still defined in @savelayer/contracts for a possible future semantic distinct from AUTH_REQUIRED; SaveLayer proxy routes do not emit it today.