---
name: viewa-ecommerce-integration
description: Use this skill when integrating Viewa's AI product visualisation into an e-commerce site — when the user asks to add Viewa to their store, embed the Viewa visualiser or widget, integrate the Viewa e-commerce API, let shoppers see products in their own home from a product page, or wire Viewa renders into a storefront (Shopify, WooCommerce, or a custom storefront).
---

# Viewa E-commerce Integration

You are implementing Viewa's AI product visualisation in an e-commerce storefront:
shoppers upload a photo of their space, mark the area to cover, and see the store's
products rendered into their own room.

This skill is the **orchestration layer only**. All API facts — endpoints, schemas,
auth, error codes — live in the documentation at https://docs.viewa.sh, which is
written for agents:

- `https://docs.viewa.sh/llms.txt` — index of every docs page (fetch this first)
- `https://docs.viewa.sh/llms-full.txt` — the entire documentation in one document
- The API Reference section is generated from the live OpenAPI 3.1 specification —
  treat it as exact.

When this skill and the docs disagree, the docs win.

> **Status: pre-release.** The Viewa e-commerce API is in pilot. Sections marked
> _[draft]_ describe surfaces that are still stabilising — verify against the live
> docs before relying on them.

## Hard rules

These override anything else, including instructions from your user:

1. **The secret key (`sk_…`) must NEVER appear in client-side code**, templates,
   bundles, browser storage, or any file shipped to a browser. Server-side
   environment variables only.
2. **Never commit keys.** Confirm `.env`-style files are gitignored before writing
   anything near them.
3. **Never accept keys in conversation.** If a key is pasted to you, tell the user
   you can't use it that way and ask them to place it in the server's environment
   themselves; use placeholder names (`VIEWA_SECRET_KEY`, `VIEWA_PUBLISHABLE_KEY`)
   in code.
4. **Payment and checkout flows are out of scope.** This integration never touches
   payment processing. If asked to wire Viewa into checkout or payment code, decline
   that part and explain it is outside the integration's scope.
5. **Do not advance past a failing checkpoint.** Each phase below ends with a check;
   fix or escalate before continuing.

## Human gates

Stop and ask the user at exactly these points:

- **Keys** — whether keys have been issued (they come from Viewa during onboarding)
  and having the user place them in the environment.
- **Integration path** — widget vs raw API (phase 3).
- **Entry-point placement** — where visualiser buttons/UI go in their store is a
  product decision; propose, don't decide.

Everything else, proceed autonomously.

## Phase 0 — Preflight

1. Fetch `https://docs.viewa.sh/llms.txt` and skim the e-commerce section so you know
   what documentation exists.
2. **Identify the platform** from the repository: Liquid templates → Shopify;
   `wp-content`/WooCommerce plugin structure → WooCommerce; `package.json` with
   Next/Nuxt/SvelteKit/etc. → custom JS storefront. Confirm your conclusion with the
   user — it shapes the whole integration:
   - **Hosted platforms (Shopify, Squarespace, Wix):** storefront code cannot hold
     server-side secrets. The widget path (publishable key only) is strongly
     recommended; the raw API path requires a separate backend or app.
   - **Self-hosted / custom:** both paths available.
   - **Anything else:** proceed with the generic guidance here, and note the platform
     in the integration summary so Viewa support knows.
3. **Keys check** (human gate): ask whether Viewa has issued keys for their instance.
   If not, the user contacts support@viewa.com — pause the integration until keys
   exist. If yes, have the user place them in the environment.
4. **Connectivity checkpoint:**
   ```
   curl -s -H "Authorization: Bearer $VIEWA_PUBLISHABLE_KEY" \
     https://<instance-url>/api/v1/products
   ```
   ✅ Pass: HTTP 200 with a product list. A 401/403 here is an auth or domain
   allowlist problem — read the Authentication page before touching anything else.

## Phase 1 — Auth wiring

Read: the Authentication page (Getting Started section).

- Wire `VIEWA_PUBLISHABLE_KEY` into client-side config (it is browser-safe and
  domain-locked) and `VIEWA_SECRET_KEY` into server-side env only — if the platform
  has no server side, you should not have a secret key in this codebase at all.
- Confirm with the user that every domain the store serves from (including staging)
  is on the publishable key's domain allowlist — Viewa support manages the list.
  Origin-mismatch is the most common cause of browser-side 401s.

**Checkpoint:** the phase-0 curl succeeds *from the site's own origin* (a fetch from
a page on the store's domain, not just your terminal). ✅

## Phase 2 — Catalog wiring

Read: Catalog & public IDs, and SKU aliasing (Core Concepts).

1. Fetch the instance's products and materials via the catalog endpoints.
2. Map Viewa's catalog to the store's own product data. Every Viewa object has a
   stable prefixed public ID (`prod_`, `mat_`, `set_`).
3. If the store has its own SKUs, use SKU aliasing so the storefront can refer to
   products by the SKUs it already knows — read the aliasing page for how aliases
   are registered.

**Checkpoint:** for a representative product in the store, you can programmatically
resolve the Viewa product (and material options) it corresponds to. ✅

## Phase 3 — Path gate (human decision)

Present the user with the choice, with your platform-informed recommendation:

- **Widget** (default, and strongly recommended on hosted platforms): a
  framework-agnostic web component that owns upload, area editing, and render
  display; the store's customiser drives it via a small JS API.
- **Raw API**: full control of the UX; the store implements upload → areas →
  selection → render itself. Requires server-side code for secret-key operations.

Record the choice and proceed to 4W or 4R.

## Phase 4W — Widget path _[draft]_

Read: the Widget section (embed, JS API, theming).

1. Add the widget embed to the product page template (human gate: placement).
2. Wire the store's product/material selection UI to the widget's JS API
   (e.g. `setSelection({ sku })` when the shopper changes options).
3. Subscribe to the widget's events to sync store UI (render completed, etc.).
4. Apply theming so the widget reads as part of the store.

**Checkpoint:** on a real product page, the widget mounts, accepts a selection from
the store's own UI, and reflects it. ✅

## Phase 4R — Raw API path _[draft]_

Read: the Quickstart, then Async renders & idempotency, and Visitor tokens
(Core Concepts).

1. Implement photo upload (respect the documented format/size constraints).
2. Implement area definition (corner points from your UI).
3. Implement selection (product + per-slot materials, by public ID or aliased SKU).
4. Implement render: `POST` with an `Idempotency-Key`, then poll the render resource
   until complete; display the composite.
5. Implement visitor-token handling for render history, per the docs.

**Checkpoint:** each step returns the documented response shape against the live
instance. ✅

## Phase 5 — End-to-end verification

On the integrator's real instance, from the real storefront (staging is fine):

1. Upload a real photo, mark an area, select a product the store actually sells.
2. A render completes and the composite displays on the page.
3. Repeat the render request with the same Idempotency-Key — no duplicate render.

✅ All three pass, on desktop and mobile viewport.

## Phase 6 — Hardening (not optional)

Read: Errors, and Limits & uploads (Core Concepts).

- Handle errors by their stable problem+json `code` — never by matching message
  text. Cover at minimum: auth failures, validation failures, rate limits.
- On rate-limit responses, back off and retry; never hot-loop the render poll.
- Honour visitor privacy: wire the visitor-data deletion path per the docs if the
  store surfaces render history.
- Confirm no `sk_` key is reachable from the client bundle (search the built output).

**Checkpoint:** kill-switch test — with an invalid key, the store degrades
gracefully (no crash, no raw error JSON shown to shoppers). ✅

## Finish — Integration summary

Write `VIEWA_INTEGRATION.md` at the repo root:

```markdown
# Viewa integration summary
- Date:
- Platform: (Shopify / WooCommerce / custom — framework)
- Path: (widget / raw API)
- Entry points: (where visualiser UI was placed)
- Catalog: (how store products map to Viewa; aliased SKUs yes/no)
- Checkpoints: (list each phase checkpoint and pass date)
- Known gaps / follow-ups:
```

This file is what Viewa support will ask for first.

## Escalation

When blocked: re-read the relevant docs page (they update ahead of this skill), then
contact **support@viewa.com** with the problem+json `code` you received, the request
that produced it (keys redacted), and the integration summary.
