Developers · Plunk Studio

Shopper App API

Base URL https://share.plunk.io. The API behind the consumer-facing visualizer apps: use the one-line embed, or call the same endpoints from your own shopper experience.

Keys & authentication

Keys are minted in Studio under Visualizer Apps; the raw key is shown once, at creation. Every API request carries it in the x-visualizer-key header. There is no shopper login: the key is the app's identity.

Key typePrefixWhere it runsRules the API enforces
Device kk_ Controlled in-store displays and showroom tablets you manage No origin check, no daily cap by default; the device itself is the security boundary.
Site sk_ Embedded in your public website, visible by design Origin-locked to the domains registered on the key, daily render cap, per-IP burst limiting, and optional capture-first (an email is required before a render is spent).

Each key is also scoped to surfaces: com (the COM/COL Visualizer), sectional (the Sectional Builder), or both. Your app declares which surface it is with the x-surface header (or ?surface=); requests outside the key's scope are refused with 403.

Website embed

The fastest integration is no integration: one script tag renders the full app in an auto-sizing iframe where the tag sits. Use a site key.

<script src="https://share.plunk.io/embed.js"
        data-key="sk_XXXXXXXXXXXX"
        data-surface="com"></script>

<!-- data-surface: "com" (COM/COL Visualizer) or "sectional" (Sectional Builder)
     data-height:  optional starting height in px (default 720); the frame then
                   follows the app's reported height automatically -->

GET/embed?key=&surface=

The iframe fallback for CMSes that strip scripts: point a plain <iframe> at it. The response carries a per-key Content-Security-Policy: frame-ancestors header listing the key's registered domains (and their subdomains), so the browser itself refuses to frame your app anywhere else. A key with no registered origins embeds anywhere. Site keys only; a device key here returns 403.

App config

Building a custom shopper app? Bootstrap from the key: branding and behavior flags come from your workspace, not your code.

GET/visualizer/config

curl https://share.plunk.io/visualizer/config \
  -H "x-visualizer-key: kk_XXXXXXXXXXXX"

{
  "storeName": "Main Street Showroom",
  "brand": "Your Company Name",
  "keyType": "device",
  "captureFirst": false,
  "surfaces": ["com", "sectional"],
  "tenantId": "a123456789012"
}

tenantId lets an app launched with only a key resolve its catalog provisioning without a slug in the URL.

Visualizer renders com

The COM/COL flow: the shopper picks a piece and a material (including a photo of their own fabric or leather) and sees the piece rendered in it. Renders ride your workspace's render pipeline and spend its credit pool.

POST/visualizer/render

FieldTypeNotes
productImageUrlstringRequired. The piece to render
swatchUrlstringRequired. The material: a swatch image, or the shopper's own photo
aspectRatiostringDefault 1:1
emailstringRequired up front only when the key is capture-first
curl -X POST https://share.plunk.io/visualizer/render \
  -H "x-visualizer-key: sk_XXXXXXXXXXXX" -H "Content-Type: application/json" \
  -d '{
        "productImageUrl": "https://your.cdn/pieces/track-arm-sofa.jpg",
        "swatchUrl": "https://your.cdn/swatches/customer-linen.jpg"
      }'

{
  "runId": "9f4c1e07b2…"
}
StatusCodeWhen
422capture_requiredCapture-first key, no valid email in the body
429rate_limitedPer-IP burst limit on site keys
429daily_cap_reachedThe key's daily render cap is spent
402The workspace is out of render credits

GET/visualizer/render/{runId}

Poll until the render lands.

{
  "status": "processing",
  "outputUrl": null
}

# …and when it's done:
{
  "status": "completed",
  "outputUrl": "https://…/render.png"
}

Lead capture

The take-home moment: the shopper leaves an email, gets a branded share link to their render, and the lead, with product and material interest attached, lands in your workspace.

POST/visualizer/capture

FieldTypeNotes
emailstringRequired.
renderUrlstringWith it, a 60-day share link is minted and returned
productSkustringInterest attribution
productNamestringTitles the share link
fabricNamestringInterest attribution
{
  "ok": true,
  "shareUrl": "https://share.plunk.io/v/a81f04c2be55d1"
}

Sectional Builder sectional

The configurator flow: bootstrap the unit and fabric libraries, render the shopper's exact configuration, and save it to a branded board. Approved popular combinations serve instantly from cache; new ones render on demand, unless your workspace runs in curated mode, where only staff-published configurations serve.

GET/sb/bootstrap

Everything the app needs to draw itself. price is null when the key hides pricing (trade and designer apps); builderMode is open or curated; publishedConfigs is populated in curated mode.

{
  "brand": "Your Company Name",
  "captureFirst": false,
  "showPricing": true,
  "builderMode": "open",
  "units": [
    {
      "sku": "3772-ALC",
      "series": "3772",
      "title": "Armless Chair",
      "unitType": "armless",
      "armSide": null,
      "attachEdges": "left,right",
      "gridW": 1,
      "gridH": 1,
      "widthIn": 32.0,
      "price": 899.00,
      "imageUrl": "https://…/3772-alc-neutral.png"
    }
  ],
  "fabrics": [
    {
      "fabricKey": "2001-05",
      "fabricName": "Stone",
      "swatchUrl": "https://…/2001-05.jpg",
      "family": "Performance weave"
    }
  ],
  "options": [
    {
      "kind": "leg",
      "optionKey": "espresso",
      "name": "Espresso legs",
      "swatchUrl": "https://…/leg-espresso.jpg",
      "sort": 1
    }
  ],
  "publishedConfigs": []
}

POST/sb/render

Render a configuration: 2–8 units placed on the layout grid, a fabric, and any selected options. A cached, approved configuration returns ready immediately; otherwise the render starts and you poll. In curated mode an unpublished combination returns 403 with status: "unavailable".

curl -X POST https://share.plunk.io/sb/render \
  -H "x-visualizer-key: sk_XXXXXXXXXXXX" -H "x-surface: sectional" \
  -H "Content-Type: application/json" \
  -d '{
        "units": [
          { "sku": "3772-LAL", "gx": 0, "gy": 0 },
          { "sku": "3772-ALC", "gx": 1, "gy": 0 },
          { "sku": "3772-COR", "gx": 2, "gy": 0 },
          { "sku": "3772-RAC", "gx": 2, "gy": 1 }
        ],
        "fabricKey": "2001-05",
        "options": { "leg": "espresso" }
      }'

# Cache hit. An approved render serves instantly:
{
  "status": "ready",
  "url": "https://…/hero.png",
  "configHash": "b04c…"
}

# Cache miss: 202, poll /sb/render/{renderId}; `fallback` carries the
# per-unit images so the app can show its exploded view meanwhile:
{
  "status": "rendering",
  "renderId": "f2a91c…",
  "configHash": "b04c…",
  "fallback": {
    "units": [
      { "sku": "3772-LAL", "url": "https://…/3772-lal-neutral.png" }
    ]
  },
  "schematicUrl": "https://…/schematic.png"
}

GET/sb/render/{renderId}

Poll the render. Every result is fidelity-checked before it can serve; pending_review means it rendered but is waiting on staff approval and will not serve until approved.

{ "status": "rendering" }
{ "status": "pending_review" }
{ "status": "failed", "verifyNote": "…" }
{
  "status": "ready",
  "url": "https://…/hero.png"
}

POST/sb/save

Save the configuration, and when the shopper leaves an email, mint a branded board with the hero render and email them the link. If the render is still in flight, pass the renderId from /sb/render: the board minting waits for the render and the email goes out when it's ready.

FieldTypeNotes
unitsarrayRequired. Same placement objects as /sb/render
fabricKeystringRequired.
optionsobjectSelected options, kind → key
titlestringBoard title, default "My sectional"
emailstringCaptures the lead + emails the board
renderIdstringAn in-flight render to attach the board to
{
  "configId": "8c1b04d2ee…",
  "totalPrice": 4796.00,
  "boardUrl": "https://share.plunk.io/b/1f8e02aa41cd6b",
  "pendingBoard": false,
  "message": null
}

# totalPrice is null when the key hides pricing. If the render was still
# in flight: pendingBoard = true and the board link is emailed on approval.
Ready to build?

Keys come with Studio Scale.

The shopper apps and their API are part of the Studio Scale plan. Want a custom shopper experience built on these rails? That's what we do.

See pricing Talk to us