NAV Navbar
cURL Java ACL

Introduction

Welcome to the Factsys API! You can use our API to include Factsys features in your own software. This is API is a pure REST api so you can use this in whatever language you want to.

The two dots in the beginning of every URL refer to https://factsys.be

    @POST("/rest/v1/ping")
    Call<Ping> ping();

For every API call examples will be available in Java (we use Retrofit in our examples) and CURL (CLI) and we'll also try to include the JSON request and/or response if applicable. You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.

The REST API is available at https://factsys.be/rest/v1/xxx. The base URL is simply https://factsys.be. This one will never be repeated.

Errors

Error Codes

Error response body

{
    "message": "A message with some more info on what exactly went wrong",
    "code": xxx,
    "errorCode": xxxx(x)
}
Code Error Code Meaning
400  1004 Bad Request -- The action you are trying to perform is not allowed
400 10004 Bad Request for OAuth -- The client ID or client secret are not correct
400 10005 Bad Request for OAuth -- The grant type provided in the OAuth request is not valid (should be either password or refresh_token)
400 99999 Bad Request -- The form you submitted is not valid, see body content for details
400 na Bad Request -- The server could not correctly interprete your request
401 na The bearer token provided in the request is no longer valid. Request a new one with your refresh_token
403 1002 Forbidden -- The resource you are trying to load is not visible to the currently authenticated user
404 1001 Not Found -- The specified endpoint is not available.
404 1003 Not Found -- The resource you are trying to load is cannot be found
404 10001 Not Found -- Either the username or password (or combination) you provided to the OAuth are not available
404 10003 Not Found -- The refresh token you provided to the OAuth request cannot be found or has expired
405 na Method Not Allowed -- You tried to access an endpoint method with an HTTP method that is not supported
406 na Not Acceptable -- You requested a format that isn't json.
500 1000 Internal Server Error -- We had a problem with our server. Try again later.
500 na Internal Server Error -- We had a problem with our server. Try again later.
503 na Service Unavailable -- We're temporarily offline for maintenance. Please try again later.

Form Validation

Form validation error response body

{
    "message": "OAuth validation failed due to field validation issues",
    "code": 400,
    "errorCode": 99999,
    "errors": [
        {
            "field": "username",
            "type": "MISSING",
            "reason": "The username is missing"
        },
        {
            "field": "password",
            "type": "MISSING",
            "reason": "The password is missing"
        }
    ]
}

Whenever submitting forms to the API (for creation (PUT), update (PATCH) or whatever else (POST)) all fields of the form will be validated. When the form is invalid for whatever reason the API will return HTTP status code 400 with an internal error code 99999. Together with this you'll receive some extra information on which forms that caused the 400 and the reason why the validation of these individual fields failed. The reason is provided in a textual explanation of what is not ok but also in a structural way using the type field.

Type  Meaning
MISSING  The specific field is required and cannot be empty
INVALID  The value provided in the field is not valid, check the reason for more details
NOT_ALLOWED  The field you are trying to change is not editable (like identifiers)

Formatting

Timestamps

Timestamps will always be formatted like this: 2020-02-14T12:13:00.164Z. The format is yyyy-MM-dd'T'hh:mm:ss.SSS'Z'. Depending on your programming language the format might be a little different. Times are always in UTC time! To format times for the user you should use the timezone and dateformat specified in the user's profile!

Authentication

Factsys makes use of two types of authentication. Both are industry standards: OAUTH2 and Basic authentication. Both however serve a different purpose. Which authentication type to use depends if you tend to create a public or private software package.

Public apps

When you develop a public application that you will publish to an application store where it can be downloaded then you have no control over what user comes to your app and wants to connect to Factsys. Therefore you choose for OAuth2. Using this authentication method you will have to ask the user for his/her username and password at Factsys, submit it to us and we will provide you with a unique but temporary access token.

Private apps

When developing a private app that should make use of or integrate with Factsys you can go for basic authentication. This way you will keep two technical values in you client app that will allow you to communicate with our backend. These values are the client-id and client-secret.

Authentication - OAUTH2

Accessing resources without authentication

{
    "code": 401,
    "message": "Full authentication is required to access this resource"
}

Factsys uses OAuth 2.0 as authentication. That means that initial authentication will go in a few steps, you will have keep track of the access_token and refresh_token on your clients and you have to manage reauthentication (401).

Prerequisites

Provide your clientId and clientSecret for authenticating users

    OkHttpClient oauthOkHttpClient() {
        final OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.addInterceptor(chain -> {
            Request.Builder requestBuilder = chain.request().newBuilder();
            String clientId = "YOUR_CLIENT_ID";
            String clientSecret = "YOUR_CLIENT_SECRET";
            requestBuilder.addHeader("Authorization", "Basic " + Base64.encodeToString((clientId + ":" + clientSecret).getBytes("UTF-8"), Base64.NO_WRAP));
            return chain.proceed(requestBuilder.build());
        });

        enableOkHttpLogging(builder);
        OkHttpClient httpClient = builder.build();
        return httpClient;
    }
curl -X POST \
  '/rest/v1/ping' \
  -H 'Authorization: Basic WU9VUl9DTElFTlRfSUQ6WU9VUl9DTElFTlRfU0VDUkVU='

To be able to authenticate a user with OAuth 2.0 you first of all need a clientId and clientSecret. You cannot currently get one from within your factsys account so you will have to request one at api@factsys.be. However in the future it will be possible generate both for your company. For all of your authentaication requests both need to be provided to the API using Basic authentication. All of the OAuth API is availalbe at /rest/v1/oauth.

Retrieve an access_token

Authenticate a Factsys user with username and password

    @POST("/rest/v1/oauth/token?grant_type=password")
    Observable<Authentication> getAccessToken(@Query("username") String username, @Query("password") String password);
curl -X POST \
  '/rest/v1/oauth/token?grant_type=password&username=user@domain.com&password=ahardtorememberpassword' \
  -H 'Authorization: Basic WU9VUl9DTElFTlRfSUQ6WU9VUl9DTElFTlRfU0VDUkVU='

Response

{
    "token_type": "bearer",
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmYWN0c3lzX21vYmlsZV9hcHAiLCJpc3MiOiJkaXJrQGFwcDMuYmUiLCJleHAiOjE1NDk2MzQ2Njl9.kUePqM4_IWpAYpBeI7DySdlTyMTFDueG9WtGSxVa-Ww",
    "refresh_token": "QNlpQ0qH4zRMPzM3i7tvqhEubrL0nu4qkjAdKFB7Yi4bBgkznVsr886Qtkgnd1X6",
    "expires_in": 180
}

So we are ready to start authenticating users now. In Factsys users authenticate with a username (= email) and password. According to the OAuth 2.0 specs the password grant type will be used for this purpose in a POST request. Although the HTTP method is POST we are not submitting a body here, only query parameters: * grant_type=password * username with the email adress of the authenticating user * password with the password of the authenticating user

And off course don't forget the Authorization: Basic xyz header!

In the result you will find the access_token and refresh_token. Save them somewhere safe because you will need them later for access resources on the server or to get a new access_token.

During authentication following errors might occur:

Code Error Code  Meaning
404 10001 Either the username or password (or combination) you provided to the OAuth are not available
400 10004 The client ID or client secret are not correct
400 10005 The grant type provided in the OAuth request is not valid (should password)
400 99999 The grant_type, username or password fields are missing or the authorization header is not provided

401 - Please reauthenticate

Response with expired access_token

{
    "code": 401,
    "message": "The Token has expired on Tue Feb 12 06:17:59 UTC 2019."
}

Issue a new access_token

    @POST("/rest/v1/oauth/token?grant_type=refresh_token")
    Observable<Authentication> getAccessToken(@Query("refresh_token") String refreshToken);
curl -X POST \
  '/rest/v1/oauth/token?grant_type=refresh_token&refresh_token=4tp61TGNRbIXqwWZFb0W6gyOS9FREQliQraaEQYtRGuXjOc8Vu3vTQ6CBOYqd33l' \
  -H 'Authorization: Basic ZmFjdHN5c19tb2JpbGVfYXBwOlImNyFCend1YHNDeFA0NT4='

Response

{
    "token_type": "bearer",
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmYWN0c3lzX21vYmlsZV9hcHAiLCJpc3MiOiJkaXJrQGFwcDMuYmUiLCJleHAiOjE1NDk2MzU1NDJ9._REkE3kgHQAvgAoIPHoMuwNqzwfjoGJDNOFvhFlekOY",
    "refresh_token": "43ZzpML5Z1id0o29mEVTKS0JEQZDyJxtQ40JgXCCMyQhw4uAIHoZSEi8p7VYTudm",
    "expires_in": 180
}

The access tokens issued by Factsys will expire every 30 minutes. That's not much time for you to do your work. And that's why you will need the refresh token as well. Whenever the access token has expired every API call will result in a 401 status. You will have to issue a new access token that will be valid for another 30 minutes. Issuing a new access token is done with a refresh token and the server will return you a new access_token off course BUT also a new refresh_token. The access tokesn are reusable for 30 minutes, the refresh tokens can only be used once. According to the OAuth 2.0 specification the grant_type for getting a new access token is refresh. The only other query parameter you need is the refresh_token itself.

Make sure to update the newly retrieved access_token and refresh_token from the response of the call to use in any future requests!

Error response

{
    "message": "Trying to refresh access token with an invalid/blacklisted refresh-token: HXmpwmcA4srFzWo7WzHDCCpM49t7jcuzCeSZztWpXG7LZmf0LMfPM32n1o28RkcS",
    "code": 404,
    "errorCode": 10003
}

While reauthenticating following errors might occur:

HTTP Code Internal error code  Meaning
404 10003 The refresh token you provided to the OAuth request cannot be found or has expired
400 10004 The client ID or client secret are not correct
400 10005 The grant type provided in the OAuth request is not valid (should refresh_token)
400 99999 The grant_type or refresh_token fields are missing or the authorization header is not provided

Authentication - Basic

Accessing resources without authentication

{
    "code": 401,
    "message": "Full authentication is required to access this resource"
}

Basic authentication is much simpler than OAuth2 on the client side to implement. The only thing you need to do is to pass on a certain header and you are good to go. No token refresh is needed. However you still need to handle 401 status codes in case the client secret gets reject on the server because of some kind of security breach!

Every request should have an Authorization header with a base64 encoded string. The header will look something like this: Basic YjE0NmJlZWYtNTA1NS00YjA5LTk5NDQtMTkzYzEzNWU3ODczOmQyZWE2YzgyLWU2NTktNDY0OS04NjIxLTQwYmI1NGZhZDkxYQ==. The base64 encoded string is the combination of the client-id and client-secret separated with a colon (:). If you base64 encode that string you will have something similar as our example. Then you put Basic (mind the space at the end) in front of it and you have your Authorization header.

Basic authentication is only availble for users with company admin rights. This means that when using this authentication method that you may safly assume the user will always have the COMPANY_ADMIN which is explained in the next section. However which features that are available can still change from user to user or company to company configuration. So before using any feature you should check if the feature is available in the user profile (which will be linked to the client-id and client-secret).

ACL and Security

Introduction

Accessing a non-existing or non-authorized resource

{
    "message": "Some more info on the resource that cannot be accessed",
    "code": 403,
    "errorCode": 1002
}

All Factsys users can authenticate against either the REST API or the web interface (at https://factsys.be/login) but the data they will see or have access to will be limited based on the Access Control Layer we have put in place. Whenever you try to access a resource that cannot be accessed by this user either because it simply does not exist or because the user is not allowed to we will return an HTTP status code 403 (Forbidden) with an internal error code 1002.

The Access Control Layer works with with Roles and Features.

Roles and Features

There are 2 roles available:

Role  Meaning
COMPANY_ADMIN The company admin (the default role you will get when you first register a company on Factsys) has access to all resources of his company.
COMPANY_USER  The company users are managed by the company admins and the resources they have access to can be limited and can change over time.

Based on these roles and the configuration of the user Factsys will determine which Features the user has access to. For most features there is a VIEW feature and a MANAGE feature. Easy as it is a user with the VIEW feature will be able to view a certain resource, if he also has the MANAGE feature he will be able to change, update or create resources in that category. A company admin will always have all VIEW features, that is for sure. However weather he has the MANAGE features depends on a valid subscription on the platform (not managable through the API's).

For company users it's more complex. Whenever an admin creates a user different configuration options are available for that user. For instance it is possible to give him access on the invoices of the company. That means that the user will have the FEATURE_VIEW_INVOICE feature, and if the subscription for the company is still valid he will also have FEATURE_MANAGE_INVOICE. But that does not mean he will be able to access all invoices. The company admin can limit the resources the user has access to. Whenever retrieving a list of resources (invoices for instance) that list will be filtered based on the access rights of the user, and only that subset of resources will be accessible to the user. If at any time the user tries to access a resource only available to the company admins (or other users but not to him) the API will return the 403 status code.

An overview of all features available today:

Feature Meaning
FEATURE_VIEW_CLIENTS View the company clients/customers
FEATURE_MANAGE_CLIENT Manage (create, update) company clients/customers
FEATURE_VIEW_PRODUCTS View the products
FEATURE_MANAGE_PRODUCT Manage (create, update) products
FEATURE_VIEW_INVOICE View the invoices
FEATURE_MANAGE_INVOICE Manage (create, update, delete, send) invoices
FEATURE_MANAGE_RECURRING_INVOICE Manage (create, update, delete) recurring invoice configurations. There is no VIEW feature available for this!
FEATURE_VIEW_QUOTATION View the quoations
FEATURE_MANAGE_QUOTATION Manage (create, update, delete, send) quoations
FEATURE_VIEW_CREDIT_NOTE View the credit notes
FEATURE_MANAGE_CREDIT_NOTE Manage (create, update, delete, send) credit notes
FEATURE_VIEW_WORK_ORDER View the work orders
FEATURE_MANAGE_WORK_ORDER  Manage (create, update, delete, send) work orders
FEATURE_VIEW_PAYMENTS View payments on invoices
FEATURE_MANAGE_PAYMENT Manage (create, update, delete) payments
FEATURE_VIEW_COMPANIES View the companies you are active in
FEATURE_MANAGE_COMPANY Manage the company details (update)
FEATURE_MANAGE_COMPANY_USERS Manage the users of the company. There is no VIEW feature available for this!
FEATURE_MANAGE_COMPANY_TASKS Manage the company internal tasks. There is no VIEW feature available for this!
FEATURE_VIEW_TIMESHEET View and manage your own time sheet
FEATURE_MANAGE_TIMESHEETS Manage the timesheet (time entries) for all users of the company

Working with Roles and Features

Since there are 2 roles to handle, the less restrictive role is the COMPANY_ADMIN one. This one will be allowed to do anything and cannot be limited in features or resources. On the other hand if the role of the user COMPANY_USER it will be limited to a set of so-called features that he will have access to. Also the resources may be limited, but that will be handled by the API internally, no need for you to worry about!

There is however a 'rare' case in which a COMPANY_ADMIN does not have access to all of the features. The only case in which this may happen is when the license for the company has expired. As soon as this happens the entire company will fall back to only have the VIEW, and no longer the MANAGE features. So ideally you should always check that a user has the according MANAGE action before executing an action (such as entering creating or finalizing invoices). However if you don't you might end up with some bad UX, but the API's will internally still handle this and disallow the action performed.

User Profile

All of the user endpoints are available at /rest/v1/users.

The Profile object

{
  "email": "user@domain.com",
  "firstName": "John",
  "lastName": "Doe",
  "language": "nl-BE",
  "timezone": "CET",
  "dateFormat": "dd/MM/yyyy",
  "favouriteCompany": {
    "id": 1,
    "name": "Hooli",
    "logo": "https://botw-pd.s3.amazonaws.com/styles/logo-thumbnail/s3/032018/untitled-1_402.png?9B3JAUEkoVDD_uwVlOJaQ57wMuBKWul_&itok=eo3I5hnx",
    "address": {
      "street": "Newell Road 5230",
      "postal": "94020",
      "city": "Palo Alto",
      "country": "US"
    },
    "vatNumber": "ATU37675002",
    "language": "nl",
    "currency": {
      "sign": "€",
      "shortName": "EUR",
      "name": "Euros"
    },
    "defaultVAT": {
      "products": 0.21,
      "services": 0.21
    }
  },
  "availableCompanies": [
    {
      "id": 1,
      "name": "Hooli",
      "logo": "https://botw-pd.s3.amazonaws.com/styles/logo-thumbnail/s3/032018/untitled-1_402.png?9B3JAUEkoVDD_uwVlOJaQ57wMuBKWul_&itok=eo3I5hnx",
      "vatNumber": "ATU37675002",
    }
  ],
  "accessibleFeatures": [
    "ROLE_COMPANY_ADMIN",
    "FEATURE_VIEW_CLIENTS",
    "FEATURE_MANAGE_CLIENT",
    "FEATURE_VIEW_PRODUCTS",
    "FEATURE_MANAGE_PRODUCT",
    "FEATURE_VIEW_INVOICE",
    "FEATURE_MANAGE_INVOICE",
    "FEATURE_MANAGE_RECURRING_INVOICE",
    "FEATURE_VIEW_QUOTATION",
    "FEATURE_MANAGE_QUOTATION",
    "FEATURE_VIEW_CREDIT_NOTE",
    "FEATURE_MANAGE_CREDIT_NOTE",
    "FEATURE_VIEW_PAYMENTS",
    "FEATURE_MANAGE_PAYMENT",
    "FEATURE_VIEW_COMPANIES",
    "FEATURE_MANAGE_COMPANY_USERS",
    "FEATURE_MANAGE_COMPANY",
    "FEATURE_MANAGE_COMPANY_TASKS",
    "FEATURE_VIEW_TIMESHEET",
    "FEATURE_MANAGE_TIMESHEETS"
  ]
}
Field Status Info
email Required
firstName Required
lastName Required
language Required
timezone Required
dateFormat Required
favouriteCompany Available The favourite company is a company from the available companies on which all actions for the user will be performed. The favouriteCompany property contains a Company object. Submission of this object on edit will be ignored.
availableCompanies Available This list can never be edited, submission on edit will be ignored.
availableCompanies.id Available
availableCompanies.name Available
availableCompanies.vatNumber Available
availableCompanies.logo Optional This field cannot be set in an update of the profile. A separate endpoint is available for this.
accessibleFeatures Available This list can never be edited, submission on edit will be ignored. This is the list you have to use to limit which features the user will have access to.

Get the profile (GET)

Get the authenticated user's profile

curl -X GET \
  /rest/v1/users/profile \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmYWN0c3lzX21vYmlsZV9hcHAiLCJpc3MiOiJkaXJrQGFwcDMuYmUiLCJleHAiOjE1MzgzMzYzNjh9.dNkyBm2F70FYyiJUntf-ftTAU3cpf1f_pCrDyWXY0NA'
    @GET("/rest/v1/users/profile")
    Call<UserProfile> getUserProfile();

Retrieval of the user's profile will result in the Profile object. All required and available fields will be filled in a GET request. The optional fields will be missing when not available.

Update the profile (PATCH)

Update the authenticated users's first name

curl -X PATCH \
  http://localhost:8082/rest/v1/users/profile \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmYWN0c3lzX21vYmlsZV9hcHAiLCJpc3MiOiJkaXJrQGFwcDMuYmUiLCJleHAiOjE1MzgzMzYzNjh9.dNkyBm2F70FYyiJUntf-ftTAU3cpf1f_pCrDyWXY0NA' \
  -H 'Content-Type: application/json' 
  -d '{
    "firstName": "Jane"
}'
    // RestService.java
    @PATCH("/rest/v1/users/profile")
    Call<ResponseBody> patchUserProfile(@Body UserProfilePatchBody);

    // Rest consuming class
    UserProfilePatchBody patchBody = new UserProfilePatchBody();
    patchBody.setFirstName("Jane");
    Response<ResponseBody> response = restService.patchUserProfile(patchBody).execute();
    if (response.isSuccessfull()) {
        // TODO the success case
    } else {
        // TODO the error case
    }

Error response

{
    "message": "Following fields are missing in the OAuth token request: username, password",
    "code": 400,
    "errorCode": 1003,
    "items": [
        "username",
        "password"
    ]
}

For updating the user profile only the main Profile fields can be updated. The ID field and relational and calculated fields cannot be updated through the update profile. For instance profile.favouriteCompany.name cannot be updated. If you would like to do so you will need to use the Company REST API calls for this purpose. Here is a quick overview of what can be updated on the userprofile and what not:

Field Updatable
email No (identifier)
firstName Yes
lastName Yes
language Yes
timezone Yes
dateFormat Yes
favouriteCompany No (relational field)
availableCompanies No (relational field)
accessibleFeatures No (calculated field)

The update of a profile should use the HTTP PATCH method and should only provide the fields that have changed. A succesfull PATCH request will result in an HTTP status code 200 and will not have a response-body.

Switch favourite company (POST)

CRM

Clients

The client object

{
  "id": 432,
  "name": "ABCompany BVBA",
  "number": 3,
  "vatNumber": "BE000000001",
  "netInvoice": "14",
  "address": {
    "street": "Grote markt",
    "postal": "1000",
    "city": "Brussel",
    "country": "BE"
  },
  "currency": {
    "sign": "€",
    "shortName": "EUR",
    "name": "Euros"
  },
  "language": "English",
  "languageCode": "en"
}

All of the CRM Client endpoints are available at /rest/v1/crm/clients.

Field Required(✓)/Optional(?) Info
id The identifier of the client will always be available, after retrieving a client this is the key data to be used for further operations on the item.
name The name of this client
number This is the internal client's number, auto numbered starting from 1 if not manually modifiable
vatNumber ? A EU compliant VAT number for the company if available, some companies are VAT eligible so this field can be empty!
netInvoice  ? The default amount of days after which the client will have to pay his invoices. We suggest you provide this field, however you can leave this one out and we will default it to 14 (days).
address  ✓  The address entity (street, postal, city and country). This should be the invoice address, not the delivery address if different. The country is the ISO country code!
currency  ✓ The currency the client will by default be invoiced in. This can be overriden when creating invoices. For setting the currency you only need to provide the field currency.shortName which is the currencies ISO representation.
language  ✗ The language as can be displayed to the user
languageCode The iso language code (supported values: en, fr, nl)

Get all the clients (GET)

Get the list of all available clients

curl -X GET \
  /rest/v1/crm/clients \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmYWN0c3lzX21vYmlsZV9hcHAiLCJpc3MiOiJkaXJrQGFwcDMuYmUiLCJleHAiOjE1MzgzMzYzNjh9.dNkyBm2F70FYyiJUntf-ftTAU3cpf1f_pCrDyWXY0NA'
    @GET("/rest/v1/crm/clients")
    Call<List<CrmClient>> getCrmClients();

Retrieval of all the clients available in the user's company mapped in a json array.

Get one client (GET)

Get one single client by ID

curl -X GET \
  /rest/v1/crm/clients/487 \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmYWN0c3lzX21vYmlsZV9hcHAiLCJpc3MiOiJkaXJrQGFwcDMuYmUiLCJleHAiOjE1MzgzMzYzNjh9.dNkyBm2F70FYyiJUntf-ftTAU3cpf1f_pCrDyWXY0NA'
    @GET("/rest/v1/crm/clients/{id}")
    Call<CrmClient> getCrmClient(@Path("id") int id);

Search clients by property (GET)

You can search for clients by name, email or vat. The endpoints available are:

They all result in an array of clients.

Create client (POST)

All the fields marked as required in the client object table should be provided when creating a new one.

POST /rest/v1/crm/clients

A client POST/PUT request object

{
  "name": "XYZ Company",
  "vatNumber": "BE000000001",
  "netInvoice": "14",
  "address": {
    "street": "Grote markt",
    "postal": "1000",
    "city": "Brussel",
    "country": "BE"
  },
  "currency": {
    "shortName": "EUR"
  },
  "languageCode": "en"
}

Edit client (PUT)

PUT /rest/v1/crm/clients/{id}

The id is the one you retieve from the list of clients or that you got after creating a client.

By not specifying a field from the client that has been set before (and that is not required) you will reset that field!

Invoices

Invoices are complex documents with quite complex states. However in Factsys we like to keep this as simple as possible. In Factsys an invoice can have 2 states: draft or ready.

The draft state means that the invoice is being created. As long as it is in this state you can keep modifying the invoice. It is not possible to download the PDF version of the invoice as long as it's in this state!

The ready state means that the invoice is ready to be sent to the customers and more advanced features are available such as sending the invoice to the accountant, getting the PDF version of the invoice,...

Invoices can transitation between these two states. The transition from draft to ready is called finalize, the transition back is called recall. In some cases it's not allowed for an invoice to transition back from ready to draft. One of these cases is when the invoice is already at the accountant.

Invoice Object

All of the invoice endpoints are available at /rest/v1/invoices.

The invoice object

{
  "id": 432,
  "subject": "Invoice for March 2020",
  "year": 2020,
  "number": 10,
  "currency": {
    "sign": "€",
    "shortName": "EUR",
    "name": "Euros"
  },
  "reverseVat": false,
  "status": "READY",
  "sentToCustomer": false,
  "date": "TBD",
  "expirationDate": "TBD",
  "language": "English",
  "languageCode": "en",
  "totalExclVat": 200.00,
  "totalVat": 42.00,
  "total": 242.00,
  "paymentDetails": {
    "paymentProvider": "epc_free",
    "communication": {
      "type": "free",
      "description" "TEST123"
    },
    "banking": {
      "iban": "BE000000001",
      "bic": "GEBABEBB"
    },
    "netInvoice": 14,
    "conditions": "The full payment conditions text"
  },
  "companyDetails": {

  },
  "clientDetails": {

  },
  "client": {
    "id": 432,
    "name": "ABCompany BVBA",
    "number": 3,
    "vatNumber": "BE000000001",
    "netInvoice": "14",
    "address": {
      "street": "Grote markt",
      "postal": "1000",
      "city": "Brussel",
      "country": "BE"
    },
    "currency": {
      "sign": "€",
      "shortName": "EUR",
      "name": "Euros"
    },
    "language": "English",
    "languageCode": "en"
  },
  "creator": {
    "email": "test@domain.com",
    "firstName": "Jane",
    "lastName": "Doe"
  },
  "paymentDetails": {

  },
  "deliveredToAccountant": true,
  "deliveredToAccountantDate": "TBD",
  "lines": [
    {
      "id": 1234,
      "order": 0,
      "type": "line",
      "description": "Cake",
      "quantity": 3.0,
      "unit": "pieces",
      "price": 10.45,
      "reduction": 0.00,
      "reductionPercentage": 0.00,
      "vat": 0.21,
    },
    {
      "id": 1235,
      "order": 1,
      "type": "comment",
      "description": "As you asked, non burned cake!"
    },,
    {
      "id": 1236,
      "order": 2,
      "type": "group",
      "description": "All of the pie:",
      "lines": [
        {
          "id": 1237,
          "order": 0,
          "type": "line",
          "description": "Cherry pie",
          "quantity": 2.0,
          "price": 10.45,
          "reduction": 0.00,
          "reductionPercentage": 0.00,
          "vat": 0.11,
        },
        {
          "id": 1238,
          "order": 1,
          "type": "line",
          "description": "Chees pie",
          "quantity": 1.0,
          "price": 15.99,
          "reduction": 0.00,
          "reductionPercentage": 0.00,
          "vat": 0.11,
        }
      ]
    },
  ],
  "payments": [
    {
      "id": 1,
      "amount": 100.10,
      "date": "TBD",
      "communication": "Yes we paid for invoice xxxx/zzzz!",
      "source": "pontno"
    },
    {
      "id": 2,
      "amount": 10.59,
      "date": "TBD",
      "communication": "+++642/4687/20064+++",
      "source": "codabox"
    }
  ]
}

Invoice

Field Required(✓)/Optional(?) Info
id The identifier of the invoice will always be available, after retrieving an invoice this is the key data to be used for further operations on the item.
subject  ✓ The subject of the invoice. This field might be empty on retrieval for legacy invoices
year  ✓ The (financial) year for this invoice
number The number of this invoice, this cannot be manually set.
invoiceNumber  ✗  The fully formated invoice, most of the times a combination fof the year and number. This cannot be manually set.
currency  ✓ The currency for this invoice. For setting the currency you only need to provide the field currency.shortName which is the currencies ISO representation. Leaving this empty on creation will take the default from the client.
reverseVat  ?  Only needs to be set if reverse VAT is applicable. If you don't set this field and reverse VAT is set through client configuration then it will be set to true afterwards.
status  ✗ The status of the invoice being either DRAFT or READY. To change the status you need to finalize or recall the invoice through the API calls.
sentToCustomer  ✗  An indication if this invoice has already been sent to the customer or not.
date  ✓ The date for which the invoice is applicable (normally you should put this to today or the day a customer made an order).
expirationDate This is a calculated date based on the invoice date and the net invoice value in the payment details.
language  ✗ The language as can be displayed to the user
languageCode The iso language code (supported values: en, fr, nl)
totalExclVat  ✗  The total excluding VAT for this invoice. Calculated based on the lines.
totalVat  ✗  The total VAT amount for this invoice. Calculated based on the lines.
total  ✗  The total including VAT for this invoice. Calculated based on the lines.
creator  ✗  The name and email of the user account used for creating this invoice.
client On invoice lookup the client object will be provided as described in the client API documentation.
deliveredToAccountant  ✗  This is only available if an invoice to accountant integration (such as accounting software or Codabox) is enabled and configured. true if already sent to the accountant, false otherwise. As soon as invoice is with the accountant you can no longer recall and edit the invoice!
deliveredToAccountantDate  ✗ This is only available if an invoice to accountant integration (such as accounting software or Codabox) is enabled and configured.

Payment details

Field Required(✓)/Optional(?) Info
paymentProvider  ? Optionally you can set a payment provider. Currently supported values are: epc_free.
communication  ✓ The communication information needs to be provided. Two types are available: free or belgian_structured. The free communication can be set through the API with the description field, the Belgium structured communication cannot be set but will be generated by the system.
banking  ✓  Both the IBAN and BIC number of the account on which you want to receive the invoice payment needs to be provided.
netInvoce The amount of days after the invoice date by which the invoice needs to be paid
conditions ?  The payment conditions. These are pre-configured on company level but can be overriden (or set empty).

Company details

TODO!

Client details

TODO!

Line

Factsys supports nesting of invoice lines to create really complex invoices in a structured way. To do so we have 3 types of invoice lines:

Important to now is that a line of the invoice from one type cannot be changed to another type! If you want to change the comment line in a regular line then you need to remove the comment line and add a new regular line!

Regular line

Field Required(✓)/Optional(?) Info
id  ✗ The identifier of the line will always be available for further operations on the item.
order ?  The order of the invoice lines. If you provide 0 for all of the lines we will do the ordering for you based on the order in which you provide the lines to the API. Don't mind if there are gaps in the order numbers you provide, that will not pose an issue!
type  ✓  Always provide the type of the line (and don't change it!), allowed values are line, group and comment. In this case it must always be line.
description  The description of what you are selling (service, product,...)
quantity  ✓ The number of items, hours,... you sell
unit  ? The unit is optional, you can leave this empty. Allowed values are (but not limited to this list!): none (same as empty), items, pieces, visits, people, packets, crates, boxes, pallets, rolls, bags, minutes, hours, days, weeks, months, kilometer, meter, decimeter, centimeter, millimeter, kilogram, gram, milligram, liter, deciliter, centiliter, milliliter, square_meter, cubic_meter, cubic_centimeter
price  ✓ The price per item at which you sell
vat  ✓ The amount of vat to be applied (eg: 0.21)
reduction  ? The total reduction you want to give the user (not per quantity but for the total). This is not required.
reductionPercentage  ? If you don't provide the total reduction you can apply a reduction percentage
totalVat  ✗ Readonly: The total amount of VAT
totalReduction  ✗ Readonly: The total reduction (if specified in percentage this is the calculated field)
totalExcludingVat  ✗ Readonly: The total excluding VAT
totalExcludingVatAndReduction  ✗ Readonly: The total excluding VAT and excluding reduction (is the same as quantity * price)
total  ✗ Readonly: The total of this line (including VAT and with reduction applied)

Comment line

Field Required(✓)/Optional(?) Info
id  ✗ The identifier of the line will always be available for further operations on the item.
order ?  The order of the invoice lines. If you provide 0 for all of the lines we will do the ordering for you based on the order in which you provide the lines to the API. Don't mind if there are gaps in the order numbers you provide, that will not pose an issue!
type  ✓  Always provide the type of the line (and don't change it!), allowed values are line, group and comment. In this case it must always be line.
description  The description is the comment.

Group line

Field Required(✓)/Optional(?) Info
id  ✗ The identifier of the line will always be available for further operations on the item.
order ?  The order of the invoice lines. If you provide 0 for all of the lines we will do the ordering for you based on the order in which you provide the lines to the API. Don't mind if there are gaps in the order numbers you provide, that will not pose an issue!
type  ✓  Always provide the type of the line (and don't change it!), allowed values are line, group and comment. In this case it must always be line.
description  The description is the name of the group, this is requried!
totalVat  ✗ Readonly: The total amount of VAT for all sub lines
totalReduction  ✗ Readonly: The total reduction for all sub lines
totalExcludingVat  ✗ Readonly: The total excluding VAT for all sub lines
totalExcludingVatAndReduction  ✗ Readonly: The total excluding VAT and excluding reduction for all sub lines
total  ✗ Readonly: The grand total of this group (including VAT and with reduction applied)

Payment

Field Required(✓)/Optional(?) Info
id The identifier of the payment will always be available for further operations on the item.
amount  ✓ The amount the user paid
date ?  The date of the payment. If not provided in a payment creation it will be set to now ( = today)
communication  ✗ The communication the user specified. In case of an automated payment with a payment provider this info could be put here. For payments we automatically process from an external resource (such as Ponto or Codabox) it will be the real communication that will be on the company accounts transaction. In case of a structured communication it will be formatted like this: +++642/4687/20064+++.
source  ✗ You cannot specify this field yourselve. It will either contain codabox or ponto. The two automated payment providers we support today. Keep in mind that in the future any value could be in this field.

Retrieving all invoices

curl --location --request GET '/rest/v1/invoices' \
--header 'Authorization: Basic YjE0NmJlZWYtNTA1NS00YjA5LTk5NDQtMTkzYzEzNWU3ODczOmQyZWE2YzgyLWU2NTktNDY0OS04NjIxLTQwYmI1NGZhZDkxYQ=='

GET /rest/v1/invoices

Retrieving a single invoice by ID

curl --location --request GET '/rest/v1/invoices/{invoiceId}' \
--header 'Authorization: Basic YjE0NmJlZWYtNTA1NS00YjA5LTk5NDQtMTkzYzEzNWU3ODczOmQyZWE2YzgyLWU2NTktNDY0OS04NjIxLTQwYmI1NGZhZDkxYQ=='

GET /rest/v1/invoices/{invoiceId}

Search invoice by year and number

curl --location --request GET '/rest/v1/invoices/by/{year}/{number}' \
--header 'Authorization: Basic YjE0NmJlZWYtNTA1NS00YjA5LTk5NDQtMTkzYzEzNWU3ODczOmQyZWE2YzgyLWU2NTktNDY0OS04NjIxLTQwYmI1NGZhZDkxYQ=='

GET /rest/v1/invoices/by/{year}/{number}

In case the year and number combination cannot be found for the authenticated user this endpoints throws a 404 with error code 1003 indicating the requested resource is not available at the moment.

Download the invoice PDF

curl --location --request GET '/rest/v1/invoices/{invoiceId}/pdf' \
--header 'Authorization: Basic YjE0NmJlZWYtNTA1NS00YjA5LTk5NDQtMTkzYzEzNWU3ODczOmQyZWE2YzgyLWU2NTktNDY0OS04NjIxLTQwYmI1NGZhZDkxYQ=='

You can download the PDF version of an invoice as soon as it has transitioned to the ready state. The invoice PDF will remain available for download as long as the invoice remains in this state! If the invoice in state draft it will return an error!

GET /rest/v1/invoices/{invoiceId}/pdf

Creating an invoice

Simples invoice creation call

{
  "subject": "My invoice created with the Factsys API",
  "year": 2020,
  "date": "2020-01-31T12:13:00.164Z",
  "lines": []
}

Invoice creation call with lines included

{
  "subject": "Test invoice",
  "year": 2020,
  "date": "2020-01-31T12:13:00.164Z",
  "lines": [
    {
      "type": "line",
      "description": "Cake",
      "quantity": 3.0,
      "unit": "pieces",
      "price": 10.45,
      "vat": 0.21
    },
    {
      "type": "line",
      "description": "Flower",
      "quantity": 3.0,
      "unit": "kilogram",
      "price": 1.50,
      "vat": 0.21
    }
  ]
}

The invoice creation is an easy process and taking into account all of the default values for payment, company and client information you can create an invoice in one single call. The invoice creation API is available at:

POST /rest/v1/invoices/create/{clientId}

Field Required(✓)/Optional(?) Info
subject  ✓ The subject of the invoice
year  The (financial) year of the invoice
date  ✓ The date for which the invoice is, technically this could be in the future, however we don't recommend this. Normally you would put the current timestamp here
lines  ? The lines for this invoice. If you don't specify them now you could specify them later

Creating an invoice and finalize

Creation and finalisation in one call

curl --location --request POST '/rest/v1/invoices/create/{invoiceId}/finalize' \
--header 'Authorization: Basic YjE0NmJlZWYtNTA1NS00YjA5LTk5NDQtMTkzYzEzNWU3ODczOmQyZWE2YzgyLWU2NTktNDY0OS04NjIxLTQwYmI1NGZhZDkxYQ==' \
--data-raw '{
  "subject": "Test invoice",
  "year": 2020,
  "date": "2020-01-31T12:13:00.164Z",
  "lines": [
    {
      "type": "line",
      "description": "Cake",
      "quantity": 3.0,
      "unit": "pieces",
      "price": 10.45,
      "vat": 0.21
    },
    {
      "type": "line",
      "description": "Flower",
      "quantity": 3.0,
      "unit": "kilogram",
      "price": 1.50,
      "vat": 0.21
    }
  ]
}'

Creating the invoice and finalizing it at once it exactly the same as for simply creating an invoice. The only difference is the endpoint:

POST /rest/v1/invoices/create/{clientId}/finalize

Finalize an invoice

curl --location --request PUT '/rest/v1/invoices/{invoiceId}/finalize' \
--header 'Authorization: Basic YjE0NmJlZWYtNTA1NS00YjA5LTk5NDQtMTkzYzEzNWU3ODczOmQyZWE2YzgyLWU2NTktNDY0OS04NjIxLTQwYmI1NGZhZDkxYQ=='

In order to be able to download the PDF, send it to a customer, send it to an accountant,... the invoice needs to be finalized first.

PUT /rest/v1/invoices/{invoiceId}/finalize

Every invoice that has been created can be finalised.

Recall an invoice

curl --location --request PUT '/rest/v1/invoices/{invoiceId}/recall' \
--header 'Authorization: Basic YjE0NmJlZWYtNTA1NS00YjA5LTk5NDQtMTkzYzEzNWU3ODczOmQyZWE2YzgyLWU2NTktNDY0OS04NjIxLTQwYmI1NGZhZDkxYQ=='

In order edit an invoice it needs to be recalled (meaning that is needs to be in the status draft).

PUT /rest/v1/invoices/{invoiceId}/recall

Recalling an invoice is not available in some cases. However the endpoint will result in a 200 and just return the invoice with an unchanged state. You should check yourself if the state has transitioned or not.

Mark invoice sent to customer

curl --location --request PUT '/rest/v1/invoices/{invoiceId}/sent' \
--header 'Authorization: Basic YjE0NmJlZWYtNTA1NS00YjA5LTk5NDQtMTkzYzEzNWU3ODczOmQyZWE2YzgyLWU2NTktNDY0OS04NjIxLTQwYmI1NGZhZDkxYQ==' \
--data-raw ''

By default when finalizing an invoice it is not sent to the customer. The user could eventually choose to do so in Factsys or you could provide this as a feature as well by downloading the PDF version of the invoice and handling the emailing yourself. In order to keep track of which invoices have been sent to the customer you can invoke this endpoint to mark is as succesfully sent.

PUT /rest/v1/invoices/{invoiceId}/sent

Payments handling

Add a payment to an invoice

curl --location --request POST 'http://localhost:8082/rest/v1/invoices/12/payments' \
--header 'Authorization: Basic YjE0NmJlZWYtNTA1NS00YjA5LTk5NDQtMTkzYzEzNWU3ODczOmQyZWE2YzgyLWU2NTktNDY0OS04NjIxLTQwYmI1NGZhZDkxYQ==' \
--data-raw '{
  "amount": 100.00,
  "date": "2020-04-13T12:13:00.164Z"
}'

Registering payments on an invoice is really simple. The only required field is the amount that has been paid. The date will by default set to now if not specified. Future dates are not allowed!

POST /rest/v1/invoices/{invoiceId}/payments

Field Required(✓)/Optional(?) Info
amount  ✓ The amount the user paid
date ?  The date of the payment. If not provided it will be set to now.
communication  ? The communication the payer has specified on the wire transfer. For online payments you can leave this field empty.
sendEmails  ? This field is not mandatory. If not specified emails will be sent out within user's company to all administrtors that a new payment is received. If you specify this flag and put it false no emails will be sent out. Most often for direct payments in a shop order you want to put this flag on false to avoid spamming of the inboxes.

Edit a invoice payment

Editing the invoice payment is similar to the payment creation. Exactly the same fields are allowed for editation as the ones in the creation. You can also skip update emails being sent by setting the sendEmails flag to false.

PUT /rest/v1/invoices/{invoiceId}/payments/{paymentId}

Delete invoice payment

curl --location --request DELETE 'http://localhost:8082/rest/v1/invoices/12/payments/1' \
--header 'Authorization: Basic YjE0NmJlZWYtNTA1NS00YjA5LTk5NDQtMTkzYzEzNWU3ODczOmQyZWE2YzgyLWU2NTktNDY0OS04NjIxLTQwYmI1NGZhZDkxYQ==' \
--header 'Content-Type: application/json'

DELETE /rest/v1/invoices/{invoiceId}/payments/{paymentId}

Time Registrations

All of the time registration endpoints are available at /rest/v1/timeentries.

The time-entry object

{
  "id": 432,
  "note": "This note gives me some more info about the time entry, however this is optional for the user to provide this!",
  "time": 9000000,
  "date": "2019-12-18T10:03:44",
  "approved": true,
  "invoiced": false,
  "task": {
    "id": 543,
    "name": "Some work needed to be done",
    "internal": false,
    "invoicable": true,
    "invoiceRate": 534.23,
    "invoiceVat": 0.21,
    "client": {
      "id": 532,
      "name": "My First Client",
      "number":  "3",
      "vatNumber":  "BE0000000000",
      "netInvoice":  "30",
      "address": {
        "street": "Grote Markt 1",
        "postal": "1000",
        "city": "Brussels",
        "country": "BE"
      },
      "email": "test@testcompany.com",
      "contactPerson": "John Doe",
      "phone": "+32477001122",
      "currency":  {
        "sign": "€",
        "shortName": "EUR",
        "name": "EURO"
      }, 
      "language": "nl", 
      "defaultRate": 112.00,
      "company": {
        "id": 8891,
        "name": "Test Company",
        "logo": "https://botw-pd.s3.amazonaws.com/styles/logo-thumbnail/s3/032018/untitled-1_402.png?9B3JAUEkoVDD_uwVlOJaQ57wMuBKWul_&itok=eo3I5hnx",
        "address": {
          "street": "Reyerslaan 32",
          "postal": "1000",
          "city": "Brussels",
          "country": "BE",
          "vatNumber": "BE0000000001",
          "language": "nl",
          "currency": {
            "sign": "€",
            "shortName": "EUR",
            "name": "EURO"
          }
        }      
      }
    }
  }
}
Field Required(✓)/Optional(?) Info
id The identifier of the time entry will always be available, after retrieving a time entry this is the key data to be used for further operations on the item.
date The date to which this time entry is linked
note ? The note is a way for the user to declare a bit more (for himself) on what work has been performed. However the note is optional.
time The time is always in milliseconds, this is the time a user claims to have spent on a certain task.
approved If the entry has been approved by an admin or not. Entries created by an admin will always be approved automatically. Approved time entries can still be edited by the user, however they will automatically go back to approved=false. This flag cannot be set in creation or update of the time entry! See the time registration management API's for approving a time entry.
invoiced If the entry has been invoiced or not. Invoiced time entries can no longer be edited by any user (not even admins). This flag cannot be set in creation or update of the time entry! See the time registration management API's for approving a time entry.
task The task that is linked to this time entry.
task.internal If the internal flag is set to true the task will not have a client relation.
task.invoicable ? If the task can be invoiced, for internal tasks info won't be available.
task.invoiceRate ? The rate against which can be invoiced. For both internal tasks and tasks that cannot be invoiced this info won't be available.
task.invoiceVat ? The VAT percentage to be applied on the rate when invoiced. For both internal tasks and tasks that cannot be invoiced this info won't be available.
task.client ?  Only available if the task is not internal. More details on which client info if always available and which only sometimes (optional) is explained in the Client section!

Retrieving Time Entries

By day

Get all the time entries for one day

curl -X GET \
  /rest/v1/timeentries/2019/12/22 \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmYWN0c3lzX21vYmlsZV9hcHAiLCJpc3MiOiJkaXJrQGFwcDMuYmUiLCJleHAiOjE1MzgzMzYzNjh9.dNkyBm2F70FYyiJUntf-ftTAU3cpf1f_pCrDyWXY0NA'
    @GET("/rest/v1/timeentries/{year}/{month}/{day}")
    Call<List<TimeEntry>> getTimeEntriesForDay(@Path("year") int year, @Path("month") int month, @Path("day") int day);

The endpoint for the day-by-day retrieval is [GET] /rest/v1/timeentries/{year}/{month}/{day}. This call will result in a list of TimeEntry objects. All required and optional fields will be filled in a GET request. The optional fields will be missing when not available.

Search all the time entries in a certain period

curl -X GET \
  /rest/v1/timeentries/search?from=1564624800000&until=1566655200000 \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmYWN0c3lzX21vYmlsZV9hcHAiLCJpc3MiOiJkaXJrQGFwcDMuYmUiLCJleHAiOjE1MzgzMzYzNjh9.dNkyBm2F70FYyiJUntf-ftTAU3cpf1f_pCrDyWXY0NA'
    @GET("/rest/v1/timeentries/search")
    Call<List<TimeEntry>> seachTimeEntries(@Query("from") long from, @Query("until") long until, @Query("taskId") Integer taskId);
Query Parameter Type  Required(✓)/Optional(?) Info
from Long/Bigint  ✓ The search starting time (oldest time value) in milliseconds
until Long/Bigint  ✓ The search ending (including) time (newest time value) in milliseconds
taskId Integer  ? To only search for time entries that are created for a certain task.

The endpoint for the day-by-day retrieval is [GET] /rest/v1/timeentries/search. This call will result in a list of TimeEntry objects. All required and available fields will be filled in a GET request. The optional fields will be missing when not available.

Create

Form Parameter Type  Required(✓)/Optional(?) Info
date Long/Bigint  The date in milliseconds since 1970 on which the time entry took place/started
note  String ?
time Long/Bigint  The time in milliseconds, this is the duration of the time entry
task.id Integer  The task to which this time entrywill be linked

The endpoint for the creating a time entry is [POST] /rest/v1/timeentries.

Update

Form Parameter Type  Required(✓)/Optional(?) Info
date Long/Bigint  The date in milliseconds since 1970 on which the time entry took place/started
note  String ?
time Long/Bigint  The time in milliseconds, this is the duration of the time entry
task.id Integer  The task to which this time entrywill be linked

The endpoint for the creating a time entry is [PATCH] /rest/v1/timeentries/{id}.

Delete

curl -X DELETE \
  /rest/v1/timeentries/543 \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmYWN0c3lzX21vYmlsZV9hcHAiLCJpc3MiOiJkaXJrQGFwcDMuYmUiLCJleHAiOjE1MzgzMzYzNjh9.dNkyBm2F70FYyiJUntf-ftTAU3cpf1f_pCrDyWXY0NA'
    @GET("/rest/v1/timeentries/{id}")
    Call<ResponseBody> getTimeEntriesForDay(@Path("id") int id);

The endpoint for the deletion is [DELETE] /rest/v1/timeentries/{id}.