Leave API
Endpoints
Section titled “Endpoints”GET /api/leave/types
Section titled “GET /api/leave/types”List cached leave types from Xero. Auto-refreshes from Xero PayItems if cache is older than 1 hour.
Permission: view:leave
Response:
{ "types": [ { "id": "xero-leave-type-id", "name": "Annual Leave", "is_paid": 1 } ]}GET /api/leave/balances
Section titled “GET /api/leave/balances”Get cached leave balances for the current user. Requires a Xero employee mapping in user_external_ids.
Permission: view:leave
Response:
{ "balances": [ { "id": "uuid", "leave_type_id": "xero-id", "leave_type_name": "Annual Leave", "balance_hours": 152.0, "fetched_at": "2026-03-01T10:00:00" } ]}POST /api/leave/balances/refresh
Section titled “POST /api/leave/balances/refresh”Force-refresh balances from Xero (bypasses 1hr cache).
Permission: update:leave
Response: Same as GET /api/leave/balances.
GET /api/leave/requests
Section titled “GET /api/leave/requests”List leave requests scoped by access level and tab.
Permission: view:leave
Query params:
tab—mine(own requests),pending(awaiting your approval),all(all requests, head/exec only)
Response:
{ "requests": [ { "id": "uuid", "user_id": "uuid", "user_name": "Name", "user_picture": "https://...", "leave_type_name": "Annual Leave", "start_date": "2026-03-10", "end_date": "2026-03-14", "total_hours": 40, "status": "pending_exec", "created_at": "2026-03-01T10:00:00" } ]}GET /api/leave/requests/:id
Section titled “GET /api/leave/requests/:id”Get a single leave request with full details and history.
Permission: view:leave
Response:
{ "request": { "id": "uuid", "user_id": "uuid", "user_name": "Name", "leave_type_id": "xero-id", "leave_type_name": "Annual Leave", "start_date": "2026-03-10", "end_date": "2026-03-14", "total_hours": 40, "notes": "Family holiday", "status": "approved", "manager_approved_by": "uuid", "manager_approved_at": "2026-03-02T09:00:00", "exec_approved_by": "uuid", "exec_approved_at": "2026-03-02T10:00:00", "xero_leave_application_id": "xero-id", "productive_booking_id": "123", "google_calendar_event_id": "gcal-id", "slack_message_ts": "1234567890.123456", "integration_errors": null, "created_at": "2026-03-01T10:00:00" }, "history": [ { "id": "uuid", "action": "submitted", "actor_id": "uuid", "actor_name": "Name", "comment": null, "created_at": "2026-03-01T10:00:00" } ]}POST /api/leave/requests
Section titled “POST /api/leave/requests”Submit a new leave request. Initial status depends on the submitter’s access level:
- Executive →
approved(auto-approved, integrations run immediately) - Manager/Head →
pending_exec - Lead/Employee →
pending_manager
Permission: update:leave
Body:
{ "leave_type_id": "xero-leave-type-id (required)", "leave_type_name": "Annual Leave (required)", "start_date": "2026-03-10 (required, YYYY-MM-DD)", "end_date": "2026-03-14 (required, YYYY-MM-DD)", "total_hours": 40, "notes": "Family holiday (optional)"}POST /api/leave/requests/:id/approve
Section titled “POST /api/leave/requests/:id/approve”Approve a pending leave request. Manager approval advances to pending_exec. Executive approval triggers integrations.
Permission: update:leave
Auth checks:
pending_manager→ approver must be squad manager or executivepending_exec→ approver must be executive
POST /api/leave/requests/:id/reject
Section titled “POST /api/leave/requests/:id/reject”Reject a pending leave request.
Permission: update:leave
Body:
{ "reason": "string (required)"}POST /api/leave/requests/:id/cancel
Section titled “POST /api/leave/requests/:id/cancel”Cancel an approved or pending leave request. If the request was approved, cancellation reverses all external integrations (Productive booking deleted, Calendar event deleted, Slack cancellation posted).
Permission: update:leave (own requests or executive)
POST /api/leave/requests/:id/retry-integrations
Section titled “POST /api/leave/requests/:id/retry-integrations”Retry failed integrations for an approved request. Clears previous errors and re-runs all integrations.
Permission: manage:leave (Executive only)
GET /api/leave/pending-count
Section titled “GET /api/leave/pending-count”Get the number of leave requests pending the current user’s approval. Used for the sidebar badge.
Permission: view:leave
Response: { "count": 3 }