Files
boc/vims-backend/docs/API.md
T
Bernt 6de2455917 v1.2.0: Add Global Markets footer, translated to 9 languages
- Added GLOBAL_MARKETS_TITLE to all translation files
- Updated footer with 12 markets (4 active + 8 upcoming)
- Translated market section to: zh-cn, zh-tw, ja, ko, th, vi, id, ms, hi
- Built and deployed to production
- CloudFront invalidation: I3RTMXVFDJXWLG3SYX208OP1CC
2026-07-08 19:56:03 +00:00

6.1 KiB

VIMS API Documentation

Base URL

Production: https://vims.landvex.com/api/v1
Development: http://localhost:3450/api/v1

Authentication

All endpoints require Bearer token authentication:

Authorization: Bearer <token>

Endpoints

Objects

List Objects

GET /api/v1/objects

Query Parameters:

  • type - Filter by object type (atm, charging_station, parking_meter, defibrillator)
  • status - Filter by status (green, yellow, orange, red, unknown)
  • customerId - Filter by customer
  • page - Page number (default: 1)
  • limit - Items per page (default: 20)
  • search - Search in name, externalId, address

Response:

{
  "objects": [
    {
      "id": "uuid",
      "name": "ATM Sveavägen 123",
      "objectType": {
        "name": "Bankomat",
        "category": "payment"
      },
      "currentStatus": "green",
      "location": {
        "latitude": 59.3368,
        "longitude": 18.0555
      },
      "lastInspection": "2026-07-08T10:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 100,
    "pages": 5
  }
}

Create Object

POST /api/v1/objects

Request Body:

{
  "objectTypeId": "uuid",
  "customerId": "uuid",
  "name": "ATM Sveavägen 123",
  "externalId": "ATM-001",
  "location": {
    "latitude": 59.3368,
    "longitude": 18.0555
  },
  "address": "Sveavägen 123, Stockholm",
  "manufacturer": "NCR",
  "model": "SelfServ 22",
  "installationDate": "2025-01-15"
}

Set Baseline Images

POST /api/v1/objects/:id/baseline

Request Body:

{
  "images": [
    {
      "angle": "front",
      "imageUrl": "https://s3.landvex.com/baseline/atm-001-front.jpg"
    },
    {
      "angle": "back",
      "imageUrl": "https://s3.landvex.com/baseline/atm-001-back.jpg"
    }
  ]
}

Observations

Upload Observation

POST /api/v1/observations

Content-Type: multipart/form-data

Fields:

  • images - Image files (JPEG, PNG, WebP)
  • objectId - Object UUID
  • angle - Camera angle (front, back, left, right, top, detail)
  • zoomerId - Zoomer UUID (optional)
  • missionId - Mission UUID (optional)
  • takenAt - ISO timestamp (optional)
  • gpsAccuracy - GPS accuracy in meters (optional)

Response:

{
  "observations": [
    {
      "id": "uuid",
      "status": "pending",
      "imageUrl": "https://s3.landvex.com/obs/..."
    }
  ],
  "objectId": "uuid",
  "processed": 1
}

Get Observation

GET /api/v1/observations/:id

Response includes detections:

{
  "id": "uuid",
  "imageUrl": "...",
  "angle": "front",
  "status": "analyzed",
  "detections": [
    {
      "componentType": "card_reader",
      "confidence": 0.95,
      "boundingBox": {
        "x": 0.1,
        "y": 0.2,
        "width": 0.3,
        "height": 0.1
      },
      "isAnomaly": false
    }
  ]
}

Compare with Baseline

GET /api/v1/observations/:id/compare

Response:

{
  "observationId": "uuid",
  "isSignificant": true,
  "changeType": "structural",
  "metrics": {
    "pixelDifference": 0.15,
    "structuralDifference": 0.25,
    "overallDifference": 0.20
  },
  "confidence": 0.92,
  "diffVisualization": "base64encoded..."
}

Detections

List Detections

GET /api/v1/detections

Query Parameters:

  • objectId - Filter by object
  • componentType - Filter by component (card_reader, pin_pad, etc.)
  • isAnomaly - Filter anomalies (true/false)
  • severity - Filter by severity (low, medium, high, critical)

Verify Detection

POST /api/v1/detections/:id/verify

Request Body:

{
  "isCorrect": true,
  "correctedType": "card_reader",
  "notes": "Verified by security team"
}

Alerts

List Alerts

GET /api/v1/alerts

Query Parameters:

  • status - open, acknowledged, investigating, resolved, false_positive
  • riskLevel - green, yellow, orange, red
  • objectId - Filter by object

Update Alert Status

PUT /api/v1/alerts/:id/status

Request Body:

{
  "status": "resolved",
  "resolution": "False positive - approved maintenance",
  "assignedTo": "user-uuid"
}

Escalate Alert

POST /api/v1/alerts/:id/escalate

Request Body:

{
  "reason": "Potential skimmer device detected",
  "escalateTo": "security-team"
}

Dashboard

Overview Statistics

GET /api/v1/dashboard/overview

Response:

{
  "summary": {
    "totalObjects": 150,
    "activeAlerts": 12,
    "recentObservations": 45,
    "overdueObjects": 3,
    "compliance": "98.0"
  },
  "objectStats": {
    "green": 130,
    "yellow": 10,
    "orange": 5,
    "red": 2
  },
  "alertRiskStats": {
    "orange": 8,
    "red": 4
  }
}

Map Objects

GET /api/v1/dashboard/objects

Query Parameters:

  • bounds - Geo bounds: {"sw":{"lat":59.3,"lng":18.0},"ne":{"lat":59.4,"lng":18.1}}
  • status - Filter by status

Activity Timeline

GET /api/v1/dashboard/timeline?days=7

Response:

{
  "observations": [
    {"date": "2026-07-01", "count": 15},
    {"date": "2026-07-02", "count": 20}
  ],
  "alerts": [
    {"date": "2026-07-01", "riskLevel": "orange", "count": 2}
  ]
}

Top Priority Alerts

GET /api/v1/dashboard/top-alerts

Webhooks

VIMS can send webhooks to your endpoint:

Alert Webhook

{
  "event": "vims.alert",
  "timestamp": "2026-07-08T12:00:00Z",
  "data": {
    "alertId": "uuid",
    "riskLevel": "red",
    "title": "Skimmer detected",
    "message": "Potential skimmer device found on ATM",
    "objectId": "uuid",
    "customerId": "uuid"
  }
}

Observation Webhook

{
  "event": "vims.observation",
  "timestamp": "2026-07-08T12:00:00Z",
  "data": {
    "observationId": "uuid",
    "objectId": "uuid",
    "status": "analyzed",
    "riskLevel": "green"
  }
}

Error Responses

{
  "error": "Validation Error",
  "message": "Missing required fields",
  "fields": ["objectId", "angle"]
}

Status Codes:

  • 200 - Success
  • 201 - Created
  • 400 - Bad Request
  • 401 - Unauthorized
  • 403 - Forbidden
  • 404 - Not Found
  • 409 - Conflict
  • 500 - Internal Server Error