Comprehensive API documentation for Biometric Attendance System Integration
Version 1.0 | Last Updated: October 10, 2025
This API provides a simple and efficient way to integrate biometric attendance systems with your workforce management platform. The system stores attendance records and provides endpoints for submitting and querying attendance data.
https://api.clms.co.tz/api/ (no .php in URLs)All API endpoints are publicly accessible and do not require authentication. You can access any endpoint directly without API keys or authentication headers.
Since authentication has been removed, consider implementing server-level security for production deployments:
Description: Check API health status and basic statistics
Authentication: Not required
{
"success": true,
"message": "API is operational",
"data": {
"api_status": "online",
"database_status": "connected",
"api_version": "1.0",
"timezone": "Africa/Dar_es_Salaam",
"server_time": "2025-10-10 14:30:00",
"statistics": {
"total_workers": 35,
"total_attendance_records": 1245,
"today_attendance": 28
}
},
"timestamp": "2025-10-10 14:30:00",
"api_version": "1.0"
}
Description: Submit attendance data from biometric system. Supports both single record and bulk submission formats.
Authentication: Not required
The API accepts biometric system format with automatic field mapping:
{
"biometricData": [
{
"status": 300,
"datetime": "2025-09-15T12:40:00Z",
"employeeIDNumber": "W00065",
"biometricName": "Gate 7/8",
"scanId": "SCAN-ID-493"
},
{
"status": 300,
"datetime": "2025-09-15T12:45:00Z",
"employeeIDNumber": "W00062",
"biometricName": "Gate 7/8",
"scanId": "SCAN-ID-473"
}
]
}
| Parameter | Type | Required | Description |
|---|---|---|---|
biometricData |
array | Required | Array of biometric records |
biometricData[].employeeIDNumberor employee_idor id_numberor worker_id |
string | Required | Employee identifier (supports multiple field names). Can be worker_id or id_number from the workers table. Priority: employeeIDNumber > employee_id > id_number > worker_id |
biometricData[].datetime |
datetime | Required | Check-in timestamp in ISO 8601 format (e.g., "2025-09-15T12:40:00Z") |
biometricData[].biometricName |
string | Optional | Location/device name (mapped to check_in_location and biometric_device_id) |
biometricData[].scanId |
string | Optional | Scan identifier (stored in remarks field) |
biometricData[].status |
integer | Optional | Status code: 300=present, 400=late, 500=absent, 600=on_leave (default: 300) |
{
"status": "success",
"message": "Successfully processed 2 biometric records",
"data": {
"processed": 2,
"successful": 2,
"failed": 0
}
}
{
"status": "success",
"message": "Successfully processed 2 biometric records",
"data": {
"processed": 3,
"successful": 2,
"failed": 1,
"failed_records": [
{
"index": 1,
"record": { ... },
"error": "Worker not found",
"details": {
"worker_id": "W00062"
}
}
]
}
}
employeeIDNumber (or employee_id/id_number/worker_id) → worker_iddatetime (ISO 8601) → check_in (converted to Y-m-d H:i:s)biometricName → check_in_location and biometric_device_idscanId → stored in remarks fieldstatus (numeric) → mapped to status enumFor backward compatibility, the API still accepts single record format:
| Parameter | Type | Required | Description |
|---|---|---|---|
worker_id OR id_number |
string | Required | Worker's unique identifier - use either worker_id or id_number (must exist in workers table) |
check_in |
datetime | Required | Check-in timestamp (Y-m-d H:i:s format) |
check_out |
datetime | Optional | Check-out timestamp (Y-m-d H:i:s format) |
status |
enum | Optional | Attendance status: present, absent, late, half_day, on_leave (default: present) |
biometric_device_id |
string | Optional | ID of the biometric device used |
check_in_location |
string | Optional | Location/device name for check-in |
check_out_location |
string | Optional | Location/device name for check-out |
remarks |
text | Optional | Additional notes or reasons |
{
"worker_id": "WRK20253975",
"check_in": "2025-10-10 08:30:00",
"biometric_device_id": "DEVICE_001",
"check_in_location": "Main Entrance",
"status": "present"
}
{
"id_number": "DPW/DSM/AP/251/027",
"check_in": "2025-10-10 08:30:00",
"biometric_device_id": "DEVICE_001",
"check_in_location": "Main Entrance",
"status": "present"
}
{
"success": true,
"message": "Attendance recorded successfully",
"data": {
"id": 1246,
"worker_id": "WRK20253975",
"worker_name": "Victor Sostenes Langula",
"date": "2025-10-10",
"check_in": "2025-10-10 08:30:00",
"check_out": null,
"total_hours": null,
"overtime_hours": 0,
"status": "present",
"biometric_device_id": "DEVICE_001"
},
"timestamp": "2025-10-10 08:30:15",
"api_version": "1.0"
}
{
"worker_id": "WRK20253975",
"check_in": "2025-10-10 08:30:00",
"check_out": "2025-10-10 17:45:00",
"check_out_location": "Main Entrance",
"status": "present"
}
{
"success": true,
"message": "Attendance record updated successfully",
"data": {
"id": 1246,
"worker_id": "WRK20253975",
"worker_name": "Victor Sostenes Langula",
"date": "2025-10-10",
"check_in": "2025-10-10 08:30:00",
"check_out": "2025-10-10 17:45:00",
"total_hours": 9.25,
"overtime_hours": 1.25,
"status": "present"
},
"timestamp": "2025-10-10 17:45:10",
"api_version": "1.0"
}
total_hours and overtime_hours when both check-in and check-out are provided. Standard work hours are set to 8 hours by default.
biometricData array.
Description: Query attendance records with filtering options
Authentication: Not required
| Parameter | Type | Required | Description |
|---|---|---|---|
worker_id OR id_number |
string | Optional | Filter by specific worker - use either worker_id or id_number |
date_from |
date | Optional | Start date (Y-m-d format) |
date_to |
date | Optional | End date (Y-m-d format) |
status |
enum | Optional | Filter by status: present, absent, late, half_day, on_leave |
page |
integer | Optional | Page number for pagination (default: 1) |
per_page |
integer | Optional | Records per page (default: 50, max: 100) |
GET /api/query-attendance?worker_id=WRK20253975&date_from=2025-10-01&date_to=2025-10-10&page=1&per_page=10 # OR using id_number: GET /api/query-attendance?id_number=DPW/DSM/AP/251/027&date_from=2025-10-01&date_to=2025-10-10
{
"success": true,
"message": "Attendance records retrieved successfully",
"data": [
{
"id": 1246,
"worker": {
"worker_id": "WRK20253975",
"name": "Victor Sostenes Langula",
"email": "victor.langula@gmail.com",
"phone": "0624089337"
},
"attendance": {
"date": "2025-10-10",
"check_in": "2025-10-10 08:30:00",
"check_out": "2025-10-10 17:45:00",
"total_hours": 9.25,
"overtime_hours": 1.25,
"status": "present"
},
"device_info": {
"biometric_device_id": "DEVICE_001",
"check_in_location": "Main Entrance",
"check_out_location": "Main Entrance"
},
"remarks": null,
"sync_status": "synced",
"timestamps": {
"created_at": "2025-10-10 08:30:15",
"updated_at": "2025-10-10 17:45:10"
}
}
],
"timestamp": "2025-10-10 18:00:00",
"api_version": "1.0",
"meta": {
"pagination": {
"total": 1,
"per_page": 10,
"current_page": 1,
"total_pages": 1,
"has_more": false
}
}
}
Description: Retrieve list of workers for biometric system synchronization
Authentication: Not required
| Parameter | Type | Required | Description |
|---|---|---|---|
status |
enum | Optional | Filter by status: available, busy, unavailable, banned, suspended |
page |
integer | Optional | Page number (default: 1) |
per_page |
integer | Optional | Records per page (default: 100, max: 500) |
GET /api/list-workers?status=available&per_page=20
{
"success": true,
"message": "Workers retrieved successfully",
"data": [
{
"id": 1,
"worker_id": "WRK20253975",
"first_name": "Victor Sostenes",
"last_name": "Langula",
"email": "victor.langula@gmail.com",
"phone": "0624089337",
"id_number": "E55454",
"availability_status": "available",
"registration_status": "approved"
}
],
"timestamp": "2025-10-10 14:30:00",
"api_version": "1.0",
"meta": {
"pagination": {
"total": 1,
"per_page": 20,
"current_page": 1,
"total_pages": 1,
"has_more": false
}
}
}
<?php
// Submit multiple attendance records
$biometricData = [
[
'status' => 300,
'datetime' => '2025-09-15T12:40:00Z',
'employeeIDNumber' => 'W00065',
'biometricName' => 'Gate 7/8',
'scanId' => 'SCAN-ID-493'
],
[
'status' => 300,
'datetime' => '2025-09-15T12:45:00Z',
'employeeIDNumber' => 'W00062',
'biometricName' => 'Gate 7/8',
'scanId' => 'SCAN-ID-473'
]
];
$payload = json_encode(['biometricData' => $biometricData]);
$ch = curl_init('https://api.clms.co.tz/api/submit-attendance');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json'
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
echo "Processed: " . $result['data']['processed'] . "\n";
echo "Successful: " . $result['data']['successful'] . "\n";
echo "Failed: " . $result['data']['failed'] . "\n";
?>
// Submit multiple attendance records
const biometricData = [
{
status: 300,
datetime: '2025-09-15T12:40:00Z',
employeeIDNumber: 'W00065',
biometricName: 'Gate 7/8',
scanId: 'SCAN-ID-493'
},
{
status: 300,
datetime: '2025-09-15T12:45:00Z',
employeeIDNumber: 'W00062',
biometricName: 'Gate 7/8',
scanId: 'SCAN-ID-473'
}
];
const response = await fetch('https://api.clms.co.tz/api/submit-attendance', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ biometricData })
});
const result = await response.json();
console.log(`Processed: ${result.data.processed}`);
console.log(`Successful: ${result.data.successful}`);
console.log(`Failed: ${result.data.failed}`);
import requests
# Submit multiple attendance records
biometric_data = [
{
'status': 300,
'datetime': '2025-09-15T12:40:00Z',
'employeeIDNumber': 'W00065',
'biometricName': 'Gate 7/8',
'scanId': 'SCAN-ID-493'
},
{
'status': 300,
'datetime': '2025-09-15T12:45:00Z',
'employeeIDNumber': 'W00062',
'biometricName': 'Gate 7/8',
'scanId': 'SCAN-ID-473'
}
]
response = requests.post(
'https://api.clms.co.tz/api/submit-attendance',
headers={
'Content-Type': 'application/json'
},
json={'biometricData': biometric_data}
)
result = response.json()
print(f"Processed: {result['data']['processed']}")
print(f"Successful: {result['data']['successful']}")
print(f"Failed: {result['data']['failed']}")
curl -X POST https://api.clms.co.tz/api/submit-attendance \
-H "Content-Type: application/json" \
-d '{
"biometricData": [
{
"status": 300,
"datetime": "2025-09-15T12:40:00Z",
"employeeIDNumber": "W00065",
"biometricName": "Gate 7/8",
"scanId": "SCAN-ID-493"
},
{
"status": 300,
"datetime": "2025-09-15T12:45:00Z",
"employeeIDNumber": "W00062",
"biometricName": "Gate 7/8",
"scanId": "SCAN-ID-473"
}
]
}'
<?php
// Submit single attendance record
$data = [
'worker_id' => 'WRK20253975',
'check_in' => '2025-10-10 08:30:00',
'biometric_device_id' => 'DEVICE_001',
'check_in_location' => 'Main Entrance',
'status' => 'present'
];
$ch = curl_init('https://api.clms.co.tz/api/submit-attendance');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json'
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
print_r($result);
?>
// Submit single attendance record
const submitAttendance = async () => {
const data = {
worker_id: 'WRK20253975',
check_in: '2025-10-10 08:30:00',
biometric_device_id: 'DEVICE_001',
check_in_location: 'Main Entrance',
status: 'present'
};
const response = await fetch('https://api.clms.co.tz/api/submit-attendance', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
const result = await response.json();
console.log(result);
};
submitAttendance();
import requests
import json
from datetime import datetime
# Submit single attendance record
url = 'https://api.clms.co.tz/api/submit-attendance'
headers = {
'Content-Type': 'application/json'
}
data = {
'worker_id': 'WRK20253975',
'check_in': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'biometric_device_id': 'DEVICE_001',
'check_in_location': 'Main Entrance',
'status': 'present'
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
print(json.dumps(result, indent=2))
curl -X POST https://api.clms.co.tz/api/submit-attendance \
-H "Content-Type: application/json" \
-d '{
"worker_id": "WRK20253975",
"check_in": "2025-10-10 08:30:00",
"biometric_device_id": "DEVICE_001",
"check_in_location": "Main Entrance",
"status": "present"
}'
The API uses standard HTTP status codes and returns consistent error responses:
| Status Code | Meaning | Description |
|---|---|---|
200 |
OK | Request succeeded |
201 |
Created | Resource created successfully |
400 |
Bad Request | Invalid request format or parameters |
403 |
Forbidden | Access denied |
404 |
Not Found | Resource not found (e.g., worker doesn't exist) |
405 |
Method Not Allowed | Wrong HTTP method used |
409 |
Conflict | Resource already exists |
422 |
Unprocessable Entity | Validation failed |
500 |
Internal Server Error | Server-side error occurred |
503 |
Service Unavailable | Database connection failed |
{
"success": false,
"error": "Error message describing what went wrong",
"timestamp": "2025-10-10 14:30:00",
"api_version": "1.0",
"details": {
"field_name": "Specific validation error"
}
}
{
"success": false,
"error": "Validation failed",
"timestamp": "2025-10-10 14:30:00",
"api_version": "1.0",
"details": {
"worker_id": "Worker ID or ID Number is required",
"check_in": "Invalid datetime format. Use: Y-m-d H:i:s"
}
}
{
"success": false,
"error": "Worker not found",
"timestamp": "2025-10-10 14:30:00",
"api_version": "1.0",
"details": {
"identifier": "WRK99999999",
"message": "No worker found with the provided worker_id or id_number"
}
}
{
"success": false,
"error": "Attendance already recorded for this date",
"timestamp": "2025-10-10 14:30:00",
"api_version": "1.0",
"details": {
"worker_id": "WRK20253975",
"date": "2025-10-10",
"suggestion": "Include check_out to update existing record"
}
}
sync_status field to track synchronization state between your biometric system and this API. Set it to "pending" when queuing data and "synced" after successful submission.
Solution:
config.phpSolution:
workers table/api/list-workers to get valid worker IDsFor development, you can enable detailed error messages in config.php:
// Development mode - shows detailed errors
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Production mode - logs errors, doesn't display
error_reporting(E_ALL);
ini_set('display_errors', 0);
ini_set('log_errors', 1);
For technical support, integration assistance, or to report issues: