Skip to main content

Introduction

The v2 API will be shut down on March 31, 2026, and will no longer respond after this date.
This guide helps you migrate from the deprecated v2 leaves API endpoint to the v3. Why is the v2 deprecated? The v2 leaves API endpoint:
  • suffers from significant performance issues, that also affect other API endpoints;
  • relies on older technology, that may suffer from security vulnerabilities.
Whereas the v3 leaves API endpoint:
  • offers faster response times;
  • gives you additional features (pagination, sorting);
  • shares the same authentication system than v2.
Are you affected by this change? If there’s any HTTP request against the /api/leaves API endpoint in your integration, then you are affected by this and must migrate to /api/v3/leaves before 2026-03-31. Otherwise, you are free to skip this.

Changes

The V3 Leaves API endpoint is documented in the API Reference.

Routes

V2V3
/api/leaves/api/v3/leaves

Query parameters

api v2api v3Description
owner.idleavePeriod.ownerIdFilter leaves by the employee ID they belong to.
Note: other query parameters did not change.

Response

In V2, all responses are a JSON object with a data property which is:
  • a JSON object for a response containing a singular resource;
  • a JSON array for a response containing a collection of resources.
In V3, all responses are a JSON object with a data property which is always a JSON object. For collections, it contains an items property, which is a JSON aray which contains the representations of resources in the collection.
type: object
properties:
  data:
    oneOf:
      - title: Single Leave
        $ref: "#/leave"
      - title: Collection of Leaves
        type: array
        items:
          $ref: "#/leave"

Fields

Some of the fields were renamed, moved, removed and/or changed type.
V2V3 EquivalentDescription
id: stringid: stringID generation pattern changed-see below.
status: string<enum>status: string<enum>Represents the approval status of the leave. The enum was changed-see below.
owner.id: integerleavePeriod.ownerId: integerID of the employee this leave belongs to. The owner is now a property of the leavePeriod.
owner.name: stringleavePeriod.owner.name: stringName of the employee this leave belongs to.
owner.mail: string<email>N/ANo longer available for security reasons*.
owner.login: stringN/ANo longer available for security reasons*.
owner.matricule: stringN/ANo longer available for security reasons*.
name: stringleaveAccount.name: stringIn v2, the name was equal to the name of the leave account.
duration: integerduration: string<time>Duration of the leave, in minutes in v2 (e.g. 720), as a time in v3 (e.g. "12:00:00").
accountCode: stringleaveAccount.id: integerUnique identifier of the leave account.
accountLabel: stringleaveAccount.name: stringName of the leave account (e.g. “Vacations”, “Sickness” …)
N/AisActiveWhether the leave was deleted. A deleted leave used to completely disappear in v2, but it can still be retrieved in v3.
leaveScope: string = ‘AM’ | ‘PM’isAM: booleanWhether the leave belong to the morning or the afternoon.
* : Fields related to the leave owner are no longer available in v3 in order to prevent data leaks. You may retrieve them with a complementary request to /api/v3/users.
Note: other fields did not change.

About Leave ID

V2 ABNF Syntax for Leave ID
leave-id = owner-id "-" date-time "-" meridiem

; Owner identifier (adjust as needed)
owner-id = 1*(ALPHA / DIGIT / "_" / "-")

; Date and time: dd/MM/yyyy hh:mm:ss
date-time = day "/" month "/" year SP hour ":" minute ":" second

day    = 2DIGIT  ; 01–31 (range not enforced in ABNF)
month  = 2DIGIT  ; 01–12
year   = 4DIGIT

hour   = 2DIGIT  ; 01–12
minute = 2DIGIT  ; 00–59
second = 2DIGIT  ; 00–59

meridiem = "AM" / "PM"

SP = %x20

; Example: 416-05/09/2025 11:42:30-AM
V3 ABNF Syntax for Leave ID
leave-id = leave-period-id "-" date "-" meridiem

; Leave period identifier (adjust as needed)
leave-period-id = 1*(ALPHA / DIGIT / "_" / "-")

; Date in YYYYMMDD format
date  = year month day

year  = 4DIGIT
month = 2DIGIT  ; 01–12 (range not enforced)
day   = 2DIGIT  ; 01–31 (range not enforced)

meridiem = "AM" / "PM"

; Example: 416-20250905-AM

About Leave Status

V2 Leave Status Enum
enum EV2Status {
  // Leave request was submitted but still has not been touched by a manager.
  NonValidated = 0,
  // The request has been partially approved by management.
  SemiValidated = 1, 
  // The request was approved by all required managers.
  Validated = 2, 
  // The request was rejected by a manager.
  Rejected = 3, 
  // The request was cancelled by the requesting employee themselves.
  Cancelled = 4, 
}
V3 Leave Status Enum
enum EV3Status {
  // The leave request has been submitted but is not approved
  // (can still be partially approved).
  Temporary = 0,
  // The leave request is formally approved.
  Confirmed = 1,
}

Code Examples

JSON Examples

{
  "header": {
    "generated": "2025-12-09T14:20:38.7951403+01:00",
    "serverTime": 17716,
    "queryTime": 17642,
    "processId": 1336
  },
  "data": [
    {
      "id": "3464-11/07/2025 00:00:00-AM",
      "name": "Télétravail",
      "owner": {
        "id": 3464,
        "name": "Daniel HERNANDEZ"
      },
      "date": "2025-07-11T00:00:00",
      "duration": 720,
      "status": 1
    },
    {
      "id": "3464-11/07/2025 12:00:00-PM",
      "name": "RTT 2025",
      "owner": {
        "id": 3464,
        "name": "Daniel HERNANDEZ"
      },
      "date": "2025-07-11T12:00:00",
      "duration": 720,
      "status": 1
    }
  ]
}

Example Mapper

enum EV2Status {
  NonValidated = 0,
  SemiValidated = 1,
  Validated = 2,
  Rejected = 3,
  Cancelled = 4,
}

enum EV3Status {
  Temporary = 0,
  Confirmed = 1,
}

interface IV2Leave {
    id: string;
    date: string;
    owner: {
      id: number;
      name: string;
      mail: string | null;
      login: string | null;
      matricule: string | null;
    };
    name: string;
    status: EV2Status;
    duration: number;
    accountCode: string;
    accountLabel: string;
    leaveScope: 'am' | 'pm';
}

interface IV3Leave {
    id: string;
    date: string;
    leavePeriod: {
        owner: {
            id: number;
            name: string;
        },
    };
    status: EV3Status;
    isActive: boolean;
    leaveAccount: {
        id: number;
        name: string;
    };
    duration: string;
    isAm: boolean;
}

function minutesSinceMidnight(timeStr: string) {
  const [hours, minutes, seconds = 0] = timeStr.split(':').map(Number);

  return hours * 60 + minutes + seconds / 60;
}


function v3ToV2Leave(v3leave: IV3Leave): IV2Leave {
  function v3ToV2Status(v3leave: IV3Leave): EV2Status {
    if (!v3leave.isActive) return EV2Status.Rejected
    if (v3leave.status === EV3Status.Temporary) {
      return EV2Status.NonValidated;
    } else {
      return EV2Status.Validated;
    }
  }
  function v3ToV2Id(v3leave: IV3Leave): string {
    const match = v3leave.id.match(/^(.+)-(\d{4})(\d{2})(\d{2})-(AM|PM)$/);

    if (!match) {
      throw new Error("Invalid leave ID format");
    }

    const [, leavePeriodId, year, month, day, meridiem] = match;

    const time = meridiem === "AM" ? "00:00:00" : "12:00:00";

    return `${v3leave.leavePeriod.ownerId}-${day}/${month}/${year} ${time}-${meridiem}`;
  }

  return {
    id: v3ToV2Id(v3leave),
    date: v3leave.date,
    owner: {
      id: v3leave.leavePeriod.owner.id,
      name: v3leave.leavePeriod.owner.name,
      mail: null, // No longer available for security reasons, you may fetch the user with GET /api/v3/users/{id}?fields=mail.
      login: null, // No longer available for security reasons, you may fetch the user with GET /api/v3/users/{id}?fields=login.
      matricule: null, // No longer available for security reasons, you may fetch the user with GET /api/v3/users/{id}?fields=employeeNumber.
    },
    name: v3leave.leaveAccount.name,
    status: v3ToV2Status(v3leave),
    duration: minutesSinceMidnight(v3leave.duration),
    accountCode: v3leave.leaveAccount.id.toString(),
    accountLabel: v3leave.leaveAccount.name,
    leaveScope: v3leave.isAm ? 'am' : 'pm',
  }
}