Akuiteo provides many APIs for managing the different aspects of your company: workforce, CRM, sales, purchase, accounting and more. Our APIs enable you to use the many features of the Akuiteo softwares outside of the application or service. Therefore, you must understand how Akuiteo works to be able to use our APIs.

Note
All our APIs are in the version 3.0. This version is the only one available and is accessed by default.

1. What is a REST API ?

API stands for Application Programming Interface. It is a set of rules that enables programs to talk to each other, in order to manage data and access features across the Internet in a consistent format. An API is a technology that links two systems together.

REST stands for Representational State Transfer. REST APIs are a type of web APIs that are based on requests and responses:

You make a request to a resource on a server.

  • The server responds with the requested information.

  • The request consists of an HTTP verb which translates an action (create, modify, search…), a specific URL to access the resource and a set of parameters.

Responses use the generic JSON content type.

2. What is a resource?

Each request is made with specific parameters and the API is accessed at a predefined set of URLs. These URLs represent various resources, that is to say any information or content accessed at that location.

In the context of Akuiteo, a resource represents a management object or an item: an employee, a sales invoice, a project, a project task, and so on.

Following the Domain-Driven-Development approach, our APIs are categorized by domains that represent the different modules in Akuiteo.

The following table lists all our API domains:

Accounting

The Accounting domain enables you to manage chart of accounts and fiscal years.

Administration

The Administration domain contains APIs used for server administration, comparable to the features available in the Administration Console.

Approval process

The Approval process domain enables you to manage the different types of approvals available: quotes and quotations, sales and purchase orders, purchase invoices, expense reports, and others.

Customer Relationship

The Customer Relationship domain enables you to manage events, opportunities, customers and prospects.

Document Management

The Document Management domain enables you to manage documents, publishing templates and document links.

Legacy

The Legacy domain regroups all our legacy APIs, which are SOAP web services.

Messaging

The Messaging domain enables you to manage subscriptions and webhooks.

Mobile

The Mobile domain gives you access to specific APIs for the Akuiteo Mobile Application that works off line. These are synchronization APIs that enable you to retrieve details about expense reports, leave requests, timesheets and so on, and also to send back the data to be saved.

Project management

The Project management domain enables you to manage projects, phases, sub-phases and project tasks.

Purchase

The Purchase domain enables you to manage quotes, orders, invoices, receptions and suppliers.

Sales

The Sales domain enables you to manage quotations, orders, invoices, billing tables and time-based billing.

Security

The Security domain enables you to manage everything regarding security, users and rights (DMFs).

Service integration

The Service integration domain enables you to send emails and messages.

Settings

The Settings domain gives you access to many setup options: triggers, translations, web portals, preferences, currencies and so on.

Support

The Support domain enables you to manage helpdesk issues and tasks, contacts, contracts, tags, products, sprints, timesheets and provides many search APIs.

Workforce

The Workforce domain enables you to manage employees, timesheets and schedules, expense reports, leave requests, custom requests and project events.

3. Technical users and user rights (DMF)

Note
We recommend that you use technical users to make calls to our APIs.

A user must be created and set up from the Application Desktop to be able to connect to and use the different applications: the Application Desktop, the web portals, the mobile application. Technical users can also be created from the Desktop specifically to access APIs. A technical user will not be able to use its login and password to access the Akuiteo applications.

Whether it is a standard or a technical user, setting up the user from the Desktop will enable you to authenticate yourself, that is to say to prove your correct identity. The authenticated user will appear in the history of a management object for creation and modification actions. For more information, refer to Authentication.

However, you must also manage user rights in order to manage authorizations, that is to say the actions allowed for that user. An API might authenticate you but not authorize you to make a certain request.

User rights are assigned to each user. In Akuiteo, these rights are called DMFs (for Domain-Module-Function) and enable users to access specific modules or functions of our applications. There are two types of DMFs:

  • Functional DMFs that apply to modules and features in the applications.

  • API DMFs that grant permissions to call APIs. These DMFs can only be assigned to technical users.

4. Authentication

Three types of authentication are available for our APIs:

Warning

If SAML is active on your environment, a non SAML authenticated, technical user must be used. SAML authenticated users will receive 401 error reponses to any API request.

4.1. Basic authentication

For the basic authentication, you send your username and password alongside every API call. This login/password must be the login information of an Akuiteo user (technical or standard).

An Authorization header must be added alongside any request:

💡
Using curl
--header 'Authorization: Basic dXNlcjpwYXNzd29yZA=='

The string Basic indicates you are using the basic authentication.

The string dXNlcjpwYXNzd29yZA== is the login/password encoded in base64.

Note

When using the basic authentication, you must use /rest as the security selector in the Resource path URL.

4.2. OAuth authentication

For the OAuth authentication, you must first request an access token in order to authenticate yourself. Then you can start to perform API calls, but only for a limited period of time before it needs to refresh that token. With this type of authentication, you do not have to share login information on each API calls.

To be able to use the OAuth authentication:

  1. From Akuiteo’s Administration Console, go to Security > Oauth2 and create a New client with the following information:

Field Description

Client ID

Client ID used for the Oauth authentication.

Duration (s)

The duration indicates the number of seconds that the access token will be valid.

Reload duration (s)

A refresh token makes it possible to request a new access token without having to enter the login information again.

The reload duration indicates the number of seconds that the refresh token can be used to request a new access token.

Scope

Fill in read_write. Since authorizations are based on DMFs, you can give access to the read_write scope to read, modify and delete resources.

⇒ Once created, Akuiteo provides you with a Client Secret.

  1. To retrieve the access token, make an API call using the request: POST /oauth/token with application/x-www-form-urlencoded as Content-Type.

curl --location --request POST 'https://akuiteo.mon-server.fr/akuiteo/oauth/token' \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data-urlencode 'grant_type=password' \
    --data-urlencode 'client_id={your_client_id}' \
    --data-urlencode 'client_secret={your_secret}' \
    --data-urlencode 'scope=read_write' \
    --data-urlencode 'username={your_username}' \
    --data-urlencode 'password={your_password}'
Note
When needed, use the refresh token to get a new access token.

Once you get the access token, an Authorization header must be added alongside any request:

💡
Using curl
--header 'Authorization: Bearer {access_token}'

The string Bearer indicates you are using the OAuth authentication.

The string {access_token} must be replaced with the access token retrieved.

Note

When using the OAuth authentication, you must use /RS as the security selector in the Resource path URL.

4.3. JWT authentication

JWT authentication requires first obtaining a JWT token. Once authenticated, you can use this token to perform subsequent API calls without sending your login credentials on each request.

4.3.1. Get the JWT Token

To retrieve the token, make a GET request to /jwt/login.html, providing your credentials in the URL parameters:

  • username = your login, URL-encoded

  • password = your password, Base64-encoded, then URL-encoded

Both parameters must be properly URL-encoded to ensure the request works reliably.

curl --location --request GET \
  'https://akuiteo.mon-server.fr/akuiteo/jwt/login.html?username={url_encoded_username}&password={url_encoded_base64_password}'
Example

If your credentials are:

  • Username: sebastien.maillard@akuiteo.com

  • Password: HWVYSS4DJKIHRWGT

Then:

  • URL-encoded username: sebastien.maillard%40akuiteo.com

  • Base64-encoded password: SFdWWVNTNERKSktIUldHVA==

  • URL-encoded Base64 password: SFdWWVNTNERKSktIUldHVA%3D%3D

Final curl command:
curl --location \
  'https://akuiteo-50.akuiteo.fr/akuiteo/jwt/login.html?username=sebastien.maillard%40akuiteo.com&password=SFdWWVNTNERKSktIUldHVA%3D%3D'

4.3.2. Use the JWT Token in Subsequent API Calls

Once you receive the JWT token, include it in the Authorization header of each API request:

--header 'Authorization: Bearer {jwt_token}'
Note

This authentication method avoids sending credentials repeatedly. When using JWT, all API requests must be made under the /rest security selector path in the Resource path URL.

5. Data formats

Our APIs follow to the same principles as typical REST services. Each operation is performed via an HTTPS request to a specific URL, which may include parameters or a request body.

When a request body is required, it must be in JSON format, which is also the format used for all API responses. The Content-Type header should be set to "application/json".

The parameters and/or request body required will depend on the specific API endpoint you are calling. It is important to consult the API documentation to understand what is needed for each API.

Parameters
Note
If there is a link in the Schema column for a parameter or a body, click on the link to know more about the type of data required.

When making a request, the data provided as parameters or in the request body should comply with standard REST conventions:

  • Objects are enclosed in curly braces { }.

  • Object properties are named in camelCase.

  • Collections are enclosed in square brackets [ ].

The data types in the schema can include:

5.1. Date Format

Our APIs use standardized date and time formats to ensure consistent handling of time zones and seasonal changes, such as daylight saving time. Below are the different formats used in the APIs.

5.1.1. Date-Time Format (date-time):

The date-time format follows the RFC 3339 standard (yyyy-MM-dd’T’HH:mm:ss.SSSXXX) and uses Coordinated Universal Time (UTC).

  • 2024-08-08T00:00:00Z means midnight on August 8, 2024, UTC.

This format also includes a time offset (+hh:mm) to represent the difference between local time and UTC.

Examples:

  • 2024-08-08T00:00:00.000+0000 represents midnight on August 8, 2024, in UTC, with no offset.

  • 2024-08-08T00:00:00.000+0200 represents the same moment (midnight UTC), but converted to Central European Summer Time (UTC+2).

5.1.2. Date Only Format (date):

This format is used when only the date without time is needed. It is represented as yyyy-MM-dd (RFC 3339) and does not include any time offset, as no time is involved.

Examples:

  • 2024-08-08: August 8, 2024.

  • 2023-12-25: December 25, 2023.

In this case, the API simply returns the date without any UTC conversion, as time is not relevant.

5.1.3. Year-Month Format (date-month):

This format includes only the year and month, without day or time. It is expressed as yyyy-MM (RFC 3339).

Examples:

  • 2024-08: August 2024.

  • 2023-12: December 2023.

Use of date format in API requests

When consulting the API documentation, the Format column indicates whether you should use a date-time format (including date and time) or a date format (date only). This helps you determine the required format for each API field.

1. Sending a request : How to set the time zone and daylight saving time?

If you want to display a specific date and time in your software according to your local time zone, you can either use the UTC time directly in the API or manually adjust the time based on your time zone offset.

Here are two possible methods for formatting the date and sending it to the API:

Method 1: Use UTC time directly

You can retrieve and send the exact UTC time for the date without adding any offset, as the API defaults to UTC. This time will then be automatically converted according to the user’s time zone.

  • If you send to the API: 2024-08-08T00:00:00.000+0000 which represents August 8, 2024, at 00:00 UTC, your software will then display this time based on the user’s time zone.

    • In France during summer (UTC+2), this will automatically be interpreted as 2:00 AM on August 8, 2024.

    • In France during winter (UTC+1), this will automatically be interpreted as 1:00 AM on August 8, 2024.

  • If you send to the API: 2024-08-07T22:00:00.000+0000 which represents August 7, 2024, at 22:00 UTC, your software will then display this time based on the user’s time zone.

    • In France during summer (UTC+2), this will be automatically interpreted as midnight (00:00) on August 8, 2024.

    • In France during winter (UTC+1), this will be interpreted as 11:00 PM on August 7, 2024.

Method 2: Adjust based on the local time zone

If you want the time to be midnight local time in France, you need to account for the offset with UTC.

  • In summer (UTC+2): To display midnight on August 8, 2024, in France, you have two options for sending the time to the API:

    • Subtract 2 hours: Send the following value to the API: 2024-08-07T22:00:00.000+0000. This corresponds to August 7, at 22:00 UTC, which will automatically be converted to midnight (00:00) on August 8, in France.

    • With UTC+2 offset: You can also send the following value directly to the API, with a +02:00 offset for daylight saving time in France: 2024-08-08T00:00:00.000+0200.

  • In winter (UTC+1): To display midnight on December 8, 2024, in France, you have two options for sending the time to the API:

    • Subtract 1 hour: Send the following value to the API: 2024-12-07T23:00:00.000+0000. This corresponds to December 7, at 23:00 UTC, which will automatically be converted to midnight (00:00) on December 8, in France.

    • With UTC+1 offset: You can also send the following value directly to the API, with a +01:00 offset for winter time in France: 2024-12-08T00:00:00.000+0100.

2. Receiving API Responses

When you make a API request, if the field format is date-time, the response will always be in date-time format expressed in UTC, regardless of the time zone of the user making the request.

Examples:

  • In summer: If you request a resource that was created on August 8, 2024, at 2:00 PM local time in France (summer, UTC+2), the API will return: 2024-08-08T12:00:00.000+0000. This response corresponds to 12:00 PM UTC, as the API always returns the date and time in UTC.

  • In winter: If you request a resource that was created on December 8, 2024, at 2:00 PM local time in France (winter, UTC+1), the API will return 2024-12-08T13:00:00.000+0000. This response corresponds to 1:00 PM UTC, as the API always returns the date and time in UTC.

5.2. Number Format

Numbers are used to represent numeric values. Here are the different types of numbers supported by the API:

  • Number: Represents any numerical value. It is important not to use separators for thousands, millions, etc. Example: 1000, not 1,000 or 1.000.

  • Number (double): Represents floating-point numbers with high precision. The period (ASCII 0x2E) must be used as the separator for decimal values. Example: 10.8, not 10,8.

  • Number (float): Also represents floating-point numbers but with less precision than the double type. The period (ASCII 0x2E) is also used as the separator for decimal values. Example: 10.8.

5.3. Integer Format

Integers are numbers without decimal points.

  • Integer (int64): Represents signed 64-bit integers (long type). Example: 10.

  • Integer (int32): Represents signed 32-bit integers (standard type).

5.4. Boolean Format

  • Boolean: Represents two possible values: true and false. Only these two values are allowed in our APIs.

    • Values such as 0, 1, null, "true", or "" (empty string) are not considered boolean values.

6. Request

The Akuiteo APIs follow the same principles as any REST APIs. Each operation is done via an HTTPS call to a specific URL, potentially accompanied with some parameters or with a request body.

Request bodies and responses use the generic JSON content type.

Note

API calls are made on resources from the default company of the user making the request. If you want to change the company targeted with your API calls, add the following line to the header alongside any request:

--header 'X-Company-Code: {company_code}'

6.1. HTTP verbs

The HTTP verbs are at the core of the APIs. To make a request, you need an HTTP verb which represents an action (create, modify, search…) made on one or multiple resources.

The method used depends on the API you want to call. You must read the API documentation to identify the resource path required for each API:

Method

The following standard methods are used in our APIs:

GET

Retrieves the targeted resource or all the items in a collection. The response does not display any sub-objects, it only displays ids.

Use this method to show the details of the targeted resource.

💡
Example

If you want to retrieve the details of an employee, the method will be:

GET /employees/{employee_id}

POST

Updates the targeted resource or all the items in a collection. This method is also used for functional operations and for Akuiteo-specific APIs.

💡
Example

If you want to modify an event (task, appointment or note), the method will be:

POST /events/{event_id}

PUT

Creates a resource.

💡
Example

If you want to create a new quotation, the method will be:

PUT /quotations

DELETE

Deletes the targeted resource or all the items in a collection.

💡
Example

If you want to delete an issue, the method will be:

DELETE /issues/{issue_id}

PATCH

Updates specific parameters in the targeted resource and not the whole resource. This method follows the RFC 6902.

💡
Example

If you want to update specific parameters in an expense report, the method will be:

PATCH /expense-reports/{expense_report_id}
Note

The AK_APPEND operation appends the new value to the value at the target location. The target location MUST exist for the operation to be successful.

For example: { "op": "AK_APPEND", "path": "/a/b/c", "value": "bar" }

If the original value at the target location was "foo", its new value will now be "foo bar".

This operation is specific to Akuiteo and is not included in RFC 6902.

Actions that do not correspond to standard HTTP methods and are specific to Akuiteo are collected under a sub-resource. For more information, refer to Akuiteo’s API specifics.

6.2. Resource path URL

The path URL to access the resource and make a request using our APIs follows the structure:

URLSchemaAPI

Let us dissect this URL:

Protocol

Always use the HTTPS protocol when making requests to our APIs.

Server URL

Address of your Akuiteo server, for example akuiteo.com or CustomerA-akuiteo.com.

Akuiteo Application Path

Name of your Akuiteo application, for example akuiteo or akuiteo-test.

Security selector

If you are using the basic or JWT authentication, use /rest as the security selector.

If you are using the OAuth authentication, use /RS as the security selector.

Domain Path

Name of the domain targeted by the request. Our APIs are categorized by domains that represent the different modules in Akuiteo.

Refer to the API documentation to know more about each domain.

💡

In the API documentation, the chapter 1.4 URI scheme of each domain displays the BasePath for each domain.

URIscheme

In this example:

  • /akuiteo is the URL Base

  • /rest is the Security selector

  • /crm is the Domain URL Base

Resource path

The resource path depends on the API you want to call. You must read the API documentation to identify the resource path required for each API:

URL

Depending on the API, there can be a number of elements in the resource path:

  • Collection Resource Path - If a resource is in the plural form, it means it is a resource collection. Resources can be grouped into collections and a collection contains only one type of resource. In the example URL, the resource collection is issues. This means the request will target all issues in the database

  • Path Param - If the API requires you to specify the ID of the resource or collection.

  • Resource path - If the API targets a single resource.

  • Sub-resource - This can either be a resource with a hierarchical relationship with the resource (projects > phases) or a verb (search, read, bill) used for Akuiteo-specific APIs. For more information, refer to Akuiteo’s API specifics.

  • Query Param - Query parameters are a defined set of parameters attached to the end of a URL. They are optional extensions of the URL that are used to help define specific content or actions based on the data being passed. For example in some APIs you can add query parameters to perform the call on resources between a specific time slot (from…​ to…​). To add query parameters, add a ‘?’ at the end of the URL, followed immediately by a query parameter and separate each query parameter with a '&'.

6.3. Response status codes

Akuiteo uses conventional HTTP response codes to indicate the success or failure of an API request.

  • If the status code starts with 2xx, it means the request has succeeded.

  • If the status code starts with 4xx, it means the request has failed because of a client error (data format, unknown resource, etc.).

  • If the status code starts with 5xx, it means the request has failed because of a server problem.

The following response status codes are used for our APIs:

Code

Type

Description

200

OK

Standard success code. The request has succeeded.

201

Created

The resource has been created (usually for PUT or POST requests).

204

No Content

The request has succeeded but there is no content to retrieve.

400

Bad Request

Standard failure code. The server cannot or will not process the request due to an unexpected error.

401

Unauthorized

The user does not have valid authentication credentials for the target resource.

403

Forbidden

The user does not have permissions to perform the request.

404

Not Found

The requested resource does not exist.

405

Method Not Allowed

The HTTP method requested on the resource is not supported, for example if you make a POST request on a resource where POST does not make sense or is not implemented.

406

Not Accepted

The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request.

500

Internal Error

Server errors. The call was successful but something went wrong on Akuiteo’s end.

6.4. Akuiteo’s API specifics

API_SpecificAPIs

Some features available in the Akuiteo applications could not be translated in REST APIs using the standard HTTP methods.

For example, you might have to make an action on a resource that will change its state without changing the resource in itself. In the context of a helpdesk issue, when you assign a task to yourself, you change the state of the task without changing the task in itself.

This is why Akuiteo provides specific APIs whenever the standard methods do not apply. These APIs:

  • use the POST HTTP verb,

  • can be identified because they mostly use a verb (search, read, bill, take, and so on) as a sub-resource in their resource path.

Described below are the main Akuiteo-specific APIs that you will encounter.

6.4.1. /search APIs

The /search APIs allow you to retrieve resources from a collection by specifying a set of searchcriteria in the request body. These APIs are designed to provide precise and flexible filtering options to target specific resources, such as employees, projects, or tasks.

Each search criterion is applied in combination with theothers, and the conditions are logically connected using an “AND” relationship. This means that only resources matching all specified criteria will be includedin the results.

For each field, you can define one and only one search criterion. It is not possible to specify multiple criteria for the same field simultaneously, such as searching for a status that is both "active" and in a list of values like ["active", "closed"].

However, if you need to define multiple conditions for the same field, you can use the CLAUSES operator. This operator allows you to combine subconditions for a single field using logical operators like AND or OR. For example, you could search for resources where the status is either "active" or belongs to a specific list of values.

By supporting these combinations and constraints, the /search APIs ensure both flexibility and clarity in how search queries are constructed. The following sections provide detailed explanations of the supported operators, examples of common use cases,and guidance on how to create complex search queries when needed.

💡
By default operations on dates only apply to the date component. It is possible to take the time component into account by adding the attribute "withTime": true.

The different search operations available are:

LIKE

Search for resources with a parameter value that matches the searched value.

You want the list of custom requests with an ID that starts with 12. The request body will be:

{
    "id": {
        "operator" : "LIKE",
        "value": "12*"
    }
}
💡
Use the asterisk character * as a joker which can replace any character sequence.

Supported wildcards can be set via the wildcards attribute ("*%_" by default). In the next example, the only supported joker is the asterisk, the percent character is searched as is, and will not be interpreted as a wildcard :

{
    "id": {
        "operator" : "LIKE",
        "value": "12%*",
        "wildcards": "*"
    }
}

NOT_LIKE

Search for resources with a parameter value that doesn’t match the searched value.

You want the list of custom requests with an ID that doesn’t start with 12. The request body will be:

{
    "id": {
        "operator" : "NOT_LIKE",
        "value": "12*"
    }
}
💡
Use the asterisk character * as a joker which can replace any character sequence.

Supported wildcards can be set via the wildcards attribute ("*%_" by default). In the next example, the only supported joker is the asterisk, the percent character is searched as is, and will not be interpreted as a wildcard :

{
    "id": {
        "operator" : "NOT_LIKE",
        "value": "12%*",
        "wildcards": "*"
    }
}

IS

Search for resources with a parameter value that is the same as the searched value.

You want the list of the custom requests from the employee with an ID AAA. The request body will be:

{
    "employeeId": {
        "operator" : "IS",
        "value": "AAA"
    }
}

IS_NOT

Search for resources with a parameter value that is not the same as the searched value.

You want the list of all custom requests except the ones with the project ID 12345. The request body will be:

{
    "projectId": {
        "operator" : "IS_NOT",
        "value": "12345"
    }
}

IS_NULL

Search for resources with a parameter value that is null.

You want the list of custom requests that are not linked to a project (projectId null). The request body will be:

{
    "projectId": {
        "operator" : "IS_NULL",
    }
}

IS_NOT_NULL

Search for resources with a parameter value that is null.

You want the list of custom requests that are not linked to a project (projectId null). The request body will be:

{
    "employeeId": {
        "operator" : "IS_NOT_NULL",
    }
}

IN

Search for resources with a parameter value that matches one of the values specified in the searched value.

You want the list of custom requests from the employees with the ID AAA, BBB and CCC. The request body will be:

{
    "employeeId": {
        "operator" : "IN",
        "value": [
            "AAA",
            "BBB",
            "CCC"
        ]
    }
}

NOT_IN

Search for resources with a parameter value that matches none of the values specified in the searched value.

You want the list of custom requests from the employees without the ID AAA, BBB or CCC. The request body will be:

{
    "employeeId": {
        "operator" : "NOT_IN",
        "value": [
            "AAA",
            "BBB",
            "CCC"
        ]
    }
}

GREATER_OR_EQUALS

Search for resources with a parameter value that is greater than or equal to the value specified in the searched value.

You want the list of custom requests with an ID that is greater than or equal to 12345. The request body will be:

{
    "id": {
        "operator" : "GREATER",
        "value": "12345"
    }
}

GREATER

Search for resources with a parameter value that is strictly greater than the value specified in the searched value.

You want the list of custom requests with an ID that is greater than 12345. The request body will be:

{
    "id": {
        "operator" : "GREATER",
        "value": "12345"
    }
}

LOWER_OR_EQUALS

Search for resources with a parameter value that is lower than or equal to the value specified in the searched value.

You want the list of custom requests with an ID that is lower than or equal to 12345. The request body will be:

{
    "id": {
        "operator" : "LOWER_OR_EQUALS",
        "value": "12345"
    }
}

LOWER

Search for resources with a parameter value that is strictly lower than or equal to the value specified in the searched value.

You want the list of custom requests with an ID that is lower than 12345. The request body will be:

{
    "id": {
        "operator" : "LOWER",
        "value": "12345"
    }
}

CLAUSES

Search for resources with a combination of clauses on the same property. The most common example is searching for elements between two values.

For example, suppose you want the list of custom requests with a date between January 1st, 2020, and December 31st, 2020. The request body would be:

{
    "date": {
        "operator" : "CLAUSES",
        "value": [
            { "operator": "GREATER_OR_EQUALS", "value": "2020-01-01" },
            { "operator": "LOWER_OR_EQUALS", "value": "2020-12-31" },
        ]
    }
}
💡
Implicit logicalOperator is AND by default.

Suppose you want the list of custom requests with a date that is not between August 1st, 2020, and August 31st, 2020. The request body would be:

{
    "date": {
        "operator" : "CLAUSES",
        "logicalOperator": "OR",
        "value": [
            { "operator": "LOWER_OR_EQUALS", "value": "2020-08-01" },
            { "operator": "GREATER_OR_EQUALS", "value": "2020-08-31" },
        ]
    }
}

You can nest clauses. For example, if you want all amounts between 100 and 200 or between 400 and 500:

{
    "amount": {
        "operator" : "CLAUSES",
        "logicalOperator": "OR",
        "value": [
            {
                "operator" : "CLAUSES",
                "logicalOperator": "AND",
                "value": [
                    { "operator": "GREATER", "value": "100" },
                    { "operator": "LOWER", "value": "200" },
                ]
            },
            {
                "operator" : "CLAUSES",
                "logicalOperator": "AND",
                "value": [
                    { "operator": "GREATER", "value": "400" },
                    { "operator": "LOWER", "value": "500" },
                ]
            },
        ]
    }
}
💡
For all operators (except IS_NULL, IS_NOT_NULL and CLAUSES), you can include null results by setting includeNullResults to true.
{
    "id": {
        "operator" : "IS",
        "value": "AAA",
        "includeNullResults": true
    }
}
{
    "id": {
        "operator" : "IN",
        "value": [
            "AAA",
            "BBB",
            "CCC"
        ],
        "includeNullResults": true
    }
}

6.4.2. /read APIs

Our /read APIs can be used to retrieve all the details of a resource. In one API call, you can get all the information about the targeted resource, including dependencies. Our /read APIs enable you to retrieve the information that is not displayed when using the standard GET method.

ReadOptions are used in the request body to specify the dependencies that you want to display in the API’s response. The ReadOptions available depend on each resource. You must read the API documentation to know more about the ReadOptions available.

Example of a standard GET API

The API GET /invoices/{purchase_invoice_id} lets you retrieve basic information on a specific purchase invoice.

Response

{
    "modeReglement": "PR",
    "conditionReglement": "00030",
    "buyerCode": null,
    "id": "FF2100462",
    "project": null,
    "projectId": "FG",
    "phase": null,
    "phaseId": "500005899",
    "subPhase": null,
    "subPhaseId": "500005450",
    "type": "PURCHASE_INVOICE",
    "number": "FF2100462",
    "date": "2021-03-25T00:00:00.000+0100",
    "name": "Facture electricité DEC 2020",
    "companyCode": "AKSAS",
    "afterTaxAmount": 1794.08,
    "preTaxAmount": 1501.46,
    "projectedApprovalDate": null,
    "currencyCode": "EUR",
    "currency": null,
    "thirdPartyId": "500001720",
    "thirdPartyToBillId": null,
    "description": null,
    "entityCode": "G&A",
    "manager": null,
    "managerId": null,
    "changeTracking": {
        "createdTime": "2021-03-25T23:21:15.433+0100",
        "createdBy": null,
        "createdById": "-1",
        "modificationTime": "2021-03-25T23:32:17.743+0100",
        "modificationBy": null,
        "modificationById": "-1"
    },
    "text": null,
    "comment": null,
    "payment": null,
    "accountingJournalId": null,
    "reference1": "108002809284",
    "reference2": null,
    "reference3": null,
    "supplier": null,
    "lines": [],
    "supplierDate": "2021-03-25T00:00:00.000+0100",
    "dueDate": null,
    "invoiceType": "INVOICE"
}

⇒ In this example, the lines of the purchase invoice are not displayed.

Example with the /read API

Now, let’s try the API POST /invoices/{purchase_invoice_id}/read to retrieve more information about the invoice’s lines.

Request body

{
    "options" : ["LINE", "LINE_PROJECT", "LINE_ITEM"]
}

Response

{
    "modeReglement": "PR",
    "conditionReglement": "00030",
    .....
    "supplier": null,
    "lines": [
        {
            "id": "500053139",
            "project": {
                "id": "FG",
                "name": "AKUITEO : FRAIS GENERAUX",
                "companyCode": "AKSAS",
                "customerId": "144",
                "endCustomerId": "144",
                "internal": true,
                "leave": false,
                "leaveType": null
            },
            "projectId": "FG",
            "phase": null,
            "phaseId": "500005899",
            "subPhase": null,
            "subPhaseId": "500005450",
            "businessDocumentId": "FF2100462",
            "name": "TOTAL : Conso Electricité",
            "complementName": null,
            "quantity": 1.0,
            "unitPriceCurrency": 1071.05,
            "unitPrice": 1071.05,
            "amountTotalCurrency": 1071.05,
            "amountTotal": 1071.05,
            "rankNumber": 100.0,
            "generalAccount": null,
            "vat1": {
                "code": "01",
                "rate": 20.0
            },
            "vat2": null,
            .....
        },
        {
            "id": "500053140",
            "project": {
                "id": "FG",
                "name": "AKUITEO : FRAIS GENERAUX",
                "companyCode": "AKSAS",
                "customerId": "144",
                "endCustomerId": "144",
                "internal": true,
                "leave": false,
                "leaveType": null
            },
            "projectId": "FG",
            .....
        },
        {
            "id": "500053137",
            "project": {
                "id": "FG",
                "name": "AKUITEO : FRAIS GENERAUX",
                "companyCode": "AKSAS",
                "customerId": "144",
                "endCustomerId": "144",
                "internal": true,
                "leave": false,
                "leaveType": null
            },
            "projectId": "FG",
            .....
        }
    ],
    "supplierDate": "2021-03-25T00:00:00.000+0100",
    "dueDate": null,
    "invoiceType": "INVOICE"
}

7. Webhooks

7.1. Introduction

Akuiteo’s webhook system enables you to receive notifications in real-time whenever specified events occur within the platform. This allows seamless integration with external services like HR management tools, financial systems, or custom applications by triggering HTTP requests when certain actions take place in Akuiteo.

7.2. How It Works

When an event occurs, the webhook sends an HTTP POST request to a specified external endpoint, containing information about the event, including the object involved and the action performed.

Event Types: The eventType field in the payload indicates the type of entity involved, such as CUSTOMER, EMPLOYEE, PROJECT, or other types. You can find a complete list of event types in the EventType.

Event Actions: The eventAction field describes what has happened to the entity—either INSERT, UPDATE, or DELETE, or other specific actions such as SMARTSCAN_DONE.

7.3. Payload Structure

The payload of the webhook is a JSON object structured to provide essential information about the event. Here is a typical example:

{
  "emittedAt": "2024-09-23T10:45:00+02:00",
  "eventType": "PROJECT",
  "eventAction": "UPDATE",
  "requestId": "12345-67890-abcdef",
  "content": {
    "id": "987654321"
  }
}
  • emittedAt: The timestamp of when the event occurred.

  • eventType: The type of entity the event pertains to (e.g., PROJECT, EMPLOYEE).

  • eventAction: The action that triggered the webhook (e.g., INSERT, UPDATE, DELETE).

  • requestId: A unique identifier for this webhook request, useful for tracking and logging.

  • content: Contains data relevant to the entity, typically including the unique id of the object that experienced the event. Depending on the event, more detailed information can be added to the content field.

7.4. Security

Webhooks in Akuiteo use a secure mechanism to ensure the authenticity of requests. Each payload is signed using an HMAC signature, which is calculated using a secret that you configure when setting up the webhook. The signature is included in the X-Hub-Signature header, allowing you to validate the webhook request by comparing the received signature with one generated on your server using the same secret.

Steps to verify webhook requests:

  • Retrieve the payload and the X-Hub-Signature header.

  • Calculate the HMAC of the payload using your configured secret and the SHA256 algorithm.

  • Compare your calculated HMAC with the received signature. If they match, the request is authentic and has not been altered.

Important

The payload used to calculate the HMAC must be taken directly from the raw content of the request body, without any formatting. Here’s an example:

{"emittedAt":"2024-09-23T10:45:00+02:00","eventType":"PROJECT","eventAction":"UPDATE","requestId":"12345-67890-abcdef","content":{"id":"987654321"}}

Ensure that your secret is securely stored and used for validation as outlined in the above steps.

You can refer to code samples in different languages (such as Java, PHP) to implement this validation here.

7.5. HTTP Status Codes & Retry Mechanism

Akuiteo expects the external system to respond with an HTTP status code between 200 and 299 to indicate successful receipt of the webhook. Any response outside this range will be interpreted as a failure, and the webhook system will automatically retry sending the request based on the following logic:

  • Retry Limit: You can configure the maximum number of retries (between 1 and 8 attempts).

  • Retry Intervals:

    • By default, retries are performed with an exponentially increasing delay, calculated as: timeout + retry_number^4. This means that each subsequent attempt is spaced further apart to avoid overloading the system.

    • Alternatively, for testing purposes, you can configure the retry system to use a fixed 3-second interval between attempts, which is helpful in non-production environments.

  • Timeout: The timeout value is configurable, determining how long the webhook system will wait for a response from the external server before considering the request failed. The recommended timeout is several milliseconds to avoid premature failure during high latency scenarios.

  • Failure Handling: If all retry attempts fail (i.e., no 2xx HTTP status is received), the webhook will stop trying to send the request, and an error log will be generated for review. You can download the logs and inspect them to identify the root cause of the failure.

Here is an example of the retry configuration in Akuiteo’s webhook setup:

  • Timeout (in ms): 5000 (wait 5 seconds for a response).

  • Max retries: 5 attempts.

  • Exponential retry: Enabled for production, ensuring the interval increases after each attempt.

7.6. Setting Up a Webhook

  1. In the Admin Console, navigate to Configuration > Webhooks.

    • Click Create a New Webhook and provide the following details:

      • Name: A label for the webhook.

      • Endpoint URL: The URL where the webhook will send POST requests.

      • Event Type: The entity for which events will be triggered (e.g., PROJECT, EMPLOYEE).

      • Event Action: The action that will trigger the webhook (e.g., INSERT, UPDATE, DELETE).

      • Secret: (Optional) Set a secret key for secure HMAC signature validation.

    • Configure retry options and timeouts.

    • Test the webhook to verify connectivity.

    • Once validated, click Create.

7.7. Best Practices

  • Secure Your Endpoint: Always use HTTPS to ensure secure data transmission.

  • Handle Retries Gracefully: Make sure your system is prepared for retries, avoiding duplicate processing.

  • Use Logging: Enable detailed logs for troubleshooting in case of webhook failures.

8. Follow our tutorial to get started!

In this tutorial, we will use the following information:

This tutorial will be using APIs from the Workforce domain. You must refer to the API documentation of this domain to know the URL paths, the parameters and other details about each API.

8.1. Searching for employees

Let’s start by searching for the employees with an employee code that starts with the letter J. We will use the API: POST /employees/search

Request (cURL)

curl --location --request POST 'https://akuiteo.my-server.fr/akuiteo/rest/workforce/employees/search' \
--header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=759870920B6FFB1F4B62C4B6CA8E596B' \
--data-raw '{
    "code": {
        "operator" : "LIKE",
        "value": "J%"
    }
}'
````

*Response*
```json
[
    {
        "id": "500001430",
        "code": "JBE",
        [...]
    },
    {
        "id": "500001421",
        "code": "JBPN",
        [...]
    },
    {
        "id": "500002344",
        "code": "JSH",
        "name": "Smith",
        "firstName": "John",
        "action": null,
        "title": "M.",
        "phone": null,
        "mobilePhone": null,
        "email": "john.smith@akuiteo.com",
        "noTimesDunning": null,
        "multiCompany": false,
        "modeTimeRecording": 3.0,
        "modeInputSchedule": 0.0,
        "timeRecordingInputMode": "HOUR_MINUTE",
        "scheduleInputMode": "DAY",
        "executive": true,
        "generic": false,
        "candidate": false,
        "external": false,
        "managerMultiCompany": false,
        "employeeFunctions": {
            "allFunctions": false,
            "allSales": false,
            "allPurchases": false,
            "allOperational": false,
            "projectSalesManager": false,
            "projectFinanceManager": false,
            "projectProductionManager": false,
            "projectAccountManager": false,
            "projectAssignment": true,
            "projectPhaseManager": false,
            "projectSendOnMission": false,
            "salesSalesAssistant": false,
            "salesOrderAndEstimateTracking": false,
            "salesBillingSchedulesTracking": false,
            "purchasingBuyer": false,
            "timeRecording": true,
            "inputOfExpenseReport": true,
            "purchasesReceptionTracking": false,
            "inputSchedule": true,
            "cMMSContractSalesManager": false,
            "cMMSContractTechnicalManager": false,
            "cMMSMRAndEOManager": true
        },
        "employeeHistory": [],
        "timeRecordingRules": null,
        "scheduleRules": null,
        "businessCards": [],
        "number": "0000000077",
        "relations": [],
        "employeeAbilities": [],
        "jobType": null,
        "jobTypeId": "100257",
        "address": null,
        "addressId": "500020664",
        "customFields": null
    },
    {
        "id": "500001331",
        "code": "JTL",
        [...]
    }
]

⇒ We will focus on the employee John Smith for this tutorial, using its id 500002344

8.2. Retrieving detailed information about a specific employee

With the 1st API, we retrieved first-level information about the employee John Smith. However, we can see that some parameters are not detailed:

ExampleResult

We want to know more about John Smith’s employee history and business cards. We will use the API: POST /employees/{employee_id}/read

Request (cURL)

curl --location --request POST 'https://akuiteo.my-server.fr/akuiteo/rest/workforce/employees/500002344/read' \
    --header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \
    --header 'Content-Type: application/json' \
    --header 'Cookie: JSESSIONID=23CA62EF43C941680939724925E737C0' \
    --data-raw '{
        "options" : ["EMPLOYEE_HISTORY", "BUSINESS_CARDS"]
    }'

Response

{
    "id": "500002344",
    "code": "JSH",
    "name": "Smith",
    "firstName": "John",
    [...]
    "employeeHistory": [
        {
            "id": null,
            "employeeHistoryType": "500000030",
            "employeeId": "500002344",
            "code": null,
            "from": "2020-10-01T00:00:00.000+0200",
            "to": null,
            "dailyHours": 7.42,
            "weekHours": 38.3,
            "occupancyRate": 0.0,
            "managerId": null,
            "companyId": "1",
            "directHourlyRate": 0.0,
            "standardHourlyRate": 0.0,
            "dailyCost": 0.0,
            "dailySellingPrice": 0.0,
            "entity": "500000040",
            "service": "500000058",
            "position": "Rédactrice Technique",
            "skill": "500000279",
            "employeeFamily1": "500000320",
            "employeeFamily2": "500000333",
            "employeeFamily3": "500000381",
            "leave": false,
            "modelWeek": null,
            "modelWeekId": "500000030",
            "salary": 1.0
        }
    ],
    "timeRecordingRules": null,
    "scheduleRules": null,
    "businessCards": [
        {
            "id": "500000040",
            "employeeId": "500002344",
            "cardNumber": "xxxxxxxxxxxx4444",
            "encryptedCardNumber": "XXXXXXXXXXXXXXXXXXXX",
            "name": "Business card",
            "type": "BUSINESS_ACCOUNT",
            "endDate": null
        }
    ],
    [...]
}

⇒ We can see that John Smith has one business card specified in his record sheet, with the id 500000040.

8.3. Modifying a field in the employee record sheet

We want to rename John Smith’s business card, from "Business card" to "John Smith’s business card". We will use the API: POST /employees/{employee_id}/business-cards/{business_card_id}

Request (cURL)

curl --location --request POST 'https://akuiteo.my-server.fr/akuiteo/rest/workforce/employees/500002344/business-cards/500000040' \
--header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=8773313A0C06782E338115058C24706A' \
--data-raw '{
    "id": "500000040",
    "employeeId": "500002344",
    "cardNumber": "xxxxxxxxxxxx4444",
    "name": "John Smith'\''s business card",
    "type": "BUSINESS_ACCOUNT",
    "endDate": null
}'

Response

500000040
````

If we make another call to the 2nd API, here is what we get:

*Response*

```json
{
    "id": "500002344",
    "code": "JSH",
    "name": "Smith",
    "firstName": "John",
    [...]
    "businessCards": [
        {
            "id": "500000040",
            "employeeId": "500002344",
            "cardNumber": "xxxxxxxxxxxx4444",
            "encryptedCardNumber": "XXXXXXXXXXXXXXXXXXXX",
            "name": "John Smith's business card",
            "type": "BUSINESS_ACCOUNT",
            "endDate": null
        }
    ],
    [...]
}

⇒ The business card’s name has been modified.

8.4. Creating a new business card and adding it to the employee record sheet

We now want to create a new business card and add it to John Smith’s record sheet. We will use the API: PUT /employees/{employee_id}/business-cards

Request (cURL)

curl --location --request PUT 'https://akuiteo.my-server.fr/akuiteo/rest/workforce/employees/500002344/business-cards' \
--header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=91BF7F6C3F9C83E3FBB959BD7AB0A7DB' \
--data-raw '{
  "employeeId" : "500002344",
  "cardNumber" : "1234123412341234",
  "name" : "Main business card",
  "type" : "BUSINESS_ACCOUNT"
}'

Response

500000045

If we make another call to the 2nd API, here is what we get:

Response

{
    "id": "500002344",
    "code": "JSH",
    "name": "Smith",
    "firstName": "John",
    [...]
    "businessCards": [
        {
            "id": "500000040",
            "employeeId": "500002344",
            "cardNumber": "xxxxxxxxxxxx4444",
            "encryptedCardNumber": "XXXXXXXXXXXXXXXXXXXX",
            "name": "John Smith's business card",
            "type": "BUSINESS_ACCOUNT",
            "endDate": null
        },
        {
            "id": "500000045",
            "employeeId": "500002344",
            "cardNumber": "xxxxxxxxxxxx1234",
            "encryptedCardNumber": "XXXXXXXXXXXXXXXXXXXX",
            "name": "Main business card",
            "type": "BUSINESS_ACCOUNT",
            "endDate": null
        }
    ],
    [...]
}

⇒ The new business card with the id 500000045 has been added to John Smith’s record sheet.

8.5. Deleting the previous business card

We now want to remove "John Smith’s business card" (id 500000040) from the record sheet. We will the API: DELETE /employees/{employee_id}/business-cards/{business_card_id}

Request (cURL)

curl --location --request DELETE 'https://akuiteo.my-server.fr/akuiteo/rest/workforce/employees/500002344/business-cards/500000040' \
--header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \
--header 'Cookie: JSESSIONID=1E31A6F9012079EB3924361358932CEA'

If we make another call to the 2nd API, here is what we get:

Response

{
    "id": "500002344",
    "code": "JSH",
    "name": "Smith",
    "firstName": "John",
    [...]
    "businessCards": [
        {
            "id": "500000045",
            "employeeId": "500002344",
            "cardNumber": "xxxxxxxxxxxx1234",
            "encryptedCardNumber": "XXXXXXXXXXXXXXXXXXXX",
            "name": "Main business card",
            "type": "BUSINESS_ACCOUNT",
            "endDate": null
        }
    ],
    [...]
}

⇒ The business card "John Smith’s business card" has been deleted and John Smith now only has one business card.