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.
| Status | Meaning |
|---|---|
| 400 | Bad request — invalid input |
| 403 | Security check failed (inquiry endpoint) |
| 404 | Dealer or watch not found |
| 429 | Rate limit exceeded — try again later |
| 500 | Server error |
/api/v1/dealers/:idDealer 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"
}
}/api/v1/dealers/:id/inventoryInventory List
Returns the dealer's public inventory. Sold watches are excluded by default.
Query Parameters
| Param | Type | Description |
|---|---|---|
| page | number | Page number. Default: 1 |
| perPage | number | Results per page. Default: 24, max: 100 |
| brand | string | Filter by brand (case-insensitive). E.g. Rolex |
| includeSold | boolean | Include 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
| Field | Type | Description |
|---|---|---|
| id | string | Unique watch identifier |
| brand | string | Brand name |
| model | string | Model name |
| referenceNumber | string | null | Manufacturer reference number |
| year | number | null | Year of production |
| condition | string | null | new, bnib, lnib, excellent, good, fair |
| hasBox | boolean | Original box included |
| hasPapers | boolean | Original papers included |
| hasWarranty | boolean | Active warranty included |
| description | string | null | Dealer's description |
| statuses | string[] | selling, trading, and/or sold |
| askingPrice | number | null | Asking price in USD |
| photos | object[] | Array of { id, url } |
| url | string | Direct link to watch page on wristly.io |
| createdAt | string | ISO 8601 timestamp |
/api/v1/dealers/:id/inventory/:watchIdSingle 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"
}/api/v1/dealers/:id/inquirySubmit 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
| Field | Type | Description |
|---|---|---|
| watchId | string | Required. The watch ID from the inventory list |
| name | string | Required. Buyer's full name |
| string | Required. Buyer's email address | |
| phone | string | Required. Buyer's phone number |
| preferences | string | Optional. Buyer's preferences, e.g. "Full set, open to trade" |
| message | string | Optional. Free-text message from the buyer (max 2000 chars) |
| honeypot | string | Optional. Leave empty — used for bot detection |
| turnstileToken | string | Optional. 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.
Questions? support@wristly.io