Developer documentation
Attendance REST API and signed webhooks
Every punch that reaches your workspace is available over HTTPS in JSON. Pull records with the REST API, or receive each event as it happens through a signed webhook. Both use the same data, so you can start with one and add the other later.
Authentication
Create an API key in your dashboard under API keys. Keys begin with attn_ and are shown only once, so store yours in an environment variable rather than in your source code.
curl -X GET "https://biometric.asaanex.com/api/v1/attendance?limit=10" \
-H "Authorization: Bearer attn_your_api_key"A missing or unknown key returns 401 Unauthorized. A key only ever sees the workspace it belongs to.
Endpoints
| Setting on the device | Value to enter | Notes |
|---|---|---|
| GET /api/v1/attendance | Attendance punches | Paginated, filterable by employee, device and date range. |
| GET /api/v1/devices | Registered terminals | Serial number, location, last seen time and status. |
| GET /api/v1/employees | Employee list | Employee number, name and department. |
| POST /api/v1/employees | Create employees | One employee, or up to 500 in a single call. |
Attendance punches
Returns punches newest first, with the total count so you can page through history.
| Setting on the device | Value to enter | Notes |
|---|---|---|
| page | 1 | Page number. Defaults to 1. |
| limit | 50 | Records per page, up to 500. |
| employee_no | EMP1004 | Only punches for one employee number. |
| device_id | UUID | Only punches from one terminal. |
| from | 2026-09-01T00:00:00+05:00 | ISO 8601 with offset. Start of the range. |
| to | 2026-09-30T23:59:59+05:00 | ISO 8601 with offset. End of the range. |
const res = await fetch(
"https://biometric.asaanex.com/api/v1/attendance?limit=100&from=2026-09-01T00:00:00%2B05:00",
{ headers: { Authorization: `Bearer ${process.env.ASAANEX_API_KEY}` } }
);
const { data, total, page, limit } = await res.json();
console.log(`${data.length} of ${total} records`);{
"data": [
{
"id": "…",
"employee_no": "EMP1004",
"event_time": "2026-09-23T09:31:04+05:00",
"in_out_state": "check_in",
"verify_type": "fingerprint",
"device_id": "…",
"employee_id": "…",
"source": "iclock",
"created_at": "2026-09-23T04:31:07.812Z"
}
],
"total": 1284,
"page": 1,
"limit": 100
}Employees and devices
Employee records carry the employee number that the terminal sends, so punches line up with the staff list in your own system. You can create employees one at a time, or send up to 500 in a single call.
curl -X POST "https://biometric.asaanex.com/api/v1/employees" \
-H "Authorization: Bearer attn_your_api_key" \
-H "Content-Type: application/json" \
-d '{"employees":[
{"employee_no":"EMP1004","name":"Ali Khan","department":"Production"},
{"employee_no":"EMP1005","name":"Sana Iqbal","department":"Accounts"}
]}'GET /api/v1/deviceslists your terminals with their serial number, location, last seen time and whether they are active, which is useful for an “is the machine online” check in your own admin screens.
Signed webhooks
Add an endpoint in the dashboard and choose the events you want. Asaanex sends a POST with a JSON body as each event happens. Current events are attendance.created, device.connected and webhook.test.
| Setting on the device | Value to enter | Notes |
|---|---|---|
| X-Asaanex-Event | attendance.created | The event that triggered this delivery. |
| X-Asaanex-Delivery | UUID | Unique id for this delivery attempt. |
| X-Asaanex-Signature-256 | sha256=... | HMAC-SHA256 of the raw body, keyed with your endpoint secret. |
{
"id": "evt_9f1c2b7d4a8e4f0b9c2d1e3f4a5b6c7d",
"event": "attendance.created",
"created_at": "2026-09-23T04:31:07.812Z",
"tenant_id": "e5324557-11f9-4225-adfb-63019ceb3ce6",
"data": {
"employee_no": "EMP1004",
"event_time": "2026-09-23T09:31:04+05:00",
"verify_type": "fingerprint",
"in_out_state": "check_in"
}
}Verify every request before you trust it. Compute the signature over the raw body, then compare in constant time:
import crypto from "node:crypto";
export function verifyAsaanexSignature(rawBody, header, secret) {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(header ?? "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Failed deliveries are retried up to five times, with a ten second timeout per attempt. Every attempt and the response it received are recorded in the dashboard.
Where punches come from
Attendance arrives from your biometric terminals over the ADMS or JSON push protocols. If you have not connected a device yet, start with the device setup guides, then come back here to pull the data into your own software.
Get an API key today
Create a workspace, connect a terminal, and call the API on real punches. Free for 3 months.
Developer questions
How do I get an API key?
Open your workspace dashboard, go to API keys, and create one. The key is shown once and starts with attn_. Send it as an Authorization: Bearer header on every request.
Is the API rate limited?
Requests return up to 500 records per page. For continuous syncing, use webhooks instead of polling in a tight loop, so your system receives each punch as it happens.
How do I verify a webhook really came from Asaanex?
Compute an HMAC-SHA256 of the exact raw request body using your endpoint secret, prefix it with sha256=, and compare it with the X-Asaanex-Signature-256 header using a constant-time comparison. Reject the request if they differ.
What happens if my webhook endpoint is down?
Each delivery is retried up to five times with a ten second timeout per attempt. Deliveries and their responses are recorded in the dashboard so you can see what failed and why.