{"openapi":"3.0.3","info":{"title":"SpotOffer","version":"1.3.6","description":"Precious-metals melt-value and dealer-offer calculator. Price multi-line deals off the live reference feed (gold-api.com, as-of timestamped) or your own spot price (body.spot / x-spot override), generate printable offer sheets, format your deals as QuickBooks-ready CSV, look up typical coin premiums, and value a stack. Privacy-first: SpotOffer never stores your sales data — your records stay yours. /api/quote is free; offer sheets are pro; seats and CSV export are shop."},"servers":[{"url":"https://spotofferapp.com","description":"Production instance"}],"paths":{"/":{"get":{"summary":"Product landing page","responses":{"200":{"description":"HTML landing page"}}}},"/api/health":{"get":{"summary":"Liveness check","responses":{"200":{"description":"OK"}}}},"/api/hub/messages":{"get":{"summary":"Agent Hub board: read messages (x-hub-token)","parameters":[{"name":"x-hub-token","in":"header","schema":{"type":"string"},"description":"Agent token (SHA-256 hashed server-side)"},{"name":"since","in":"query","schema":{"type":"string"},"description":"ISO timestamp — only newer messages"},{"name":"limit","in":"query","schema":{"type":"integer"},"description":"Max messages (1-200, default 50)"}],"responses":{"200":{"description":"OK"}}},"post":{"summary":"Agent Hub board: post a message (x-hub-token)","parameters":[{"name":"x-hub-token","in":"header","schema":{"type":"string"},"description":"Agent token"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body"],"properties":{"to":{"type":"string","description":"board or agent name"},"body":{"type":"string"}}}}}},"responses":{"200":{"description":"OK"}}}},"/api/hub/agents":{"post":{"summary":"Mint an agent token (x-hub-admin)","parameters":[{"name":"x-hub-admin","in":"header","schema":{"type":"string"},"description":"HUB_ADMIN_KEY"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"role":{"type":"string"},"seat":{"type":"string"}}}}}},"responses":{"200":{"description":"OK — raw token returned once"}}},"delete":{"summary":"Revoke an agent token (x-hub-admin)","parameters":[{"name":"x-hub-admin","in":"header","schema":{"type":"string"},"description":"HUB_ADMIN_KEY"},{"name":"name","in":"query","schema":{"type":"string"},"description":"Agent name to revoke"}],"responses":{"200":{"description":"OK"}}}},"/api/metals":{"get":{"summary":"Supported metals, units and karat table","responses":{"200":{"description":"OK"}}}},"/api/spot":{"get":{"summary":"Live reference price feed in USD per troy oz (gold-api.com) — display only; /api/quote defaults to it unless overridden","responses":{"200":{"description":"Feed prices with as-of timestamp and staleness flag, or nulls before the first poll"}}}},"/api/quote":{"post":{"summary":"Price a multi-line bullion deal (free: 25/day per IP; unlimited with x-pro-key). Spot precedence: dealer spot (body.spot/x-spot) > live feed > HTTP 503 when the feed is more than 30 min stale.","parameters":[{"name":"x-spot","in":"header","schema":{"type":"string"},"description":"Alternative to body.spot for non-JSON callers (e.g. CSV lot intake): JSON like {\"gold\": 2680.50, \"silver\": 31.20, \"platinum\": 980, \"palladium\": 1050}"},{"name":"x-demo-token","in":"header","schema":{"type":"string"},"description":"Landing-page demo session token from POST /api/demo-session — the demo runs on its own quota (10 quotes per 30-min session) instead of the anonymous 25/day IP quota. Only honored on this endpoint."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}}},"responses":{"200":{"description":"Per-line melt and offer breakdown plus deal totals","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"}}}}}}},"/api/demo-session":{"post":{"summary":"Mint a landing-page demo session token (10 quotes per 30-min session; max 3 sessions/hour per IP)","responses":{"200":{"description":"Demo session token — send as the x-demo-token header on POST /api/quote"},"429":{"description":"Demo session limit reached for this IP"}}}},"/api/deals.csv":{"post":{"summary":"Your deals in, QuickBooks-ready CSV out — stateless, nothing stored (shop — requires x-pro-key header)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["deals"],"properties":{"deals":{"type":"array","items":{"type":"object"}}}}}}},"responses":{"200":{"description":"CSV download"}}}},"/api/lot":{"post":{"summary":"Batch lot intake — JSON items or CSV in, fully priced lot out (pro — requires x-pro-key header)","parameters":[{"name":"x-spot","in":"header","schema":{"type":"string"},"description":"REQUIRED for CSV intake (no JSON body to carry it): your spot price as JSON, e.g. {\"gold\": 2680.50, \"silver\": 31.20, \"platinum\": 980, \"palladium\": 1050}"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"type":"object"}},"default_offer_pct":{"type":"number","default":100},"offer_pct":{"type":"object","additionalProperties":{"type":"number"}}}}},"text/csv":{"schema":{"type":"string","description":"Header row + item rows. Required: metal, weight. Optional: unit, karat, purity_pct, label, offer_pct. Empty cells are omitted — a blank offer_pct cell prices that line at 100%; a present-but-invalid offer_pct is a 400."}}}},"responses":{"200":{"description":"Priced lot with per-metal subtotals"}}}},"/api/offer-sheet":{"post":{"summary":"Printable HTML offer sheet for a deal (pro — requires x-pro-key header)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}}},"responses":{"200":{"description":"HTML offer sheet"}}}},"/api/offer-sheet/email":{"post":{"summary":"Email the printable offer sheet to the seller (pro — requires x-pro-key header)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["lines","to"],"properties":{"lines":{"type":"array","items":{"type":"object"}},"default_offer_pct":{"type":"number","default":100},"to":{"type":"string","format":"email"},"subject":{"type":"string"}}}}}},"responses":{"200":{"description":"Email sent (or logged without RESEND_API_KEY)"}}}},"/api/premiums":{"get":{"summary":"Typical coin premiums over melt — reference ranges (now with buy/sell premium defaults)","parameters":[{"name":"metal","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Premium reference table"}}}},"/api/premiums/matrix":{"get":{"summary":"Buy/sell premium matrix — your dealer numbers (pro — requires x-pro-key header)","parameters":[{"name":"x-pro-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your Pro API key"}],"responses":{"200":{"description":"Effective buy/sell premium matrix (seeds + dealer overrides)"},"402":{"description":"Pro key required"}}},"put":{"summary":"Update the buy/sell premium matrix (pro — requires x-pro-key header)","parameters":[{"name":"x-pro-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your Pro API key"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"matrix":{"type":"array","items":{"type":"object"}}},"example":{"matrix":[{"product":"Generic silver round 1oz","buy_premium_usd_per_ozt":-1,"sell_premium_usd_per_ozt":2}]}}}}},"responses":{"200":{"description":"Matrix saved"},"402":{"description":"Pro key required"},"503":{"description":"premium_matrix storage not configured — DDL returned"}}}},"/api/coin-value":{"post":{"summary":"Coin value lookup: melt + Numista catalog + eBay asking-price comps (pro — requires x-pro-key header)","parameters":[{"name":"x-pro-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your Pro API key"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string"},"year":{"type":"string"},"mint":{"type":"string"},"grade":{"type":"string"},"numista_id":{"type":"string"}}},"example":{"description":"1909-S VDB Lincoln cent","grade":"XF45"}}}},"responses":{"200":{"description":"Melt, catalog, and market layers with per-layer status"},"402":{"description":"Pro key required"}}}},"/api/stack":{"post":{"summary":"Value a stack at melt (100%) or a sale percentage","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["lines"],"properties":{"lines":{"type":"array","items":{"type":"object"}},"sale_pct":{"type":"number","default":100},"offer_pct":{"type":"object","additionalProperties":{"type":"number"}}}}}}},"responses":{"200":{"description":"Stack valuation"}}}},"/api/redeem":{"post":{"summary":"Redeem a creator promo code for a 90-day Pro key","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"code":{"type":"string","example":"JEBULLION3"}}}}}},"responses":{"200":{"description":"Pro key, expiry date, and usage note (shown once)"},"400":{"description":"Missing or invalid promo code"},"409":{"description":"Code already redeemed — one key per creator code"}}}},"/api/redeem-viewer":{"post":{"summary":"Redeem a creator viewer code for a 30-day Pro trial (multi-use, one per email)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code","email"],"properties":{"code":{"type":"string","example":"JEBULLION"},"email":{"type":"string","format":"email","example":"you@example.com"}}}}}},"responses":{"200":{"description":"30-day Pro trial key, expiry date, and usage note (shown once)"},"400":{"description":"Missing/invalid code or email"},"409":{"description":"This email already redeemed this viewer code"}}}},"/api/redeem-affiliate":{"post":{"summary":"Redeem a creator affiliate code for a 30-day Pro trial","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code","email"],"properties":{"code":{"type":"string","example":"JEBULLIONAFF"},"email":{"type":"string","format":"email","example":"you@example.com"}}}}}},"responses":{"200":{"description":"30-day Pro key, expiry date, and usage note (shown once)"},"400":{"description":"Missing/invalid code or email"},"409":{"description":"This email already redeemed this creator code"}}}},"/api/affiliate-report":{"get":{"summary":"Affiliate ledger: per-creator redemptions, conversions, kickback owed (pro)","responses":{"200":{"description":"Per-creator ledger with totals"}}}}},"components":{"schemas":{"QuoteRequest":{"type":"object","required":["lines"],"properties":{"spot":{"type":"object","required":["gold","silver","platinum","palladium"],"description":"YOUR spot price in USD per troy oz — optional override. Omit it and the quote engine prices off the live reference feed (gold-api.com) with an as-of timestamp; send it to price off your own number instead.","properties":{"gold":{"type":"number"},"silver":{"type":"number"},"platinum":{"type":"number"},"palladium":{"type":"number"}}},"lines":{"type":"array","items":{"type":"object","required":["metal","weight"],"properties":{"metal":{"type":"string","enum":["gold","silver","platinum","palladium"]},"weight":{"type":"number","description":"Weight in the given unit"},"unit":{"type":"string","default":"ozt","description":"g, kg, ozt (troy oz), dwt, gr (grain), oz (avoirdupois)"},"karat":{"type":"number","description":"Gold karat, e.g. 14, 18, 24"},"purity_pct":{"type":"number","description":"0–100, e.g. 99.9"},"fineness":{"type":"number","description":"0–1 fraction, e.g. 0.999"},"label":{"type":"string","description":"Optional line label, e.g. \"wedding band\""},"offer_pct":{"type":"number","description":"Optional line-level offer as % of melt — wins over the per-metal offer_pct map and default_offer_pct. Same strictness: only omission falls back; null/\"\"/arrays/objects are a 400; 0 prices the line to $0."}}}},"offer_pct":{"type":"object","description":"Per-metal offer as % of melt, e.g. {\"gold\": 92, \"silver\": 85}. Only omission falls back to default_offer_pct; null/\"\"/arrays/objects are a 400.","additionalProperties":{"type":"number"}},"default_offer_pct":{"type":"number","default":100,"description":"Fallback % of melt when a line and its metal have no offer_pct entry. Only undefined falls back — null, \"\", arrays, and objects are a 400; 0 prices to $0."}}},"QuoteResponse":{"type":"object","properties":{"lines":{"type":"array","items":{"type":"object"}},"melt_total_usd":{"type":"number"},"offer_total_usd":{"type":"number"},"spot":{"type":"object"}}}}}}