QicsMilestones REST API



Users of QicsMilestones can create their own client applications that access QicsMilestones data programmatically via REST API.


General information about API can be found here.


API exists in several versions, so that older client applications remain functional, while new improvements and changes can be introduced to the latest version.
Supported versions are named at the root of the API documentation website. At the moment of this document writing there are two versions available:

Version 3 – is the version under development and can change over time.
Version 2 – is the latest version that is not changing anymore and is maintained to work the same way as it was working at the moment when it was frozen. This is the version that client applications should be addressing at the moment of this document writing.

API Metadata

For each API version, the metadata describing the structures and data types can be found here:

For example, for the currently stable version 2.


Basic authentication is used. Username and password is passed in request header:

Header name



Format: “Basic user:password”

User and password parts are Base 64 encoded.

Example in C#:

string username = Convert.ToBase64String(Encoding.UTF8.GetBytes(UserName));
string password = Convert.ToBase64String(Encoding.UTF8.GetBytes(Password));

string auth = string.Format("{0}:{1}", username, password);
string enc = Convert.ToBase64String(Encoding.ASCII.GetBytes(auth));
string cred = string.Format("{0} {1}", "Basic", enc);

RequestMessage.SetHeader(System.Net.HttpRequestHeader.Authorization.ToString(), cred);


Client applications are recognized by QicsMilestones using API keys. Each client application must have its own API key assigned by QICS. For new client applications API keys can be requested at the following email address: support@qics.nl

Provide a name and a short description of the client application when requesting new API key. Client application must identify itself with the assigned key using request header:

Header name



Key provided by QICS that uniquely identifies each web api client.

Tenant Number

Tenant is the term used for the environment within QicsMilestones into which the user has access. Each user can have access to multiple tenants, that’s why each request (made under a certain user) must also identify the tenant in the request header:

Header name



Tenant number into which the request is addressed.

Tenant ID can be seen in the address field of the web browser when user is logged into QicsMilestones website:

Here the Tenant number is 3.

Detail Entities

Most objects are exposed in the REST-ful way, identified by the object name in the URL. For example, to work with a Customer object, the following HTTP verbs are supported:

Here, the client must know the internal ID (unique identifier) of each customer to get it from the API and subsequently manipulate it. To find out the ID of each object that needs to be manipulated, client must make API query that returns one or more objects from which the client can get the IDs.

Documentation for all entities can be found here.

API Queries with Overview Entities

API queries can be used to obtain a set of objects such as customers, that satisfy certain query criteria. API has an “Overview” version of endpoint for most objects. This endpoint can be used to make complex queries using the properties exposed by the given object in ODATA format. Objects that do not have their “Overview” counterpart can be queried directly using the HTTP GET verb.

For example, for the Customer object there is OverviewCustomer endpoint:

Summary of the differences between detail and overview entities are:

1. Detail version of the object
    • Exposes only properties owned by the object
    • Allows selection only using primary key property
    • Supports Selects, Inserts, Updates and Deletes
2. Overview version of the object
    • Object name prefixed with Overview
    • Exposes limited set of object’s properties together with number of properties of related objects.
    • Allows querying based on all exposed properties
    • Supports only selection of objects.
    • Insert, Update and Delete is not possible.

Usually objects are selected first by querying the overview endpoint, using a more or less complex query. Then iterate through the set to display the objects and select single object based on given ID from the detail endpoint to modify the object if necessary.


When using API Queries (“Overview” endpoints) client must apply paging to each request. That puts a maximum on the number of objects that can be obtained in one call. If the client does not provide the paging information with a request, only the first 50 objects from the entire set are returned.

Paging information must be provided by the client in ODATA format.

EntityState Endpoint

QicsMilestones objects such as Customers, Employee, Items, etc. are often synchronized to/form external systems, such as CRM, or bookkeeping software. In this scenario, it is important to keep track of the synchronization state of the given object (e.g. Customer) for each external system.

External clients should use the EntityState endpoint to report the synchronization state for each object that is a subject of the synchronization. Because each QicsMilestones object can be synchronized from/to different external systems, multiple EntityState records can be linked with each QicsMilestones object. For example:

Synchronization client should after each synchronization step create, or update EntityState record with the latest results of a synchronization attempt (success, or failure).

Object Properties:

Property name





Primary key



Identifies the company within which the object was synchronized



Custom name identifying a single data source



One of the predefined entity types: General | CustomerCategory | Company | CostCenter | Currency | Customer | Department | Employee | Function | InvoiceLayout | InvoiceMutationReason | IrregularityPercentage | Item | ItemGroup | ItemMainGroup | Material | Project | ProjectBudget | ProjectContract | ProjectMember | ProjectTask | TimesheetEntry | User | VatCode | Rate | ProjectCategory | ProjectInstallment | ProjectItem | MaterialCompany | EmployeeCompany | ItemCompany | CustomerCompany | Role | UserPreferences | ExtendedPropertyGroup | ExtendedProperty | InvoiceHeader | Competence | ProjectState | ProjectTaskBreakdown | EmailTemplate | ReportRole | InvoiceLine | PaymentCondition | ItemCompanyCustomerCompany | EmployeeCompanyItemCompany | EntityState.

Messages that are not bound to a specific existing entity type, should use type: General



Id of the entity in QicsMilestones



Key uniquely identifying the synchronized object in the data source



Client’s date and time of the synchronization attempt



True if the synchronization attempt failed



Description of the synchronization failure

EntityState keeps only a single record for unique combination of following properties:

- CompanyId
- DataSource
- EntityType
- EntityId

An object in one company can thus only hold one state information for each external system (data source).

EntityId can be set to 0, for situations when object does not exist yet in QicsMilestones and insert was not successful. 

Synchronization client can record general synchronization problem (e.g. authentication to external system) by setting EntityType to General and setting EntityId to 0.

When multiple error messages are to be recorded for single QM object, the web API client has to merge them into one message and post to EntityState object.

Example in Visual Studio and C#

Service Reference

In visual studio, a service reference can be created to generate proxy classes for accessing the API:


Service Object

After the service reference is established the service object can be instantiated and initialized in the client application:

// Web service object, created from the web service reference
var uri = "https://qicsmilestones.qics.nl/api/v2/odata";
QicsMilestones.QicsMilestonesContainer service;
service = new QicsMilestones.QicsMilestonesContainer(new Uri(uri));
service.IgnoreMissingProperties = true;

Request Headers

The request headers can be filled in in an event handler created for the SendingRequest2 event of the service object:

// Add event handler that fill in the request headers when the request is being sent
service.SendingRequest2 += (sender, e) =>
    string username = Convert.ToBase64String(Encoding.UTF8.GetBytes(UserName));
    string password = Convert.ToBase64String(Encoding.UTF8.GetBytes(Password));
    string authHeader = System.Net.HttpRequestHeader.Authorization.ToString();

    string auth = string.Format("{0}:{1}", username, password);
    string enc = Convert.ToBase64String(Encoding.ASCII.GetBytes(auth));
    string cred = string.Format("{0} {1}", "Basic", enc);

    e.RequestMessage.SetHeader(authHeader, cred);
    e.RequestMessage.SetHeader("api-key", ApiKey);
    e.RequestMessage.SetHeader("tenant-id", TenantID.ToString());

Querying the API Entities

With the service object instantiated and properly initialized, client can query the Overview endpoints to get one or more QicsMilestones objects:

var overviewCustomerQuery = service.OverviewCustomer.Where(
    p => p.CompanyCode == CompanyCode
    && p.CustomerCode == customerCode.Trim()).Skip(pageSize*pageNr).Take(pageSize);

Modifying Existing Entities

From the query object created using Overview endpoint, we can enumerate each object in the set. Here we just fetch the first one:

var overviewCustomer = overviewCustomerQuery.ToList().FirstOrDefault();

The overviewCustomer object is read-only. It cannot be used to modify data in QicsMilestones. In order to have an object that can be used to modify existing customer we need to fetch Customer entity from the API, by providing ID value. Note, that in the case of Customer object the ID property is called CustomerCompanyId (not CustomerId) this is because Customer is one of the objects that can be shared between multiple companies.

var customer = service.Customer.Where(
    e => e.CustomerCompanyId == overviewCustomer.CustomerCompanyId)

Modify the Customer object:

customer.VisitAddressAddress1 = "My new address 2";

After this call the Customer object in QicsMilestones is modified with the new value in the visit address field.

Hebt u meer vragen? Een aanvraag indienen


Mogelijk gemaakt door Zendesk