📋 Attendance System API

Comprehensive API documentation for Biometric Attendance System Integration

Version 1.0 | Last Updated: October 10, 2025

📖 Overview

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.

Key Features

📌 Base URL: https://amline-attendance.clms.co.tz/api/
🕐 Timezone: Africa/Dar_es_Salaam
📅 Date Format: Y-m-d (e.g., 2025-10-10)
🕒 DateTime Format: Y-m-d H:i:s (e.g., 2025-10-10 08:30:00)

🔐 Authentication

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

Default Test Credentials

API Key:    BIO_SYSTEM_2025_API_KEY_12345678
API Secret: SECRET_2025_BIOMETRIC_INTEGRATION_KEY_987654321
⚠️ Security Note: Change the default API credentials in production! Store your API credentials securely and never expose them in client-side code.

IP Whitelisting

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.

🔗 API Endpoints

GET /api/status.php

Description: Check API health status and basic statistics

Authentication: Not required

Success Response (200 OK):

{
    "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"
}
POST /api/submit-attendance.php

Description: Submit attendance data from biometric system. Supports both single record and bulk submission formats.

Authentication: Required

Permission: attendance.submit

✨ New Feature: Bulk submission support! Submit multiple attendance records in a single API call for improved efficiency.

📦 Bulk Submission Format (Recommended)

The API accepts biometric system format with automatic field mapping:

Bulk Request Format:

{
    "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"
        }
    ]
}

Bulk Request Parameters:

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)

Bulk Submission Response:

{
    "status": "success",
    "message": "Successfully processed 2 biometric records",
    "data": {
        "processed": 2,
        "successful": 2,
        "failed": 0
    }
}

Partial Success Response (some records failed):

{
    "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"
                }
            }
        ]
    }
}
💡 Field Mapping: The API automatically maps biometric format fields:
  • employeeIDNumberworker_id
  • datetime (ISO 8601) → check_in (converted to Y-m-d H:i:s)
  • biometricNamecheck_in_location and biometric_device_id
  • scanId → stored in remarks field
  • status (numeric) → mapped to status enum

📝 Single Record Format (Legacy - Still Supported)

For backward compatibility, the API still accepts single record format:

Request Parameters:

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

Example Request:

{
    "worker_id": "WRK20253975",
    "check_in": "2025-10-10 08:30:00",
    "biometric_device_id": "DEVICE_001",
    "check_in_location": "Main Entrance",
    "status": "present"
}

Success Response (201 Created):

{
    "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"
}

Check-Out Update Request:

{
    "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"
}

Update Response (200 OK):

{
    "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"
}
💡 Note: The system automatically calculates total_hours and overtime_hours when both check-in and check-out are provided. Standard work hours are set to 8 hours by default.
🔄 Backward Compatibility: Single record format is still fully supported. The API automatically detects the format based on the presence of biometricData array.
GET /api/query-attendance.php

Description: Query attendance records with filtering options

Authentication: Required

Permission: attendance.query

Query Parameters:

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)

Example Request:

GET /api/query-attendance.php?worker_id=WRK20253975&date_from=2025-10-01&date_to=2025-10-10&page=1&per_page=10

Success Response (200 OK):

{
    "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
        }
    }
}
GET /api/list-workers.php

Description: Retrieve list of workers for biometric system synchronization

Authentication: Required

Permission: workers.list

Query Parameters:

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)

Example Request:

GET /api/list-workers.php?status=available&per_page=20

Success Response (200 OK):

{
    "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
        }
    }
}

💻 Code Examples

📦 Bulk Submission Examples

PHP Example - Bulk Submission

<?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";
?>

JavaScript Example - Bulk Submission

// 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}`);

Python Example - Bulk Submission

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 Example - Bulk Submission

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"
        }
    ]
}'

📝 Single Record Examples (Legacy Format)

PHP Example (cURL)

<?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);
?>

JavaScript Example (Fetch API)

// 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();

Python Example (Requests)

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 Command Line

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"
  }'

⚠️ Error Handling

The API uses standard HTTP status codes and returns consistent error responses:

HTTP Status Codes

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

Error Response Format

{
    "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"
    }
}

Common Error Examples

Authentication Error (401):

{
    "success": false,
    "error": "Invalid API key",
    "timestamp": "2025-10-10 14:30:00",
    "api_version": "1.0"
}

Validation Error (422):

{
    "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"
    }
}

Not Found Error (404):

{
    "success": false,
    "error": "Worker not found",
    "timestamp": "2025-10-10 14:30:00",
    "api_version": "1.0",
    "details": {
        "worker_id": "WRK99999999"
    }
}

Duplicate Error (409):

{
    "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"
    }
}

✨ Best Practices

Security

Performance

Integration

💡 Pro Tip: Use the 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.

🔧 Troubleshooting

Common Issues

Issue: "Database connection failed"

Solution:

  • Verify database credentials in config.php
  • Ensure MySQL server is running
  • Check database name and permissions
  • Verify firewall rules allow database connections

Issue: "Invalid API key"

Solution:

  • Verify API key is correct and hasn't been modified
  • Check that the key exists in the api_keys table
  • Ensure is_active is set to 1
  • Check if the key has expired (expires_at)

Issue: "Worker not found"

Solution:

  • Verify the worker_id exists in the workers table
  • Ensure exact match (case-sensitive)
  • Use /api/list-workers.php to get valid worker IDs

Issue: "IP address not authorized"

Solution:

  • Add your IP to the ip_whitelist JSON array
  • Use CIDR notation for IP ranges (e.g., "192.168.1.0/24")
  • Check your actual IP address (may differ if behind proxy)
  • Set ip_whitelist to NULL to disable IP filtering

Debug Mode

For 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);

📞 Support & Contact

For technical support, integration assistance, or to report issues:

📚 Version History:
v1.0 (October 10, 2025) - Initial release
  • Core attendance submission and querying
  • API authentication with IP whitelisting
  • Worker list endpoint
  • Automatic overtime calculation