Skip to content

Leave API

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

Force-refresh balances from Xero (bypasses 1hr cache).

Permission: update:leave

Response: Same as GET /api/leave/balances.

List leave requests scoped by access level and tab.

Permission: view:leave

Query params:

  • tabmine (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 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"
}
]
}

Submit a new leave request. Initial status depends on the submitter’s access level:

  • Executiveapproved (auto-approved, integrations run immediately)
  • Manager/Headpending_exec
  • Lead/Employeepending_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)"
}

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 executive
  • pending_exec → approver must be executive

Reject a pending leave request.

Permission: update:leave

Body:

{
"reason": "string (required)"
}

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 the number of leave requests pending the current user’s approval. Used for the sidebar badge.

Permission: view:leave

Response: { "count": 3 }