Record leave
Records leave from another system (an HR system, a paper form), approved at once, by the rules HR follows in the
Console: within the balance and the type's negative allowance, never on days the person already has leave, and
with over_reason when it's more than a per-occasion type allows. Working days never count public holidays,
closed days or days the person doesn't work (Labour Act s.22). Its days come off the balance and
leave.approved fires.
Scope: attendance:manage · Plan: Pro and Enterprise in live mode; every plan in test mode.
AuthorizationBearer <token>Send Authorization: Bearer <token> on every request. The token is one of:
| Prefix | What it is | Where it may be used |
|---|---|---|
agoo_sk_live_ | Secret key, live mode | Your servers only |
agoo_sk_test_ | Secret key, test mode | Your servers only |
agoo_pk_live_ | Publishable key, live mode | Browsers and apps: create pre-registrations and bookings, read public booking types (with their intake questions) and their free slots, read the visit types open for pre-registration with their public forms. Never lists people. |
agoo_pk_test_ | Publishable key, test mode | As above, in test mode |
Admins create keys in Console → Developers → API keys and choose each key's scopes. A key is shown once. Never put a secret key in a URL, a browser or a mobile app.
Scope: attendance:manage
Idempotency-Key*stringA UUID you generate for this operation. If you retry with the same key within 24 hours, Agoo returns the
original response instead of acting twice. While the first request is still running, a retry returns
conflict. Reusing a key with a different request returns idempotency_key_reused. Responses with
rate_limited, internal_error or unavailable aren't stored, so retry those with the same key.
uuidapplication/json- body
person_id*PersonIdA person ID (TypeID with the person prefix).
^person_[0-7][0-9a-hjkmnp-tv-z]{25}$"person_01kjsesxm0e4zbyvkr7bcfxbfg"type_id*LeaveTypeIdA leave type ID (TypeID with the ltype prefix).
^ltype_[0-7][0-9a-hjkmnp-tv-z]{25}$"ltype_01m4y8r6t0e9k2b7c5d3f1g0hj"starts_on*stringdateends_on*stringdatehalf_day?booleanA half day: one day, of a type taken in half days.
half_day_part?string"morning""afternoon"reason?stringlength <= 500extra?booleanMaternity: an abnormal confinement or multiple births add the type's extra days (s.57(3)).
over_reason?stringRequired when it's more than a per-occasion type allows.
length <= 300The leave, approved.
application/json- response
A leave request. A private type (sick and maternity by default) comes back as kind: away, without its type, reason or note.
id*LeaveRequestIdA leave request ID (TypeID with the leave prefix).
^leave_[0-7][0-9a-hjkmnp-tv-z]{25}$"leave_01m4z0q2k8fv7tqz3x9c1nbe5d"person_id*PersonIdA person ID (TypeID with the person prefix).
^person_[0-7][0-9a-hjkmnp-tv-z]{25}$"person_01kjsesxm0e4zbyvkr7bcfxbfg"type_id*|null for a private type.
^ltype_[0-7][0-9a-hjkmnp-tv-z]{25}$type_name*string"Annual leave""Away"kind*string"annual""sick""maternity""paternity""compassionate""study""unpaid""other""away"private*booleanstatus*string"pending""approved""declined""cancelled"starts_on*stringdateends_on*stringThe last day of leave.
datehalf_day*booleanhalf_day_part*|"morning""afternoon"nulldays*numberWhat it counts, in unit.
3unit*string"working_days""calendar_days"paid*booleanreason*|The person's note; null for a private type.
decision_note*|The approver's note; null for a private type.
decided_at*|date-timedecided_by*|Who decided it; null when recorded, decided through the API or approved at once.
^person_[0-7][0-9a-hjkmnp-tv-z]{25}$approval*|The approval's state; null when it needed none.
cancelled_at*|date-timecreated_at*stringdate-timeupdated_at*stringdate-timecurl -X POST 'https://api.agoo.ardent.africa/v1/leave-requests' \ -H 'Idempotency-Key: 3f6b2a1e-8c4d-4f7a-9b2e-5d1c0a7e9f64' \ -H 'Content-Type: application/json' \ -d '{ "person_id": "person_01kjw4mg80efgb5aqbsqnxhtbh", "type_id": "ltype_01m4y8r6t0e9k2b7c5d3f1g0hj", "starts_on": "2026-11-16", "ends_on": "2026-11-18", "reason": "Family visit in Ho"}'{ "id": "leave_01m4z0q2k8fv7tqz3x9c1nbe5d", "person_id": "person_01kjw4mg80efgb5aqbsqnxhtbh", "type_id": "ltype_01m4y8r6t0e9k2b7c5d3f1g0hj", "type_name": "Annual leave", "kind": "annual", "private": false, "status": "approved", "starts_on": "2026-11-16", "ends_on": "2026-11-18", "half_day": false, "half_day_part": null, "days": 3, "unit": "working_days", "paid": true, "reason": "Family visit in Ho", "decision_note": null, "decided_at": "2026-10-14T09:12:00Z", "decided_by": null, "approval": null, "cancelled_at": null, "created_at": "2026-10-14T09:12:00Z", "updated_at": "2026-10-14T09:12:00Z"}List leave requests GET
Returns leave requests, newest first by their first day: waiting for a decision (`pending`), `approved`, `declined` and `cancelled`. Filter by person, status, type, and dates (leave overlapping `from` to `to`). A private type of leave (sick and maternity by default) comes back as `kind: away` and `type_name: Away`, with `type_id`, `reason` and `decision_note` null: integrations never see what it is. Certificates never leave Agoo. **Scope:** `attendance:read` · **Plan:** Pro and Enterprise in live mode; every plan in test mode.
Get a leave request GET
Returns one leave request. A private type comes back as `away`. **Scope:** `attendance:read` · **Plan:** Pro and Enterprise in live mode; every plan in test mode.