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://amline-attendance.clms.co.tz/api/All API requests (except the status endpoint) require authentication using API keys. Include the following headers in every request:
| Header | Description | Example |
|---|---|---|
X-API-Key |
Your unique API key | BIO_SYSTEM_2025_API_KEY_12345678 |
X-API-Secret |
Your API secret key | SECRET_2025_BIOMETRIC_INTEGRATION_KEY_987654321 |
API Key: BIO_SYSTEM_2025_API_KEY_12345678 API Secret: SECRET_2025_BIOMETRIC_INTEGRATION_KEY_987654321
For enhanced security, you can restrict API access to specific IP addresses. Configure the ip_whitelist field in the api_keys table. Supports both individual IPs and CIDR notation.
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: Required
Permission: attendance.submit
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[].employeeIDNumber |
string | Required | Worker's unique ID (mapped to 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 → 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 |
string | Required | Worker's unique ID (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"
}
{
"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: Required
Permission: attendance.query
| Parameter | Type | Required | Description |
|---|---|---|---|
worker_id |
string | Optional | Filter by specific worker ID |
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.php?worker_id=WRK20253975&date_from=2025-10-01&date_to=2025-10-10&page=1&per_page=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: Required
Permission: workers.list
| 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.php?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://amline-attendance.clms.co.tz/api/submit-attendance.php');
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',
'X-API-Key: BIO_SYSTEM_2025_API_KEY_12345678',
'X-API-Secret: SECRET_2025_BIOMETRIC_INTEGRATION_KEY_987654321'
]);
$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://amline-attendance.clms.co.tz/api/submit-attendance.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'BIO_SYSTEM_2025_API_KEY_12345678',
'X-API-Secret': 'SECRET_2025_BIOMETRIC_INTEGRATION_KEY_987654321'
},
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://amline-attendance.clms.co.tz/api/submit-attendance.php',
headers={
'X-API-Key': 'BIO_SYSTEM_2025_API_KEY_12345678',
'X-API-Secret': 'SECRET_2025_BIOMETRIC_INTEGRATION_KEY_987654321'
},
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://amline-attendance.clms.co.tz/api/submit-attendance.php \
-H "Content-Type: application/json" \
-H "X-API-Key: BIO_SYSTEM_2025_API_KEY_12345678" \
-H "X-API-Secret: SECRET_2025_BIOMETRIC_INTEGRATION_KEY_987654321" \
-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://amline-attendance.clms.co.tz/api/submit-attendance.php');
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',
'X-API-Key: BIO_SYSTEM_2025_API_KEY_12345678',
'X-API-Secret: SECRET_2025_BIOMETRIC_INTEGRATION_KEY_987654321'
]);
$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://amline-attendance.clms.co.tz/api/submit-attendance.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'BIO_SYSTEM_2025_API_KEY_12345678',
'X-API-Secret': 'SECRET_2025_BIOMETRIC_INTEGRATION_KEY_987654321'
},
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://amline-attendance.clms.co.tz/api/submit-attendance.php'
headers = {
'Content-Type': 'application/json',
'X-API-Key': 'BIO_SYSTEM_2025_API_KEY_12345678',
'X-API-Secret': 'SECRET_2025_BIOMETRIC_INTEGRATION_KEY_987654321'
}
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://amline-attendance.clms.co.tz/api/submit-attendance.php \
-H "Content-Type: application/json" \
-H "X-API-Key: BIO_SYSTEM_2025_API_KEY_12345678" \
-H "X-API-Secret: SECRET_2025_BIOMETRIC_INTEGRATION_KEY_987654321" \
-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 |
401 |
Unauthorized | Authentication failed |
403 |
Forbidden | Insufficient permissions or IP not allowed |
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": "Invalid API key",
"timestamp": "2025-10-10 14:30:00",
"api_version": "1.0"
}
{
"success": false,
"error": "Validation failed",
"timestamp": "2025-10-10 14:30:00",
"api_version": "1.0",
"details": {
"worker_id": "Worker ID 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": {
"worker_id": "WRK99999999"
}
}
{
"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"
}
}
last_used_at for unusual API activitysync_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:
api_keys tableis_active is set to 1expires_at)Solution:
workers table/api/list-workers.php to get valid worker IDsSolution:
ip_whitelist JSON arrayip_whitelist to NULL to disable IP filteringFor 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: