# LegitV API Reference

> AI-powered product authentication. Verify sneakers, luxury goods, streetwear.
> Base URL: `https://api.legitv.io`

---

## Authentication

Two auth methods depending on endpoint type:

| Method | Header | Used For |
|--------|--------|----------|
| **API Key** | `X-API-Key: lv_xxx` | Templates, admin, programmatic access |
| **Supabase JWT** | `Authorization: Bearer eyJ...` | User-facing (verify, credits, history) |

API keys also work as `Authorization: Bearer lv_xxx`.

---

## Verification Flow

The core product — authenticate an item in 3 steps:

### Step 1: Create Verification
```
POST /api/verify/create
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "itemType": "yeezy_350_v2",
  "images": ["base64_image_1", "base64_image_2"],
  "title": "My Yeezy Check",
  "listingUrl": "https://vinted.fr/items/...",
  "userDescription": "Bought from marketplace, box included"
}
```

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| itemType | string | ✅ | Template slug (e.g. `nike_dunk_low`, `levis_501`) |
| images | string[] | ⚠️ | Base64 images (max 10). Required if no listingUrl |
| listingUrl | string | ⚠️ | Vinted/Vestiaire URL. Required if no images |
| title | string | — | Display name |
| userDescription | string | — | User's context about the item |
| metadata | object | — | Free-form key/value pairs |
| parentId | uuid | — | Link to previous verification (for re-runs) |

→ Returns `201`: `{ "id": "uuid", "status": "draft", "parentId": null }`

### Step 2: Launch AI Analysis
```
POST /api/verify/:id/analyze
Authorization: Bearer <jwt>
```

Starts the AI authentication workflow. Costs 1 credit.
- Scrapes listing URL if provided (extracts images + description)
- Runs visual extraction against template criteria
- Synthesizes results into a scored verdict
- Generates blockchain-anchored proof hash

→ Returns `200`: `{ "status": "queued", "position": 1, "estimatedWait": "~30s" }`

### Step 3: Poll Results
```
GET /api/verify/:id
Authorization: Bearer <jwt>
```

→ Returns the full verification with results when complete:
```json
{
  "id": "uuid",
  "status": "completed",
  "itemType": "yeezy_350_v2",
  "score": 750,
  "verdict": "authentic",
  "confidence": 92,
  "results": {
    "criteria": [
      { "name": "boost_pattern", "score": 95, "passed": true, "explanation": "..." },
      { "name": "heel_tab", "score": 88, "passed": true, "explanation": "..." }
    ],
    "dealbreakers": [],
    "overallAnalysis": "..."
  },
  "proofHash": "0xabc..."
}
```

**Status progression**: `draft` → `processing` → `completed` | `failed`

### Queue Status
```
GET /api/verify/:id/queue        → { "position": 2, "status": "waiting" }
GET /api/verify/queue/status     → { "pending": 3, "processing": 1, "total": 4 }
```

### List My Verifications
```
GET /api/verify/list
Authorization: Bearer <jwt>
```

### Delete Verification
```
DELETE /api/verify/:id
Authorization: Bearer <jwt>
```

---

## Proof System (Public)

Verify authenticity proof without authentication. Rate limited: 10 req/min.

### Get Proof
```
GET /api/verify/:id/proof
```
→ Returns proof data with hash, score, verdict for public verification pages.

### Verify Proof Hash
```
POST /api/verify/proof/check
Content-Type: application/json

{ "hash": "0xabc...", "verificationId": "uuid" }
```
→ `{ "valid": true, "tampered": false }`

### Download PDF Report
```
GET /api/verify/:id/report
Authorization: Bearer <jwt>
```
→ PDF binary with full authentication report.

---

## Templates (API Key Required)

Manage product templates and authentication criteria.

### List Templates
```
GET /api/templates                   → public, basic info
GET /api/templates/admin/all         → admin, full details
```

### Get Template
```
GET /api/templates/:id               → by UUID
GET /api/templates/by-type/:itemType → by slug (e.g. "nike_dunk_low")
```
→ Returns template with `criteria[]` and `examples[]`.

### Create Template
```
POST /api/templates
X-API-Key: lv_xxx

{
  "itemType": "nike_dunk_low",
  "displayName": "Nike Dunk Low",
  "brand": "Nike",
  "category": "sneakers",
  "description": "Iconic low-top, highly counterfeited since 2020.",
  "eraInfo": "Original 1985, Retro 2020+. SP/SB variants exist."
}
```

### Update Template
```
PATCH /api/templates/:id
X-API-Key: lv_xxx

{ "displayName": "Nike Dunk Low Retro", "isActive": true }
```

### Delete Template
```
DELETE /api/templates/:id
X-API-Key: lv_xxx
```
⚠️ Cascades: deletes all criteria and examples.

---

## Criteria

Each template has authentication criteria — the checks the AI performs.

### Add Criterion
```
POST /api/templates/:templateId/criteria
X-API-Key: lv_xxx

{
  "name": "swoosh_stitching",
  "displayName": "Swoosh Stitching Quality",
  "category": "major",
  "zone": "construction",
  "photoNeeded": "side_closeup",
  "weight": 150,
  "description": "Check swoosh stitching: authentic uses 8-10 stitches/cm, consistent tension.",
  "descriptionFr": "Vérifier les coutures du swoosh: 8-10 points/cm, tension régulière.",
  "authenticSigns": "Even stitching, consistent thread tension, smooth curve.",
  "fakeSigns": "Uneven stitches, loose threads, angular swoosh curves.",
  "dealbreakers": [],
  "whitelist": []
}
```

**Categories & Weights:**
| Category | Weight | Behavior |
|----------|--------|----------|
| `dealbreaker` | N/A | Instant fail (−1000). Use for impossible-on-authentic features. |
| `major` | 50–200 | Core authentication points. |
| `minor` | 10–50 | Supporting evidence. |

### Update Criterion
```
PATCH /api/templates/:templateId/criteria/:criterionId
X-API-Key: lv_xxx

{ "weight": 200, "description": "Updated..." }
```

### Delete Criterion
```
DELETE /api/templates/:templateId/criteria/:criterionId
X-API-Key: lv_xxx
```

---

## Examples

Reference images attached to templates (authentic vs fake).

### Add Example
```
POST /api/templates/:templateId/examples
X-API-Key: lv_xxx

{
  "type": "authentic",
  "imageUrl": "https://...",
  "description": "Retail pair, 2024 release. Note correct tongue tag.",
  "era": "2024"
}
```
`type`: `"authentic"` or `"fake"`

### Delete Example
```
DELETE /api/templates/:templateId/examples/:exampleId
X-API-Key: lv_xxx
```

---

## Categories

### List Categories
```
GET /api/templates/categories
```
→ `{ "categories": [{ "id", "name", "displayName", "sortOrder" }] }`

### Create Category
```
POST /api/templates/categories
X-API-Key: lv_xxx

{ "name": "sneakers", "displayName": "Sneakers" }
```

### Delete Category
```
DELETE /api/templates/categories/:name
X-API-Key: lv_xxx
```

### Criteria Options (zones & photo types)
```
GET /api/templates/criteria-options
```
→ `{ "zones": ["button", "label", ...], "photoTypes": ["front", "back", ...] }`

---

## Credits

### Check Balance
```
GET /api/credits/balance
Authorization: Bearer <jwt>
```
→ `{ "credits": 5 }`

### List Packages
```
GET /api/credits/packages
```
→ Available credit packages and pricing.

---

## History

### List Verification History
```
GET /api/history
Authorization: Bearer <jwt>
```

### Get Historical Verification
```
GET /api/history/:id
Authorization: Bearer <jwt>
```

---

## Share

### Get Shared Verification (Public)
```
GET /api/share/:id
```
→ Public-safe verification data for sharing pages.

---

## Providers

### List Available AI Providers
```
GET /api/verify/providers
```
→ `{ "providers": [{ "id": "gemini", "name": "Google Gemini", "status": "available" }], "default": "gemini" }`

---

## Admin Endpoints

All admin endpoints require either admin JWT or API key.

### Users
```
GET    /api/admin/users                    → List users (paginated, searchable)
GET    /api/admin/users/:id                → User details + verifications
PATCH  /api/admin/users/:id                → Update user (credits, isAdmin)
POST   /api/admin/users/:id/reset-password → Send password reset email
POST   /api/admin/users/:id/resend-confirmation → Resend email confirmation
GET    /api/admin/users/stats/overview     → User statistics
```

Query params for list: `?page=1&pageSize=20&search=email&sortBy=createdAt&sortOrder=desc`

### Verifications
```
GET /api/admin/verifications              → List all verifications (paginated)
GET /api/admin/verifications/stats        → Verification statistics
GET /api/admin/verifications/:id          → Full verification details
GET /api/admin/verifications/filters/item-types → Distinct item types for filtering
```

Query params: `?page=1&pageSize=20&status=completed&itemType=nike_dunk_low&userId=xxx`

### Purchases
```
GET /api/admin/purchases                  → List purchases (paginated)
GET /api/admin/purchases/stats            → Revenue statistics
GET /api/admin/purchases/:id              → Purchase details
```

### Promo Codes
```
GET    /api/admin/promo                   → List promo codes
GET    /api/admin/promo/:id               → Promo code details + usage
POST   /api/admin/promo                   → Create promo code
PATCH  /api/admin/promo/:id               → Update promo code
DELETE /api/admin/promo/:id               → Delete promo code
POST   /api/admin/promo/:id/validate      → Validate a promo code
```

### API Keys
```
GET    /api/admin/api-keys                → List API keys
POST   /api/admin/api-keys                → Create API key
DELETE /api/admin/api-keys/:id            → Revoke API key
```

### Analytics
```
GET /api/admin/analytics                  → Dashboard analytics
```

---

## Health
```
GET /health       → { "status": "ok", "timestamp": "..." }
GET /health/ready → { "status": "ready", "database": true }
```

---

## Error Format

All errors follow:
```json
{ "error": "Human-readable message" }
```

| Status | Meaning |
|--------|---------|
| 400 | Bad request / validation failed |
| 401 | Missing or invalid authentication |
| 403 | Insufficient permissions |
| 404 | Resource not found |
| 409 | Conflict (duplicate itemType, criterion name) |
| 410 | Gone (deprecated endpoint) |
| 429 | Rate limited |
| 500 | Server error |
