{"openapi":"3.1.0","info":{"title":"ShowroomPro agent API","version":"2026-10-13","summary":"Create an embeddable AI ring builder for any website, then receive its leads.","description":"Public endpoints for coding agents and developers.\n\n1. `POST /api/v1/sandboxes` creates a working ring builder with no account or API key. Paste one of the returned snippets into the site, then give the jeweler the `claimUrl`.\n2. Unclaimed sandboxes render 3 AI designs in total, send no emails, and expire after 14 days. Claiming starts a 7-day free trial and keeps the same token, so the pasted snippet keeps working.\n3. After claiming, the jeweler can add a webhook (Settings → Integrations) to receive `lead.created`, `design.generated` and `quote.requested` events, and read full sessions from `GET /api/webhooks/sessions/{id}`.\n\nDocs: https://www.showroompro.ai/docs · Agent index: https://www.showroompro.ai/llms.txt","contact":{"name":"ShowroomPro support","email":"support@showroompro.ai","url":"https://www.showroompro.ai/docs"}},"externalDocs":{"description":"ShowroomPro developer docs","url":"https://www.showroompro.ai/docs"},"servers":[{"url":"https://www.showroompro.ai","description":"Production. Always use the www host: the apex domain redirects, which turns POST into GET."}],"tags":[{"name":"Sandboxes","description":"Instant, claimable ring builders for agents. No authentication."},{"name":"Webhook sessions","description":"Read design sessions after a webhook. Bearer = your webhook signing secret."}],"paths":{"/api/v1/sandboxes":{"get":{"tags":["Sandboxes"],"operationId":"describeSandboxApi","summary":"Describe the sandbox API","description":"Self-describing contract: request body fields and current limits.","responses":{"200":{"description":"Contract","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxApiDescription"}}}}}},"post":{"tags":["Sandboxes"],"operationId":"createSandbox","summary":"Create a claimable ring builder","description":"Creates an unclaimed ring builder and returns its quiz token, embed snippets, a public quiz URL and a private claim URL. CORS is open. The claimUrl is a bearer secret: hand it to the jeweler only.","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxCreateRequest"},"example":{"businessName":"Atelier Rose Jewelers","websiteUrl":"https://atelierrose.example","platform":"shopify","source":"claude-code"}}}},"responses":{"201":{"description":"Sandbox created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sandbox"}}}},"400":{"description":"Invalid input (for example a malformed websiteUrl)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: 5 sandboxes per IP per hour, 20 per day (code rate_limited), or a global pause (code capacity_exhausted).","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error. Safe to retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sandboxes/status/{token}":{"get":{"tags":["Sandboxes"],"operationId":"getSandboxStatus","summary":"Check a sandbox by quiz token","description":"Reports whether the sandbox was claimed, whether the widget has been seen live on a real domain, and remaining renders. The quiz token is already public on the site, so this needs no auth.","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"The sandbox's quizToken."}],"responses":{"200":{"description":"Status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxStatus"}}}},"404":{"description":"No sandbox uses this token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/webhooks/sessions/{id}":{"get":{"tags":["Webhook sessions"],"operationId":"getWebhookSession","summary":"Read a design session","description":"Returns the full session referenced by a webhook's sessionUrl, including every iteration's images. Only sessions owned by the account whose webhook secret is used are readable.","security":[{"webhookSecret":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"sessionId from a webhook payload."}],"responses":{"200":{"description":"Session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSession"}}}},"401":{"description":"Missing or invalid Bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimpleError"}}}},"403":{"description":"The session belongs to another account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimpleError"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimpleError"}}}}}}}},"webhooks":{"lead.created":{"post":{"summary":"A shopper finished the quiz","description":"Contact details only. Preferences arrive with design.generated.","parameters":[{"name":"X-RingBuilder-Signature","in":"header","description":"Hex-encoded HMAC-SHA256 of the raw request body, keyed with your webhook signing secret.","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{64}$"}},{"name":"User-Agent","in":"header","schema":{"type":"string","example":"RingBuilder-Webhook/1.0"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadCreatedEvent"}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges delivery. Anything else (or no answer within 10 seconds) is retried after 5 s, 30 s and 2 min."}}}},"design.generated":{"post":{"summary":"AI designs were generated","description":"Fires when a session's first AI designs are generated (engagement ring or wedding band). Later refinements do not re-send it; read the session for newer images.","parameters":[{"name":"X-RingBuilder-Signature","in":"header","description":"Hex-encoded HMAC-SHA256 of the raw request body, keyed with your webhook signing secret.","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{64}$"}},{"name":"User-Agent","in":"header","schema":{"type":"string","example":"RingBuilder-Webhook/1.0"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DesignGeneratedEvent"}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges delivery. Anything else (or no answer within 10 seconds) is retried after 5 s, 30 s and 2 min."}}}},"quote.requested":{"post":{"summary":"The shopper approved a design and asked for a quote","description":"Same shape as design.generated plus customerNotes; imageUrls holds only the approved image.","parameters":[{"name":"X-RingBuilder-Signature","in":"header","description":"Hex-encoded HMAC-SHA256 of the raw request body, keyed with your webhook signing secret.","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{64}$"}},{"name":"User-Agent","in":"header","schema":{"type":"string","example":"RingBuilder-Webhook/1.0"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequestedEvent"}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges delivery. Anything else (or no answer within 10 seconds) is retried after 5 s, 30 s and 2 min."}}}},"test.ping":{"post":{"summary":"Test delivery from the dashboard","description":"Sent by the Send test button.","parameters":[{"name":"X-RingBuilder-Signature","in":"header","description":"Hex-encoded HMAC-SHA256 of the raw request body, keyed with your webhook signing secret.","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{64}$"}},{"name":"User-Agent","in":"header","schema":{"type":"string","example":"RingBuilder-Webhook/1.0"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestPingEvent"}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges delivery. Anything else (or no answer within 10 seconds) is retried after 5 s, 30 s and 2 min."}}}}},"components":{"securitySchemes":{"webhookSecret":{"type":"http","scheme":"bearer","description":"Your webhook signing secret from Settings → Integrations → Webhooks."}},"schemas":{"Error":{"type":"object","required":["object","apiVersion","error"],"properties":{"object":{"const":"error"},"apiVersion":{"type":"string"},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["invalid_request","rate_limited","capacity_exhausted","not_found","internal_error"]},"message":{"type":"string"},"field":{"type":"string","description":"Request field that failed validation (invalid_request only)."},"retryAfterSeconds":{"type":"integer","description":"Seconds to wait before retrying (429 only). Also sent as the Retry-After header."},"signupUrl":{"type":"string","format":"uri","description":"Where a human can sign up directly instead (429 only)."}}}}},"SimpleError":{"type":"object","properties":{"error":{"type":"string"}}},"SandboxCreateRequest":{"type":"object","additionalProperties":true,"properties":{"businessName":{"type":"string","maxLength":120,"description":"The jeweler's business name. Pre-fills the claim form."},"websiteUrl":{"type":"string","description":"The site the ring builder goes on. A bare domain like example.com is accepted."},"platform":{"type":"string","enum":["shopify","wix","squarespace","wordpress","woocommerce","webflow","bigcommerce","custom","other"],"description":"Unknown values are stored as \"other\"."},"source":{"type":"string","maxLength":80,"description":"Your agent or tool name, e.g. claude-code, cursor, codex. `agent` is accepted as an alias."}}},"Sandbox":{"type":"object","required":["object","apiVersion","id","status","quizToken","quizUrl","claimUrl","expiresAt","renders","snippets"],"properties":{"object":{"const":"sandbox"},"apiVersion":{"type":"string","example":"2026-10-13"},"id":{"type":"string"},"status":{"type":"string","enum":["unclaimed"]},"quizToken":{"type":"string","description":"Public token used by every snippet."},"quizUrl":{"type":"string","format":"uri","description":"Hosted quiz page. Shareable as a plain link."},"claimUrl":{"type":"string","format":"uri","description":"Private. Give it only to the jeweler; do not commit it or put it in page code."},"expiresAt":{"type":"string","format":"date-time"},"renders":{"type":"object","required":["limit","used","remaining"],"properties":{"limit":{"type":"integer","description":"Total AI renders an unclaimed sandbox may make."},"used":{"type":"integer"},"remaining":{"type":"integer"}}},"snippets":{"type":"object","required":["inline","floating","popup","iframe"],"properties":{"inline":{"type":"string","description":"Recommended. Renders in place and auto-resizes."},"floating":{"type":"string","description":"Corner launcher button on every page."},"popup":{"type":"string","description":"Button where the snippet is pasted; opens a popup."},"iframe":{"type":"string","description":"Plain 800px iframe for builders that strip scripts. No auto-resize or install detection."}}},"business":{"type":"object","properties":{"name":{"type":["string","null"]},"websiteUrl":{"type":["string","null"]},"platform":{"type":["string","null"]}}},"source":{"type":"string"},"notes":{"type":"array","items":{"type":"string"},"description":"Plain-language notes worth relaying to the user."}}},"SandboxStatus":{"type":"object","properties":{"object":{"const":"sandbox_status"},"apiVersion":{"type":"string"},"id":{"type":"string"},"status":{"type":"string","enum":["unclaimed","claimed","expired","purged"],"description":"purged: expired and removed; it can no longer be claimed."},"claimed":{"type":"boolean"},"expiresAt":{"type":"string","format":"date-time"},"renders":{"type":"object","required":["limit","used","remaining"],"properties":{"limit":{"type":"integer","description":"Total AI renders an unclaimed sandbox may make."},"used":{"type":"integer"},"remaining":{"type":"integer"}}},"installDetected":{"type":"boolean","description":"True once embed.js has loaded on a real (non-localhost) domain."}}},"SandboxApiDescription":{"type":"object","properties":{"object":{"const":"sandbox_api"},"apiVersion":{"type":"string"},"create":{"type":"object"},"limits":{"type":"object","properties":{"rendersPerSandbox":{"type":"integer"},"expiresAfterDays":{"type":"integer"},"createPerIpPerHour":{"type":"integer"}}}}},"EngagementPreferences":{"type":["object","null"],"description":"Engagement ring choices. Option values are stable slugs.","properties":{"stoneType":{"type":"string"},"stoneShape":{"type":"string"},"stoneCarat":{"type":"string"},"settingStyle":{"description":"String or array of strings."},"metalType":{"type":"string"},"bandStyle":{"type":"string"},"bandWidth":{"type":"string"},"settingHeight":{"type":["string","null"]},"finishingTouches":{"type":"array","items":{"type":"string"}},"engraving":{"type":["string","null"]},"engravingPlacement":{"type":["string","null"],"enum":["inside","outside","both",null]},"birthstoneCount":{},"birthstone1":{},"birthstone2":{},"sideStoneCount":{"type":["integer","null"]},"sideStoneShape":{"type":["string","null"]},"budgetRange":{"type":"string"},"ringSize":{"type":["string","null"]},"timeline":{"type":["string","null"]},"purpose":{"type":["string","null"]},"additionalNotes":{"type":["string","null"]}}},"WeddingBandPreferences":{"type":["object","null"],"properties":{"bandGender":{"type":"string"},"metalType":{"type":"string"},"goldPurity":{"type":["string","null"]},"width":{"type":"string"},"bandProfile":{"type":["string","null"]},"bandFit":{"type":["string","null"]},"edgeDetail":{"type":["string","null"]},"finish":{"type":["array","string","null"],"items":{"type":"string"},"description":"Usually an array of finish slugs."},"hasStones":{"type":"boolean"},"stoneType":{"type":["string","null"]},"stoneShape":{"type":["string","null"]},"stonePlacement":{"type":["string","null"]},"stoneCount":{},"ringSize":{"type":["string","null"]},"budgetRange":{"type":["string","null"]},"timeline":{"type":["string","null"]},"additionalNotes":{"type":["string","null"]}}},"LeadCreatedEvent":{"type":"object","required":["event","timestamp","sessionId","sessionUrl","customer"],"properties":{"event":{"const":"lead.created"},"timestamp":{"type":"string","format":"date-time"},"sessionId":{"type":"string"},"sessionUrl":{"type":"string","format":"uri"},"shareUrl":{"type":["string","null"],"format":"uri"},"customer":{"type":"object","properties":{"name":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"country":{"type":["string","null"]}}}}},"DesignPayload":{"type":"object","description":"Fields shared by design.generated and quote.requested. Each event schema adds its own event value.","required":["event","timestamp","sessionId","sessionType","sessionUrl","customer","iterationNumber","imageUrls"],"properties":{"event":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"sessionId":{"type":"string"},"sessionType":{"type":"string","description":"\"engagement-ring\", or a wedding band session type (for example \"mens-band\" or \"womens-band\")."},"sessionUrl":{"type":"string","format":"uri"},"shareUrl":{"type":["string","null"],"format":"uri"},"customer":{"type":"object","properties":{"name":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"country":{"type":["string","null"]}}},"iterationNumber":{"type":"integer"},"imageUrls":{"type":"array","items":{"type":"string","format":"uri"}},"preferences":{"$ref":"#/components/schemas/EngagementPreferences","description":"Engagement-ring sessions only."},"weddingBandPreferences":{"$ref":"#/components/schemas/WeddingBandPreferences","description":"Wedding-band sessions only."}}},"DesignGeneratedEvent":{"allOf":[{"$ref":"#/components/schemas/DesignPayload"},{"type":"object","required":["event"],"properties":{"event":{"const":"design.generated"}}}]},"QuoteRequestedEvent":{"allOf":[{"$ref":"#/components/schemas/DesignPayload"},{"type":"object","required":["event","customerNotes"],"properties":{"event":{"const":"quote.requested"},"customerNotes":{"type":["string","null"],"description":"The message the shopper wrote when approving the design."},"imageUrls":{"type":"array","items":{"type":"string","format":"uri"},"description":"Only the approved image."}}}]},"TestPingEvent":{"type":"object","required":["event","timestamp"],"properties":{"event":{"const":"test.ping"},"timestamp":{"type":"string","format":"date-time"},"message":{"type":"string"}}},"WebhookSession":{"type":"object","properties":{"sessionId":{"type":"string"},"sessionType":{"type":"string"},"status":{"type":"string"},"shareToken":{"type":["string","null"]},"approvedAt":{"type":["string","null"],"format":"date-time"},"customer":{"type":"object","properties":{"name":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"country":{"type":["string","null"]}}},"preferences":{"type":["object","null"]},"weddingBandPreferences":{"type":["object","null"]},"latestImages":{"type":"array","items":{"type":"string"}},"allIterationImages":{"type":"array","items":{"type":"object","properties":{"iteration":{"type":"integer"},"images":{"type":"array","items":{"type":"string"}},"isApproved":{"type":"boolean"}}}},"allImageUrls":{"type":"array","items":{"type":"string"}},"customerMessage":{"type":["string","null"]},"sessionUrl":{"type":"string","format":"uri"},"shareUrl":{"type":["string","null"],"format":"uri"}}}}}}