Quickstart
From nothing to the summary of a month in three steps.
1. Create a key
In the Kabal app, an administrator of the company opens API in the menu and presses Nueva llave. The key has a name (what it is for, say "Payroll") and an expiry: 30, 90 or 365 days, a date, or none.
The key is shown once: copy it and store it as a secret, in the same place your system keeps its other credentials. Kabal keeps only a fingerprint of it and cannot show it again; if it is lost, create another and revoke the old one.
A key looks like this:
kba_4Hq0x9mT1vZp8bLwYc3RkN7sJ2dF6gAeU5oXiQyKtMh
2. Call the summary
Send the key in the Authorization header, as a bearer token, and ask for a date range. Dates are civil dates in the company's timezone, both included:
curl -H "Authorization: Bearer $KABAL_API_KEY" \
"https://app.kabal-attendance.com/api/v1/reports/summary/?date_from=2026-10-01&date_to=2026-10-31"
The answer is one row per active employee of every site, ordered by code:
{
"date_from": "2026-10-01",
"date_to": "2026-10-31",
"company_timezone": "America/Guatemala",
"results": [
{
"employee": "0193a4b2-7c1e-7d40-9a51-3f2c8b6e1d07",
"code": "E001",
"name": "Ana López",
"location": "0193a4b2-6f00-7a10-8c22-1b9d4e5f6a70",
"location_name": "Oficina central",
"expected_days": 22,
"expected_minutes": 10560,
"late_days": 3,
"late_minutes": 41,
"worked_minutes": 10612,
"ordinary_minutes": 10480,
"extra_minutes": 132,
"has_pending": false
}
]
}
The row above is cut short: the reference lists every field.
3. Read what a person owes
What each person owes over the range is one subtraction on the same row:
owed = expected_minutes - ordinary_minutes
Ana's shifts asked for 10 560 minutes and she worked 10 480 of them: she owes 80 minutes. The 132 extra minutes do not pay them back, because ordinary minutes never include extra time. Reports explains when the subtraction holds and when to wait before trusting it.
A script for the monthly close
import os
import requests
BASE = 'https://app.kabal-attendance.com/api/v1'
session = requests.Session()
session.headers['Authorization'] = f"Bearer {os.environ['KABAL_API_KEY']}"
summary = session.get(
f'{BASE}/reports/summary/',
params={'date_from': '2026-10-01', 'date_to': '2026-10-31', 'include_inactive': 'true'},
timeout=60,
)
summary.raise_for_status()
for row in summary.json()['results']:
owed = row['expected_minutes'] - row['ordinary_minutes']
print(row['code'], row['late_days'], row['late_minutes'], owed, row['has_pending'])
include_inactive=true brings the people who left during the month too; without it only the employees active today come back.
Next
- Authentication: what each 401 and 403 means.
- Reports: the detail, its pages and every column.