API Documentation
v1.0Public REST API for submission intake, status tracking, webhook configuration, and certificate verification.
Authentication
All tenant-scoped endpoints use Bearer token authentication.
Authorization: Bearer lp_live_...- Live keys start with
lp_live_ - Sandbox keys start with
lp_sandbox_ - Contact team@echt.watch to get your API key
Submissions
Create and track watch authentication submissions.
/api/v1/submissionsSubmit a watch for authentication.
Request Headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer lp_live_... | Yes |
| Content-Type | application/json | Yes |
Request Body
| Field | Type | Required |
|---|---|---|
| brand | string | Yes |
| reference_number | string | Yes |
| declared_year | string | Optional |
| case_material | string | Optional |
| dial_variant | string | Optional |
| bracelet_type | string | Optional |
| bezel_type | string | Optional |
| box_present | boolean | Optional |
| papers_present | boolean | Optional |
| service_parts | string | null | Optional |
| listing_description | string | Optional |
| listing_url | string | Optional |
| submitter_email | string | Yes |
| photo_urls | object | Yes |
POST /api/v1/submissions
Content-Type: application/json
Authorization: Bearer lp_live_...
{
"brand": "Rolex",
"reference_number": "116500LN",
"submitter_email": "seller@example.com",
"photo_urls": {
"dial": "https://cdn.platform.com/watches/dial.jpg",
"caseback": "https://cdn.platform.com/watches/back.jpg",
"serial": "https://cdn.platform.com/watches/serial.jpg",
"full_front": "https://cdn.platform.com/watches/front.jpg"
}
}201 Created
{
"submission_id": "SUB-0000001",
"status": "received",
"estimated_completion": "2026-03-20T14:30:00Z",
"sla_hours": 4,
"track_url": "https://echt.watch/status/SUB-0000001"
}Error responses: 400, 401, 409
/api/v1/submissions/{id}Get submission status.
{
"submission_id": "SUB-0000001",
"status": "verified",
"ai_result": "pass",
"ai_confidence": "high",
"certificate_id": "CRT-0000001",
"completed_at": "2026-03-20T13:45:00Z"
}/api/v1/submissions/{id}/outcomeSend physical authentication outcome.
Body: outcome, notes, authenticated_by
{
"outcome": "authentic",
"notes": "Verified at hub intake bench",
"authenticated_by": "Bezel NYC"
}{
"submission_id": "SUB-0000001",
"outcome": "authentic",
"match": true,
"specialist_accuracy": 94.2,
"points_awarded": 2
}Webhooks
Configure delivery endpoints for real-time event notifications.
/api/v1/webhooks/configureConfigure webhook URL and subscribed events for a tenant.
{
"webhook_url": "https://platform.com/webhooks/echt",
"events": [
"submission.verified",
"submission.failed",
"submission.second_opinion_requested",
"submission.additional_photos_required",
"submission.ai_rejected",
"certificate.expiring"
]
}Events
submission.verified— assessment verified and certificate issuedsubmission.failed— specialist marked watch as failedsubmission.second_opinion_requested— moved to second specialist reviewsubmission.additional_photos_required— submitter must upload better photossubmission.ai_rejected— AI rejected at pre-check stagecertificate.expiring— certificate nearing expiry window
{
"event": "submission.verified",
"submission_id": "SUB-0000001",
"certificate_id": "CRT-0000001",
"result": "verified",
"timestamp": "2026-03-20T13:45:00Z"
}Retry logic: immediate → +5min → +30min → abandoned. Security: verify X-Loupe-Delivery header.
Certificates
Public certificate verification endpoint.
/api/v1/certificates/{id}Public endpoint: no API key required.
{
"id": "CRT-0000001",
"status": "valid",
"watch": {
"brand": "Rolex",
"reference_number": "116500LN"
},
"result": "verified",
"issued_at": "2026-03-21T00:00:00Z",
"expires_at": "2026-06-21T00:00:00Z",
"specialist": {
"name": "Stan D.",
"tier": "standard"
},
"verify_url": "https://echt.watch/verify/CRT-0000001"
}Status values: valid, expired.
Errors
Standard error format:
{
"error": "string",
"details": {}
}| HTTP | Meaning |
|---|---|
| 200 | Success |
| 201 | Resource created |
| 400 | Invalid input |
| 401 | Invalid API key |
| 404 | Resource not found |
| 409 | Conflict |
| 500 | Internal error |
Rate limiting: 100 requests per hour per API key.