API documentation

REST API reference

Base URL: https://api.orientalvindecoder.com/api/v1
Programmatic decode: X-API-Key header (create keys in the dashboard).
Web / session access: Authorization: Bearer <accessToken> from login.

How to authenticate decode requests
Use X-API-Key: ovd_live_… for server-to-server integrations (keys created once in the dashboard). Use Authorization: Bearer <accessToken> when calling from a logged-in web session — obtain the access token via POST /auth/login and refresh it with POST /auth/refresh.

POST /api/v1/auth/login Public · rate limited

Login (issue JWT access token)

Email/password login. Returns a short-lived access token for the Authorization: Bearer header. Refresh token is set as an httpOnly cookie — not returned in JSON.

Request

{
  "email": "user@example.com",
  "password": "your-password"
}

Response 200

{
  "success": true,
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "expiresIn": 900,
    "tokenType": "Bearer",
    "user": {
      "id": "usr_01H...",
      "email": "user@example.com",
      "accountState": "active"
    }
  }
}

Errors: 401 invalid credentials · 403 email not verified · 403 account suspended

POST /api/v1/auth/refresh Cookie: refreshToken

Refresh access token

Rotate the httpOnly refresh cookie and return a new access token when the current JWT expires (~15 minutes).

Request

POST /api/v1/auth/refresh
Cookie: refreshToken=<httpOnly cookie from login>

Response 200

{
  "success": true,
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "expiresIn": 900,
    "tokenType": "Bearer"
  }
}

Errors: 401 missing or revoked refresh token

POST /api/v1/validate-vin Quota: free · unlimited

Validate VIN format

Checks 17-character format and check digit. Never consumes daily quota.

Request

{
  "vin": "1HGCM82633A004352"
}

Response 200

{
  "success": true,
  "data": {
    "vin": "1HGCM82633A004352",
    "isValidFormat": true,
    "checkDigitValid": true
  }
}
POST /api/v1/decode Quota: 1 per successful decode

Decode a single VIN

Returns structured USA/Canada vehicle attributes from licensed NHTSA-backed data.

Request

{
  "vin": "1HGCM82633A004352"
}

Response 200

{
  "success": true,
  "data": {
    "vin": "1HGCM82633A004352",
    "modelYear": 2003,
    "make": "HONDA",
    "model": "Accord",
    "trim": "EX",
    "vehicleType": "PASSENGER CAR",
    "bodyStyle": "Sedan",
    "engine": { "cylinders": 4, "displacementL": 2.4, "fuelType": "Gasoline" },
    "transmission": "Automatic",
    "drivetrain": "FWD",
    "manufacturer": { "name": "HONDA", "country": "USA" },
    "safetyFeatures": []
  }
}

Errors: 400 invalid format · 404 not in database · 429 quota exceeded

POST /api/v1/decode-from-image Quota: plan-based · OCR failure does not consume quota

Decode VIN from image (OCR)

Upload a JPEG/PNG of a VIN plate. On success returns the same vehicle payload as /decode.

Request

Content-Type: multipart/form-data

image: <file>

Errors: 400 when no VIN is detected in the image

POST /api/v1/batch-decode Quota: billable successes only · max 1,000 VINs

Batch decode

Decode up to 1,000 VINs in one request. Successful VINs return results even if others fail. Duplicates are deduplicated in-summary.

Request

{
  "vins": ["1HGCM82633A004352", "INVALIDVIN1234567"]
}

Response 200

{
  "success": true,
  "data": {
    "summary": {
      "totalSubmitted": 2,
      "successful": 1,
      "failed": 1,
      "duplicates": 0,
      "billableCount": 1
    },
    "results": [
      { "vin": "1HGCM82633A004352", "status": "success", "vehicle": { } },
      { "vin": "INVALIDVIN1234567", "status": "error", "errorCode": "INVALID_VIN" }
    ]
  }
}
GET /api/v1/usage Quota: unlimited (read-only)

Usage & quota status

Current plan, daily used/remaining, and overage for the authenticated account or API key.

Response 200

{
  "success": true,
  "data": {
    "plan": "pro",
    "dailyQuota": 3333,
    "usedToday": 142,
    "remainingToday": 3191,
    "overageEnabled": true,
    "overageToday": 0,
    "periodEndsAt": "2026-08-01T00:00:00Z"
  }
}
POST /api/v1/webhooks Pro and Business only

Register a webhook

Subscribe to subscription, quota threshold, and API key events. Signing secret is returned once.

Request

{
  "url": "https://customer.example.com/hooks/vin-decoder",
  "events": ["subscription.updated", "quota.threshold", "api_key.revoked"]
}

Response 201: webhook ID and signing secret (shown once)

Admin routes live under /api/v1/admin/* and require an admin JWT — not listed on this public docs page. Rate limits: plan daily quota for decode; supplementary 100 req/min per user; login brute-force protection. This page is a prototype reference aligned with docs/Architecture/api-design.md.