Kaizely API Documentation

This document describes the REST API endpoints available in the Kaizely application.

Authentication

Kaizely API supports two authentication methods that can be used interchangeably:

Option 1: API Key Authentication (Recommended for IoT/Industrial Systems)

API keys provide long-lived, revocable credentials ideal for machine-to-machine communication such as Kepware, PLCs, and SCADA systems.

Obtaining an API Key:

  1. Log in to Kaizely as a SiteAdmin
  2. Navigate to Admin → Manage API Keys
  3. Click Create API Key
  4. Enter a descriptive name (e.g., "Kepware Production Line 1")
  5. Copy the displayed key immediately (shown only once)

Include the API key in the X-API-Key header for authenticated requests:

X-API-Key: kzly_your_api_key_here

Example cURL Request:

curl -X POST https://kaizely.com/api/AssetDowntimeTracker/offline \
  -H "X-API-Key: kzly_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"assetId": 123, "reasonName": "Planned Maintenance"}'

Security Features:

Option 2: JWT Bearer Token Authentication

JWT tokens are ideal for user-driven applications and short-lived sessions.

POST
/api/auth/login

Authenticate and receive a JWT token.

Request Body:

{
  "username": "string",
  "password": "string"
}

Response:

{
  "token": "jwt_token_here",
  "expiration": "2025-12-27T09:42:11.0000000Z"
}

No authentication required

Include the token in the Authorization header for authenticated requests:

Authorization: Bearer your_jwt_token_here

Token Lifetime:

JWT tokens expire after 1 hour by default. You'll need to refresh or re-authenticate after expiration.

API Controllers

Assets Controller

Manage assets within an organization.

GET
/api/Asset

Get all assets for the user's organization. Optional query parameter: onlineOnly=true

Authentication Required: API Key or JWT Bearer Token

GET
/api/Asset/{id}

Get a specific asset by ID.

JWT Authentication Required

GET
/api/Asset/Code/{code}

Get assets by code (case-insensitive).

JWT Authentication Required

POST
/api/Asset

Create a new asset.

Request Body:

{
  "name": "string",
  "code": "string",
  "description": "string",
  "parentId": number,
  "assetType": number,
  "sortOrder": number,
  "isOnline": boolean
}

JWT Authentication Required

PUT
/api/Asset/{id}

Update an existing asset.

Request Body:

{
  "name": "string",
  "code": "string",
  "description": "string",
  "parentId": number,
  "assetType": number,
  "sortOrder": number,
  "isOnline": boolean
}

JWT Authentication Required

DELETE
/api/Asset/{id}

Delete an asset. Requires SiteAdmin role.

JWT Authentication Required (SiteAdmin role)

Losses Controller

Manage losses within an organization.

GET
/api/Losses

Get all losses for the user's organization.

JWT Authentication Required

GET
/api/Losses/{id}

Get a specific loss by ID.

JWT Authentication Required

POST
/api/Losses

Create a new loss.

JWT Authentication Required

PUT
/api/Losses/{id}

Update an existing loss.

JWT Authentication Required

DELETE
/api/Losses/{id}

Delete a loss.

JWT Authentication Required

Quality Loss Dailies Controller

Manage daily machine quality losses (QualityLossDaily) within an organization.

GET
/api/QualityLossDailies

Get quality loss daily rows for the user's organization. Optional filters:

JWT Authentication Required

GET
/api/QualityLossDailies/{id}

Get a specific quality loss daily row by ID.

JWT Authentication Required

POST
/api/QualityLossDailies

Create a new quality loss daily row. OrgId is forced server-side.

Request Body (example):

{
  "assetId": 123,
  "defectModeId": 1,
  // OR (if defectModeId is not provided)
  // "defectModeName": "rework",
  "day": "2025-12-31",
  "occurrences": 2,
  "externalSource": "MES",
  "externalId": "abc-123"
}
            

Note: You can provide either defectModeId or defectModeName. If defectModeId is missing/0 and defectModeName is provided, the server will look up the defect mode by name (case-insensitive, org-scoped). If the name is unknown, the API returns 400 BadRequest.

Note: If TotalCost is not supplied, the server calculates it from AssetDefectModeCost (or UnitCostOverride if provided).

JWT Authentication Required

PUT
/api/QualityLossDailies/{id}

Update an existing quality loss daily row. Row must belong to the caller's organization.

JWT Authentication Required

DELETE
/api/QualityLossDailies/{id}

Delete a quality loss daily row. Row must belong to the caller's organization.

JWT Authentication Required

Loss Types Controller

Manage loss types within an organization.

GET
/api/LossTypes

Get all loss types for the user's organization.

JWT Authentication Required

GET
/api/LossTypes/{id}

Get a specific loss type by ID.

JWT Authentication Required

POST
/api/LossTypes

Create a new loss type.

Request Body:

{
  "name": "string"
}

JWT Authentication Required

PUT
/api/LossTypes/{id}

Update an existing loss type.

Request Body:

{
  "id": number,
  "name": "string"
}

JWT Authentication Required

DELETE
/api/LossTypes/{id}

Delete a loss type.

JWT Authentication Required

Asset Downtime Tracker Controller

Track asset downtime. Perfect for Kepware integration!

GET
/api/AssetDowntimeTracker/{id}

Get a downtime tracking record by ID.

Authentication Required: API Key or JWT Bearer Token

GET
/api/AssetDowntimeTracker/offline

Get all assets currently offline for the user's organization.

Authentication Required: API Key or JWT Bearer Token

POST
/api/AssetDowntimeTracker/offline

Set an asset offline and start downtime tracking. Ideal for Kepware/PLC integration.

Request Body:

{
  "assetId": number, // Required: ID of the asset to set offline
  "reasonId": number, // Optional: ID of downtime reason (preferred if provided)
  "reasonName": "string", // Optional: Name of downtime reason (case-insensitive, used if reasonId not provided)
  "startTimeUtc": "datetime" // Optional: Start time (defaults to current UTC time if not provided)
}

Note: Either reasonId or reasonName can be provided, but reasonId is preferred when available. Both are optional - downtime can be tracked without a specific reason.

Authentication Required: API Key or JWT Bearer Token

POST
/api/AssetDowntimeTracker/online

Set an asset online and end downtime tracking. Ideal for Kepware/PLC integration.

Request Body:

{
  "assetId": number, // Required: ID of the asset to set online
  "downtimeTrackingId": number, // Optional: Specific downtime record ID (if not provided, finds most recent open record)
  "endTimeUtc": "datetime" // Optional: End time (defaults to current UTC time if not provided)
}

Authentication Required: API Key or JWT Bearer Token

Kepware Downtime Controller

NEW! High-performance batch endpoint optimized for Kepware IoT Gateway integration. Processes multiple asset state changes in a single API call with automatic deduplication and state validation.

POST
/api/KepwareDowntime/batch

Batch update asset online/offline status from Kepware IoT Gateway. This endpoint is specifically designed to work with Kepware's native IoT Gateway JSON format.

🎯 Key Features:

📋 Request Body (Kepware IoT Gateway Format):

{
  "timestamp": 1770143088685,  // Optional: Unix milliseconds (not used, per-tag timestamps take precedence)
  "values": [
    {
      "id": "PLC.Line1.PRESS-001",  // Tag path - asset code extracted from last segment
      "v": 5,                        // Value: -1 = online, >0 = reason ID for offline
      "q": true,                     // Quality: true = good, false = bad (bad quality skipped)
      "t": 1770143088685             // Tag timestamp: Unix milliseconds
    },
    {
      "id": "Building.Floor2.WELD-003",
      "v": -1,                       // -1 means bring asset online
      "q": true,
      "t": 1770143089865
    }
  ]
}

🏷️ Asset Code Extraction:

The endpoint extracts the asset code from the last segment of the tag path (after the final period):

Note: Asset code matching is case-insensitive. The asset must exist in your organization.

📊 Value Interpretation:

Value Meaning Action
-1 Asset is online Closes open downtime record, calculates cost, marks asset online
> 0 Asset is offline Creates downtime record with reason ID = value, marks asset offline
0 Invalid Skipped with error message

🔄 Duplicate Handling:

When multiple updates for the same asset appear in a single batch request:

  1. Records are grouped by asset code (case-insensitive)
  2. Within each group, only the record with the latest timestamp (t) is processed
  3. All other records for that asset are automatically discarded

Example: If you send 3 updates for "PRESS-001" with timestamps 100, 200, 150, only the update with timestamp 200 will be processed.

✅ Quality Filtering:

Records with "q": false (bad quality) are automatically filtered out and not processed. Only good quality data ("q": true) affects asset state.

📤 Response Format:

{
  "success": true,              // Overall success (false if any errors occurred)
  "processedCount": 2,          // Number of assets successfully updated
  "skippedCount": 0,            // Number of assets skipped (errors, invalid state, etc.)
  "errors": [],                 // Array of error messages
  "updates": [
    {
      "tagId": "PLC.Line1.PRESS-001",
      "assetCode": "PRESS-001",
      "assetId": 123,
      "previousState": "online",        // State before this update
      "newState": "offline",            // State after this update
      "status": "offline",              // (Deprecated, use newState)
      "stateTransition": "online → offline",  // Visual representation
      "reason": "Unplanned Breakdown",  // Downtime reason (if going offline)
      "timestamp": "2026-03-02T18:31:28.685Z",
      "downtimeTrackingId": 456         // ID of created/closed downtime record
    },
    {
      "tagId": "Building.Floor2.WELD-003",
      "assetCode": "WELD-003",
      "assetId": 124,
      "previousState": "offline",
      "newState": "online",
      "status": "online",
      "stateTransition": "offline → online",
      "timestamp": "2026-03-02T18:31:29.865Z",
      "downtimeTrackingId": 789
    }
  ]
}

❌ Common Errors:

Error Cause Solution
Asset 'STMP' not found Asset code doesn't exist in organization Create asset in Kaizely with exact code
Asset already online Trying to set online when already online No action needed - skip is normal
Asset already offline Trying to set offline when already offline No action needed - skip is normal
Downtime reason ID 5 not found Reason ID doesn't exist in organization Create reason in Admin → Manage Downtime Reasons
No open downtime record Trying to set online but no downtime was started Ensure asset was set offline before bringing online

🔧 Example: Kepware IoT Gateway Configuration

1. Configure Kepware Tag Structure:

// In your PLC/Kepware, create tags like:
PLC.Line1.PRESS-001    // Integer: -1=online, >0=reason ID
PLC.Line1.WELD-003     // Integer: -1=online, >0=reason ID
Building.STMP          // Integer: -1=online, >0=reason ID

2. Configure IoT Gateway:

3. PLC Logic Example:

// In PLC ladder logic or structured text:
IF machine_running THEN
    status_tag := -1  // Online
ELSIF breakdown_detected THEN
    status_tag := 5   // Offline with reason ID 5 (Unplanned Breakdown)
ELSIF maintenance_mode THEN
    status_tag := 3   // Offline with reason ID 3 (Planned Maintenance)
END_IF

📈 State Transitions & Logging:

Every successful state change generates detailed log entries:

// Going offline:
Asset PRESS-001 successfully transitioned online → offline with reason 'Unplanned Breakdown' (ID: 5)

// Coming back online:
Asset PRESS-001 successfully transitioned offline → online, 
closed downtime ID 456, duration: 2.35 hours, cost: $47.50

💡 Best Practices:

🚀 Performance Characteristics:

Authentication Required: API Key or JWT Bearer Token