Skip to main content

API Reference

Fetch live dealer inventory and integrate it natively into any website.

Introduction

The Wristly REST API lets you pull a dealer's live inventory and render it as native HTML on any website. Because the content lives on your domain — not inside an iframe — search engines crawl and index it as your content, which is significantly better for SEO.

All responses are JSON. The base URL for every endpoint is:

https://www.wristly.io/api/v1

Your dealer ID is available on the My Storefront page inside your Wristly dealer dashboard under the API Access section.

Authentication

The read endpoints (GET) are fully public — no API key or token required. Inventory data is already public on the Wristly platform, so there is nothing to protect. Just make the request.

CORS

All endpoints return the following header, so you can call the API directly from a browser on any domain:

Access-Control-Allow-Origin: *

This means you can use fetch() from client-side JavaScript on your dealer's website without any proxy or server-side workaround.

Errors

Errors return a JSON object with an error field and a standard HTTP status code.

StatusMeaning
400Bad request — invalid input
403Security check failed (inquiry endpoint)
404Dealer or watch not found
429Rate limit exceeded — try again later
500Server error
GET/api/v1/dealers/:id

Dealer Profile

Returns public profile information for a dealer.

Example Request

fetch("https://www.wristly.io/api/v1/dealers/DEALER_ID")
  .then(res => res.json())
  .then(data => console.log(data));

Example Response

{
  "dealer": {
    "id": "cm9abc123...",
    "name": "5D Watches",
    "bio": "Specialist in vintage and modern luxury timepieces.",
    "logo": "https://cdn.wristly.io/logos/5d-watches.jpg",
    "banner": "https://cdn.wristly.io/banners/5d-watches.jpg",
    "website": "https://5dwatches.com",
    "phone": "+1 (555) 000-1234",
    "isVerified": true,
    "createdAt": "2024-03-15T12:00:00.000Z"
  }
}
GET/api/v1/dealers/:id/inventory

Inventory List

Returns the dealer's public inventory. Sold watches are excluded by default.

Query Parameters

ParamTypeDescription
pagenumberPage number. Default: 1
perPagenumberResults per page. Default: 24, max: 100
brandstringFilter by brand (case-insensitive). E.g. Rolex
includeSoldbooleanInclude sold watches. Default: false

Example Request

fetch("https://www.wristly.io/api/v1/dealers/DEALER_ID/inventory?page=1&perPage=12&brand=Rolex")
  .then(res => res.json())
  .then(({ watches, pagination }) => {
    watches.forEach(watch => {
      console.log(watch.brand, watch.model, watch.askingPrice);
    });
  });

Example Response

{
  "dealer": {
    "id": "cm9abc123...",
    "name": "5D Watches",
    "logo": "https://cdn.wristly.io/logos/5d-watches.jpg",
    "isVerified": true
  },
  "watches": [
    {
      "id": "cm9xyz789...",
      "brand": "Rolex",
      "model": "Daytona",
      "referenceNumber": "116500LN",
      "year": 2021,
      "condition": "excellent",
      "hasBox": true,
      "hasPapers": true,
      "hasWarranty": false,
      "description": "Full set, unworn. White dial.",
      "statuses": ["selling"],
      "askingPrice": 35000,
      "photos": [
        { "id": "ph_01...", "url": "https://cdn.wristly.io/watches/photo.jpg" }
      ],
      "url": "https://www.wristly.io/watch/cm9xyz789...",
      "createdAt": "2025-01-10T09:30:00.000Z"
    }
  ],
  "pagination": {
    "total": 47,
    "page": 1,
    "perPage": 12,
    "totalPages": 4
  }
}

Watch Fields

FieldTypeDescription
idstringUnique watch identifier
brandstringBrand name
modelstringModel name
referenceNumberstring | nullManufacturer reference number
yearnumber | nullYear of production
conditionstring | nullnew, bnib, lnib, excellent, good, fair
hasBoxbooleanOriginal box included
hasPapersbooleanOriginal papers included
hasWarrantybooleanActive warranty included
descriptionstring | nullDealer's description
statusesstring[]selling, trading, and/or sold
askingPricenumber | nullAsking price in USD
photosobject[]Array of { id, url }
urlstringDirect link to watch page on wristly.io
createdAtstringISO 8601 timestamp
GET/api/v1/dealers/:id/inventory/:watchId

Single Watch

Returns full details for a single watch including all photos and additional specifications. The watch must be public and belong to the specified dealer.

Example Request

fetch("https://www.wristly.io/api/v1/dealers/DEALER_ID/inventory/WATCH_ID")
  .then(res => res.json())
  .then(watch => console.log(watch));

Example Response

{
  "id": "cm9xyz789...",
  "brand": "Rolex",
  "model": "Daytona",
  "referenceNumber": "116500LN",
  "year": 2021,
  "condition": "excellent",
  "caseSize": "40mm",
  "caseMaterial": "Stainless Steel",
  "movement": "Automatic",
  "hasBox": true,
  "hasPapers": true,
  "hasWarranty": false,
  "description": "Full set, unworn. White dial. Comes with all original packaging.",
  "statuses": ["selling"],
  "askingPrice": 35000,
  "photos": [
    { "id": "ph_01...", "url": "https://cdn.wristly.io/watches/photo1.jpg", "order": 0 },
    { "id": "ph_02...", "url": "https://cdn.wristly.io/watches/photo2.jpg", "order": 1 }
  ],
  "dealer": {
    "id": "cm9abc123...",
    "name": "5D Watches",
    "logo": "https://cdn.wristly.io/logos/5d-watches.jpg",
    "isVerified": true
  },
  "url": "https://www.wristly.io/watch/cm9xyz789...",
  "createdAt": "2025-01-10T09:30:00.000Z"
}
POST/api/v1/dealers/:id/inquiry

Submit Inquiry

Submits a buyer inquiry for a specific watch. When successful, Wristly automatically:

  • Sends an email notification to the dealer
  • Creates or updates a contact in the dealer's CRM with the buyer's name, email, and phone — tagged with source embed_inquiry
  • Sends an in-app notification to the dealer with a direct link to their CRM

Rate limited to 5 requests per IP per hour.

Request Body

FieldTypeDescription
watchIdstringRequired. The watch ID from the inventory list
namestringRequired. Buyer's full name
emailstringRequired. Buyer's email address
phonestringRequired. Buyer's phone number
preferencesstringOptional. Buyer's preferences, e.g. "Full set, open to trade"
messagestringOptional. Free-text message from the buyer (max 2000 chars)
honeypotstringOptional. Leave empty — used for bot detection
turnstileTokenstringOptional. Cloudflare Turnstile token if implementing CAPTCHA

Example Request

fetch("https://www.wristly.io/api/v1/dealers/DEALER_ID/inquiry", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    watchId: "cm9xyz789...",
    name: "John Smith",
    email: "john@example.com",
    phone: "+1 (555) 000-5678",
    preferences: "Full set preferred, open to trade",  // optional
    message: "Is the price negotiable?",               // optional
    honeypot: ""   // always send empty — bots fill this
  })
})
  .then(res => res.json())
  .then(data => console.log(data)); // { "success": true }

Success Response

{ "success": true }

Spam protection: Always include a hidden honeypot input in your form and leave it empty. Real users never fill it; bots do. The server silently discards submissions where this field is populated, so never pre-fill it.