Guides

Leave

Read leave types, requests, balances, public holidays and locked dates, book and cancel leave, and approve or reject requests through the API, with the visibility rules that apply.

This guide covers the leave side of the API: what can be booked, who is away, how much allowance is left, and the approval flow. Every parameter and field is in the API Reference.

Leave written through the API follows exactly the same rules as leave booked in the app: notice periods, balances, overlaps, locked dates, holidays, approval chains, notifications and calendar sync all apply.

Scopes for this area

TaskScopeLowest owner role
Read leave typesleave_types:readEmployee
Read leave requestsleaves:readEmployee
Book and cancel leaveleaves:writeEmployee
Approve and reject requestsleaves:approveDepartment Head
Read balancesleave_balances:readEmployee
Read public holidays and locked datesholidays:readEmployee

The Leave sync and Leave management templates bundle these scopes.

Whose leave a token can see

A token sees the leave its owner sees in the app:

  • Their own leave, always.
  • Anyone whose personal calendar the owner may open. That is everyone, unless your organization restricts calendars; then it is the owner's direct reports and, for a Department Head, the people in their departments. Administrators and Executives can open everyone's.
  • What the team calendar shows them: pending and approved leave across the organization, or only their own department when your organization hides other departments.

A leave the owner cannot see answers 404 NOT_FOUND, the same as an id that does not exist.

Private leave types are redacted unless the owner may see their details: the person themselves, Administrators, Executives, and the Department Head of the person's department, as in the app. For everyone else the leave type appears as "name": "Private" and the reason and other free text are null. The dates still show, so availability stays correct. On GET /leave-types, a private type is masked the same way unless the owner is a Department Head, Administrator or Executive; its id still books it.

Find out who is away

curl "https://app.bookyourpto.com/api/v1/leaves?status=APPROVED&start_date=2026-08-01&end_date=2026-08-31&limit=100" \
  -H "Authorization: Bearer $BOOKYOURPTO_TOKEN"

start_date and end_date select leave that overlaps the period: leave ending on or after start_date and starting on or before end_date. Results are ordered by start date. Filter further with user_id, status and leave_type_id.

{
  "data": [
    {
      "id": "cmlve0hmarsh2026080300",
      "user_id": "cmusr0lenafischer000000",
      "leave_type": { "id": "cmlvt0annual00000000000", "name": "Annual Leave", "code": "ANNUAL", "color": "#3b82f6", "is_private": false },
      "status": "APPROVED",
      "start_date": "2026-08-03",
      "end_date": "2026-08-07",
      "start_half": "FULL_DAY",
      "end_half": "FULL_DAY",
      "total_days": 5,
      "...": "more fields"
    }
  ],
  "meta": { "request_id": "...", "has_more": false, "next_cursor": null }
}

start_date and end_date are calendar days, inclusive. A leave from 2026-08-03 to 2026-08-07 covers both of those days, whatever timezone you read it in. start_half and end_half say whether the first and last day are full days or a FIRST_HALF / SECOND_HALF. Leave booked in hours has start_time, end_time and total_hours.

Reference: List leave requests.

Book leave

curl -X POST https://app.bookyourpto.com/api/v1/leaves \
  -H "Authorization: Bearer $BOOKYOURPTO_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 4a7d2c19-8e3f-4b60-a2d5-9c1e7f3b8a46" \
  -d '{
    "leave_type_id": "cmlvt0annual00000000000",
    "start_date": "2026-08-03",
    "end_date": "2026-08-07",
    "reason": "Family trip to Tofino"
  }'
  • Without user_id the leave is for the token's owner. Booking for someone else needs an owner who is an Administrator or Executive, or that person's Department Head.
  • Half days: set start_half or end_half to FIRST_HALF or SECOND_HALF, where the leave type allows half days. For leave in hours on a single day, send start_time and end_time as HH:MM; it is priced against the person's schedule.
  • Weekends, the person's public holidays and half days are taken out of the day count. Minimum notice is counted from today on the person's own calendar.
  • Overlapping requests, locked dates, insufficient balance and too little notice are refused with 422 VALIDATION_FAILED, and the message says why. Show it to the person rather than retrying.
  • The answer is 201 with the new request. It is PENDING when its type needs approval, and APPROVED straight away when the type needs none, when the person is an Executive, or when it is booked for them by an Administrator, an Executive or the Department Head of their department.
  • It raises leave.requested, whatever the status.

Get the leave_type_id from GET /leave-types?active=true, which also returns each type's rules: allow_half_days, allow_hourly, min_days_notice, max_days_per_request and requires_approval.

Reference: Request leave, List leave types.

Cancel leave

curl -X POST https://app.bookyourpto.com/api/v1/leaves/cmlve0hmarsh2026080300/cancel \
  -H "Authorization: Bearer $BOOKYOURPTO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Trip postponed to September." }'

What happens depends on who asks and on the leave's state:

OwnerPending leaveApproved leave
The person whose leave it isWithdrawn (CANCELLED), until it startsA cancellation request is filed: the leave stays APPROVED, cancellation_requested_at is set, and an approver decides
Their Department HeadCancelledCancelled
Administrator or ExecutiveCancelledCancelled

A direct cancellation raises leave.cancelled; a cancellation request raises leave.cancellation_requested.

Reference: Cancel a leave.

Approve or reject

curl -X POST https://app.bookyourpto.com/api/v1/leaves/cmlve0hmarsh2026080300/approve \
  -H "Authorization: Bearer $BOOKYOURPTO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "comment": "Enjoy the trip." }'

/approve and /reject (with an optional reason) work on two things:

  • a pending request: approving raises leave.approved, rejecting raises leave.rejected;
  • an approved leave with a cancellation request: approving the cancellation cancels the leave and raises leave.cancelled; rejecting it keeps the leave APPROVED.

Rules:

  • Needs leaves:approve, so the owner must be a Department Head or above.
  • Nobody decides their own leave, whatever their role: 403 FORBIDDEN.
  • A Department Head decides only within the departments they head or manage. Outside them the answer is 403 FORBIDDEN, checked before anything else, so the response does not reveal whether the request was already decided.
  • A leave in any other state, for example already approved with no cancellation request, answers 422 VALIDATION_FAILED.
  • The person is notified and their connected calendars are updated, as in the app.

To build an approval queue, list GET /leaves?status=PENDING with a Department Head's or Administrator's token. Subscribe to leave.requested and leave.cancellation_requested to hear about new items as they arrive.

Reference: Approve, Reject.

Read a balance

curl "https://app.bookyourpto.com/api/v1/leave-balances?user_id=cmusr0lenafischer000000&year=2026" \
  -H "Authorization: Bearer $BOOKYOURPTO_TOKEN"

The answer is one object with the figures the app's calendar shows for that fiscal year: an annual and a sick summary (allowance, used, remaining, plus base allowance and carry-over for annual), a leave_types list per deductible type, and non_deductible for types that do not draw from a balance. fiscal_period_start and fiscal_period_end give the year's boundaries.

  • Without user_id, the owner's own balance. Without year, the current fiscal year on the person's calendar.
  • Another person's balance follows the calendar rule above. Anyone the owner may not see answers 404 NOT_FOUND.
  • This endpoint is not paginated and returns one person per request. To export balances for everyone, walk GET /users and request each person's balance, keeping within the rate limits.

Reference: Get a leave balance.

Holidays and locked dates

# The organization's holidays for a year
curl "https://app.bookyourpto.com/api/v1/public-holidays?year=2026" \
  -H "Authorization: Bearer $BOOKYOURPTO_TOKEN"

# One person's holiday calendar (their holiday country and overrides)
curl "https://app.bookyourpto.com/api/v1/public-holidays?year=2026&user_id=cmusr0lenafischer000000" \
  -H "Authorization: Bearer $BOOKYOURPTO_TOKEN"
  • The whole year comes back in one page. Each holiday's date is a calendar day; source says where it came from: organization, personal_country (the person's own holiday country) or personal_override.
  • With user_id, you get that person's calendar when the owner may see it: the person themselves, Administrators, Executives and their Department Head. Anyone else gets the organization's holidays.
  • GET /locked-dates lists periods when leave cannot be booked, for everyone, for some departments or for some people (scope, department_ids, user_ids). Filter with start_date and end_date.

Reference: Holidays.

Gotchas

  • Do not convert calendar days through a timezone. 2026-07-01 is July 1 for everyone. Parsing it as a timestamp in a timezone west of UTC turns it into June 30. See Dates and times.
  • A 422 on booking is an answer, not a fault. It carries the reason the app would show: not enough balance, too little notice, an overlap or a locked date.
  • Leave booked for someone else is usually approved at once. Booking for another person as an Administrator, an Executive or their Department Head creates the leave already approved, as in the app.
  • Your own requests cannot be decided through the API, even by an Executive. Use a token owned by the approver.
  • Every page of a leave list is recorded as an export in the audit log. Use webhooks for change notifications rather than polling.