Delete a leave
This API method deletes a leave of a user the acting user manages (approvers, location admins and admins only). Requires `leaves:write`. No request body. The acting user is the key owner, or the `X-On-Behalf-Of` user. It waits for the deletion and returns the deleted leave with `200`, or `202 Accepted` with an operation to poll. Errors: `403 ON_BEHALF_OF_NOT_ALLOWED`, `403 FORBIDDEN` (the acting user does not manage the user), `404 RESOURCE_NOT_FOUND` (the leave does not exist or is outside the key's data reach), and the `Idempotency-Key` codes.
Authorization
bearerAuth leaves:writeAPI key sent as Authorization: Bearer vt_live_<secret>. See the Authentication and Scopes sections at the top of this document for how keys are created, which scopes exist and how the key owner's role limits them.
In: header
Scope: leaves:write
Path Parameters
resource ID.
Header Parameters
optional key (1 to 255 printable ASCII characters) that makes a write request safe to retry. Keys are retained for 24 hours per API key. Retrying with the same key and the same request within those 24 hours replays the original response (with the Idempotent-Replayed header). Reusing the key with a different request returns 422 IDEMPOTENCY_KEY_REUSED; retrying while the first request is still running returns 409 IDEMPOTENCY_REQUEST_IN_PROGRESS; an invalid key returns 400 INVALID_IDEMPOTENCY_KEY.
1 <= length <= 255ID of the user the write is performed for. Requires the users:act-on-behalf scope, and the key owner must manage the target user; otherwise the request fails with 403 ON_BEHALF_OF_NOT_ALLOWED. Ignored on GET requests.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X DELETE "https://example.com/v2/leaves/string"{
"data": {
"id": "leaveRequest-cfe5d39b-f1f0-45aa-85cd-ce93e637a262",
"userId": "user-63b17e97-d38b-4586-8498-bb4f5d2e2c14",
"approverId": "user-0f3a9c1e-6c2b-4a5d-9e7f-1b2c3d4e5f60",
"autoApproved": false,
"durationCalendarDays": 5,
"durationWorkingDays": 5,
"startDate": "2026-11-09",
"endDate": "2026-11-13",
"isFullDayLeave": true,
"leaveTypeId": "leaveType-fcc1e5c5-ba93-479d-bd58-8f860ba10615",
"locationId": "location-79c77ac8-e5bf-479f-9488-648a58379ff1",
"departmentId": "team-0a21c25e-6925-4a5b-90ee-ecf82096c4f6",
"status": "APPROVED",
"reason": "Family trip",
"statusReason": null,
"approvedLevel": null,
"createdAt": "2026-10-01T09:12:40.062Z"
}
}{
"operation": {
"id": "pa_3f1c2b9e-8a47-4d5e-9c61-2b7f0e4a1d93",
"status": "PENDING",
"url": "/v2/operations/pa_3f1c2b9e-8a47-4d5e-9c61-2b7f0e4a1d93"
}
}{
"error": {
"code": "VALIDATION_ERROR",
"name": "ValidationError",
"description": "A query parameter, body field or header failed validation.",
"resolution": "Fix the fields listed in details.fields and send the request again.",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
"details": {
"fields": [
{
"path": "startDate",
"message": "\"startDate\" is required"
}
]
}
}
}{
"error": {
"code": "UNAUTHORIZED",
"name": "Unauthorized",
"description": "The API key is missing, malformed or not recognized.",
"resolution": "Send a valid API key in the Authorization header as Bearer followed by the full vt_live_ key.",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef"
}
}{
"error": {
"code": "INSUFFICIENT_SCOPE",
"name": "InsufficientScope",
"description": "The API key does not have the scopes this request needs.",
"resolution": "Add the scopes listed in details.requiredScopes to the key in the Vacation Tracker dashboard.",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
"details": {
"requiredScopes": [
"leaves:write"
]
}
}
}{
"error": {
"code": "RESOURCE_NOT_FOUND",
"name": "ResourceNotFound",
"description": "The requested resource does not exist or is outside the data this key can access.",
"resolution": "Check the ID of the resource named in details.resource and that the key owner can see it.",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef"
}
}{
"error": {
"code": "CONFLICT",
"name": "Conflict",
"description": "The request conflicts with the current state of the resource.",
"resolution": "Read the resource again and retry with up-to-date values.",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef"
}
}{
"error": {
"code": "LEAVE_NEGATIVE_BALANCE",
"name": "LeaveNegativeBalance",
"description": "The user does not have enough balance for this leave.",
"resolution": "Shorten the leave, use a different leave type, or ask an admin to adjust the quota.",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef"
}
}{
"error": {
"code": "RATE_LIMITED",
"name": "RateLimited",
"description": "Too many requests were sent in a short time.",
"resolution": "Wait a few seconds and retry, using the same Idempotency-Key for writes.",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"name": "InternalError",
"description": "Something went wrong on our side.",
"resolution": "Retry later, and if it keeps happening, contact Vacation Tracker support and quote the requestId.",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef"
}
}Need help?
developers@vacationtracker.ioUpdate a leave
This API method changes an `OPEN` or `APPROVED` leave. Requires `leaves:write`. It waits for the change and returns the leave with `200`, or `202 Accepted` with an operation to poll. Send only the fields you want to change; at least one field is required. The change is merged with the leave's current state: fields you omit keep their current value, including the substitute approver. - The acting user (the key owner, or the `X-On-Behalf-Of` user) can edit their own leaves, or the leaves of users they manage. When editing their own leave, every rule of the leave policy applies, as in the Vacation Tracker dashboard; when editing someone else's, only overlapping leaves, dates without working days and part-day leaves longer than a working day block the change. - `isFullDayLeave: true` turns the leave into a full-day leave; the time fields must then be omitted. `isFullDayLeave: false`, or any time field sent for a full-day leave, turns it into a part-day leave, and all four time fields (sent or stored) are then required. - A change that breaks an automation fails with `409 AUTOMATION_OVERRIDE_REQUIRED` (`details.warnings`). Overrides are not accepted when updating a leave. Errors: `400 VALIDATION_ERROR` (`details.fields`), `403 ON_BEHALF_OF_NOT_ALLOWED`, `403 FORBIDDEN`, `404 RESOURCE_NOT_FOUND` (the leave does not exist or is outside the key's data reach), `409 AUTOMATION_OVERRIDE_REQUIRED`, `422 LEAVE_REQUEST_NOT_EDITABLE`, `422 LEAVE_OVERLAP`, `422 LEAVE_NO_WORKING_DAYS`, `422 LEAVE_INVALID_DATE_RANGE`, `422 LEAVE_INVALID_DURATION`, `422 LEAVE_INVALID_HALF_DAY_DURATION`, `422 LEAVE_START_DATE_IN_PAST`, `422 LEAVE_NEGATIVE_BALANCE`, `422 LEAVE_REASON_REQUIRED`, `422 LEAVE_TYPE_INACTIVE`, `422 LEAVE_POLICY_MISSING`, `422 USER_NOT_EMPLOYED_ON_DATES`, and the `Idempotency-Key` codes.
Approve a leave
This API method approves an `OPEN` leave. Requires `leaves:approve`. The acting user (the key owner, or the `X-On-Behalf-Of` user) must be an approver (or substitute approver) of the user's department, a location admin, or an admin. The request body is optional; `reason` is stored as the leave's `statusReason`. It waits for the approval and returns the leave with `200`, or `202 Accepted` with an operation to poll. In a department with multi-level approval, the first-level approval returns the leave still `OPEN` with `approvedLevel` set to `FIRST`; that is the final result of this call, and a second-level approver must approve it again. Errors: `400 VALIDATION_ERROR` (`details.fields`), `403 ON_BEHALF_OF_NOT_ALLOWED`, `403 FORBIDDEN` (the acting user cannot approve this user's leaves), `404 RESOURCE_NOT_FOUND` (the leave does not exist or is outside the key's data reach), `422 LEAVE_REQUEST_NOT_OPEN` (the leave is no longer `OPEN`), and the `Idempotency-Key` codes.