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 entity (product, variant, collection, metaobject, article, or page) to the customer's default list. Saved state is written to D1 and projected into the customer's single save_list metaobject by the savelayer-saved-sync worker. Returns DUPLICATE_SAVE (409) when the entity is already saved.
  • POST /remove — Removes a saved entity from the list. Returns ITEM_NOT_FOUND (404) if the entity is not saved.
  • POST /toggle — Removes if saved, saves if not. Returns the resulting state.
  • GET /list — Lists saved entities 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.