VismaBouwsoftAPICalls | Class (Inherits from Object) |
Visma Bouwsoft API - Version 1
Which server to call
Calling the API
API Calls
Which server to call
When making a call to the Visma Bouwsoft API, we always call the server that we get back from the authentication server when we get a new Accesstoken
For instance:
https://authenticationserver.bouwsoft.be/api/v1/Authorize/AccessToken
(where authenticationserver is one of the available authenticationservers
See https://support.bouwsoft.be/manual/api/?page=connection)
with the following HeaderParameters
Parameter name | Type | Description |
---|---|---|
Clientnr | Integer | Clientnr of the Visma Bouwsoft client |
RefreshToken | String | RefreshToken received from the Visma Bouwsoft activation system |
The answer we receive is in JSON format and contains
Parameter name | Type | Description |
---|---|---|
Accesstoken | String | AccessToken you need to use the Visma Bouwsoft API |
ServerIP | String | The IP address of the server you need to contact to access the data for this client |
ServerName | String | The name of the server you need to contact to access the data for this client (referenced below as myserver) |
Layout | String | A = Archisoft, B = Bouwsoft, G = Groensoft |
ValidUntil | String | The time until which the current Accesstoken will remain valid |
Example:
{
"AccessToken": "btffWxfAOOzSfPDVtfzRqkGkfPtPVIfUfPkUzbjjkIAfmlDUrn",
"ServerIP": "95.138.166.117",
"ServerName": "Osiris.bouwsoft.be",
"Layout": "B",
"ValidUntil": "2017–09–05T15:01Z"
}
Calling the API
API Calls
The Visma Bouwsoft API gets called with the following (possible) Parameters
HeaderParameters
Parameter name | Type | Description |
---|---|---|
Accesstoken | String | AccessToken received from the Visma Bouwsoft activation system |
Clientnr | Integer | Clientnr of the Visma Bouwsoft client |
PathParameters
/ApiMethodName/{id}
If you want to get the data for one specific item, you call the API with the id of the item you want to receive
For instance:
https://myserver.bouwsoft.be/api/v1/Addresses/167046
QueryParameters
If you want to modify the list of data you want to receive, you can use the following parameters
Parameter name | Type | Description |
---|---|---|
columns | String | The names of the fields you want in the result set ( seperated by | ) |
filter | String | The filter you will use on the list |
limit | Integer | The maximum number of lines you want to see in the result set |
nextid | Integer | The id of the item we want as start of the result set. This parameter is incompatible with offset |
offset | Integer | The offset of the displayed list compared to the start of the result set. This parameter is incompatible with nexid |
sort | String | The field names you will use to sort the list ( seperated by | , use -fieldname for descending ) |
distinct | Boolean | Set this field to true if you want to do a distinct select on the selected columns |
PS:
Only the columns parameter can be used if the API call was made with a PathParameter
Filtering data, sorting data and selecting columns
The filter query parameter will be used to filter the result.
Multiple filters are seperated by ‘and’ or ‘or’.
Different operators can be used for each column.
If we use a value as the parameter for the filter instead of a comparison (for instance: https://myserver.bouwsoft.be/api/v1/Projects/?filter=201500575 ), the api will look across all fields of the same type (text fields in case of text and number fields in case of a passed number)
If we want to filter data it works as follows :
https://myserver.bouwsoft.be/api/v1/Projects/?filter=pnr%20gt%20201500575
Through this call we will get all projects with a pnr greater than 201500575
The filter is URL Encoded (%20 is a space)
You can use the following operators in a filter
Operator | Description |
---|---|
gt | Greater than |
ge | Greater than or equal to |
eq | equal to |
lt | smaller than |
le | smaller than or equal to |
ne | not equal to |
has | contains |
Sorting works as follows:
https://ra.bouwsoft.be/api/v1/Projects/?filter=pnr%20gt%20201500575&sort=zipcode%7Cname
This command will get all projects with a number greater than 20150575 and sort the sort by zipcode and name
The filter and sort are URL Encoded (%20 is a space and %7C is a | )
sort ascending: sort=columname
sort descending: sort=-columnname
If we only want certain columns in the result we can do it the following way:
https://ra.bouwsoft.be/api/v1/Projects/?filter=pnr%20gt%20201500575&sort=zipcode%7Cname&columns=pnr%7Cname%7Caddress%7Czipcode%7Ccity%7Cts_lastupdate
Here we filter for the projects with pnr greater than 201500575, sort it on zipcode and name and request only the following columns in the output: Name, Address, zipode, city, ts_lastupdate
HTTP Response Codes
Code | Description |
---|---|
200 OK | Successful response for GET and PUT (Update/Replace) requests. |
201 Created | Successful response for POST (Create) requests. |
204 No Content | Successful response for DELETE requests. |
400 Bad Request | Failure response to a request with a malformed body. |
401 Unauthorized | Failure response for missing or invalid authentication credentials. |
404 Not Found | Failure response because the requested resource is invalid. |
405 Method Not Allowed | This Resource does not support this method |
500 Internal Server Error | No Response received from this call |
501 Not Implemented | This Resource does not support this method |
503 Service Unavailable | The server is temporarily unable to service your request |
Examples of queryparameters
columns
The names of the fields you want in the result set (seperated by |) . No single quotes are needed.
For instance GET_Addresses.
With queryparameters
* columns = id|clientnr|name
* limit = 3
Result:
{
"Content-Range": "0–2/*",
"NextId": "167079",
"Records": [
{
"id": 167070,
"clientnr": 20170058,
"name": ""
},
{
"id": 167071,
"clientnr": 20170059,
"name": ""
},
{
"id": 167078,
"clientnr": 20170064,
"name": ""
}
]
}
filter
The filter you will use on the list.
For instance GET_Addresses
With queryparameters
* filter = name has ‘neorej’ and zipcode has ‘8730’
Result:
{
"Content-Range": "0–0/1",
"Records": [
{
"id": 167090,
"clientnr": 20170076,
"suppliernr": 0,
"salutation": "Dhr.",
"name": "Stpilf Neorej",
"address": "Hoornstraat 9",
"countrycode": "BE",
"zipcode": "8730",
"city": "Beernem",
"state": "West-Vlaanderen",
"telephone1": "",
"telephone2": "",
"fax": "",
"gsm": "+32 (0) 484 23 79 29",
"email": "",
"vatnr": "",
"vatregistered": true,
"date_created": "2017–09–11",
"ts_lastupdate": "2017–09–11 12:31:28"
}
]
}
For instance GET_LedgerNumbers
With queryparameters
* filter = ‘heftruck’
Result:
{
"Content-Range": "0–0/1",
"Records": [
{
"id": 1298,
"nr": 371,
"description": "Afschrijving Heftruck"
}
]
}
limit
The maximum number of records you want to see in the result set. Default is 200, max is 500. Higher numbers will result in slower respons times.
For instance GET_LedgerNumbers
With queryparameters
* limit = 3
Result:
{
"Content-Range": "0–2/*",
"NextId": "190",
"Records": [
{
"id": 26,
"nr": 1,
"description": "Eigen vermogen"
},
{
"id": 59,
"nr": 2,
"description": "Oprichtingsk., vaste activa, vord. > 1 j"
},
{
"id": 164,
"nr": 3,
"description": "Voorraden en bestellingen in uitvoering"
}
]
}
nextid
The id of the item we want as start of the result set. This parameter is incompatible with offset.
For instance GET_LedgerNumbers
With queryparameters
* nextid = 190
* limit = 3
Result:
{
"Content-Range": "3–5/1297",
"NextId": "734",
"Records": [
{
"id": 190,
"nr": 4,
"description": "Vorderingen en schulden < 1 jaar"
},
{
"id": 341,
"nr": 5,
"description": "Geldbeleggingen en liq. midd."
},
{
"id": 368,
"nr": 6,
"description": "Kostenrekeningen"
}
]
}
offset
The offset of the displayed list compared to the start of the result set. This parameter is incompatible with nexid.
For instance GET_LedgerNumbers
With queryparameters
* offset = 3
* limit = 3
Result:
{
"Content-Range": "3–5/*",
"NextId": "734",
"Records": [
{
"id": 190,
"nr": 4,
"description": "Vorderingen en schulden < 1 jaar"
},
{
"id": 341,
"nr": 5,
"description": "Geldbeleggingen en liq. midd."
},
{
"id": 368,
"nr": 6,
"description": "Kostenrekeningen"
}
]
}
sort
The field names you will use to sort the list ( seperated by | , use -fieldname for descending ).
For instance GET_OrderLines
With queryparameters
* sort = orderid|linenr
* nextid = 141
* limit = 3
Result:
{
"Content-Range": "101–103/43612",
"NextId": "144",
"Records": [
{
"id": 141,
"orderid": 22,
"linenr": 1,
"group": "Général 1",
"nr1": "11",
"nr2": "",
"description": "une nouvelle poste",
"unit1": "5 l",
"unit2": "",
"quantity1": 80,
"quantity2": 1,
"quantity3": 1,
"quantity4": 1,
"quantity_total": 80,
"price_unit": 30.460000000000001,
"price_total": 2436.800000000000182,
"vatcode": "6",
"vatpercent": 6,
"vatcocontractor": false,
"ledger": 70000
},
{
"id": 142,
"orderid": 22,
"linenr": 2,
"group": "Général 1",
"nr1": "11",
"nr2": "",
"description": "une nouvelle poste",
"unit1": "5 l",
"unit2": "",
"quantity1": 90,
"quantity2": 1,
"quantity3": 1,
"quantity4": 1,
"quantity_total": 90,
"price_unit": 30.460000000000001,
"price_total": 2741.400000000000091,
"vatcode": "6",
"vatpercent": 6,
"vatcocontractor": false,
"ledger": 70000
},
{
"id": 143,
"orderid": 22,
"linenr": 3,
"group": "Général 1",
"nr1": "11",
"nr2": "",
"description": "une nouvelle poste",
"unit1": "5 l",
"unit2": "",
"quantity1": 36,
"quantity2": 1,
"quantity3": 1,
"quantity4": 1,
"quantity_total": 36,
"price_unit": 30.460000000000001,
"price_total": 1096.559999999999945,
"vatcode": "6",
"vatpercent": 6,
"vatcocontractor": false,
"ledger": 70000
}
]
}
distinct
Set this field to true if you want to do a distinct select on the selected columns.
For instance GET_Products
With queryparameters
* sort = list
* offset = 12
* limit = 3
* columns = list
* distinct = true
Result:
{
"Content-Range": "12–14/*",
"Records": [
{
"list": "ART GALLERY"
},
{
"list": "ARTIKELLIJST DECOVAN"
},
{
"list": "ASEA BROWN BOVERI"
}
]
}
Response Codes
200 OK
201 CREATED
204 NO CONTENT
400 BAD REQUEST
columns
filter
General
Field Type Specific
BOOLEAN
CURRENCY
CURRENCYHD
DATE
INTEGER
TEXT
limit
nextid
offset
sort
401 UNAUTHORIZED
404 NOT FOUND
405 METHOD NOT ALLOWED
501 NOT IMPLEMENTED
503 SERVICE UNAVAILABLE
200 OK
Successful response for GET and POST requests.
The reponse is the data you just asked for in JSON format
for instance:
answer to https://myserver.bouwsoft.be/api/v1/Addresses with a queryparameter limit that has a value of 2
{
"Content-Range": "0–1/*",
"NextId": "42443",
"Records": [
{
"id": 11342,
"clientnr": 20160049,
"suppliernr": 0,
"salutation": "SPRL",
"name": "\"CONSTRUCTION\" SPRL",
"address": "Rue Albert Ier 11",
"countrycode": "BE",
"zipcode": "5500",
"city": "Dinant",
"state": "Namur",
"telephone1": "",
"telephone2": "",
"fax": "",
"gsm": "",
"email": "",
"vatnr": "BE 0111.111.111",
"vatregistered": true,
"date_created": "2013–10–21",
"ts_lastupdate": "2017–08–17 10:08:12"
},
{
"id": 6424,
"clientnr": 20160048,
"suppliernr": 0,
"salutation": "",
"name": "’t Bouwbedrijf",
"address": "Verboekt 17",
"countrycode": "BE",
"zipcode": "3980",
"city": "Tessenderlo",
"state": "Limburg",
"telephone1": "012 34 56 78",
"telephone2": "",
"fax": "+32 (0) 12 34 56 79",
"gsm": "",
"email": "",
"vatnr": "BE 0111.111.112",
"vatregistered": true,
"date_created": "2013–10–21",
"ts_lastupdate": "2017–02–23 10:02:29"
}
]
}
201 CREATED
Successful response for PUT requests.
The reponse is the data you just inserted in JSON format with the creation date and a timestamp
for instance:
answer to https://myserver.bouwsoft.be/api/v1/Addresses with a queryparameter colums that has the following value
name=’SpikeAPI‘,address=’Groothof 23’,countrycode=’BE‘,zipcode=’8200’,city=’brugge’
{
"RecordChanges": [
{
"id": 167043,
"clientnr": 0,
"suppliernr": 0,
"salutation": "",
"name": "SpikeAPI",
"address": "Groothof 23",
"countrycode": "BE",
"zipcode": "8200",
"city": "Brugge",
"state": "West-vlaanderen",
"telephone1": "",
"telephone2": "",
"fax": "",
"gsm": "",
"email": "",
"vatnr": "",
"vatregistered": true,
"date_created": "2017–09–05",
"ts_lastupdate": "2017–09–05 08:44:27"
}
]
}
204 NO CONTENT
Successful response for DELETE requests.
The reponse is empty since the data has just been deleted.
Therefore the only answer to a delete request is the status code.
PS:
If you try to delete something that does not exist, you will also get a 204 NO CONTENT
The Visma Bouwsoft API does not consider it an error if you try to delete something that does not exist
The end result will always be that the data does indeed not exist after the API Call
400 BAD REQUEST
If there is something wrong with the request, the response status and code is 400
Examples of bad requests:
columns
General
columns: name
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Column not found: name”}
columns: address_id=27332
{“Code”: “400”,“Message”: “Bad Request”,“Description”: “Can’t update address_id”}
POST on a record that is supposed to be connected to another record where corresponding id does not exist
bv. POST op AddressContacts met
columns: contact_name=’SpikeAPI2’,address_id=12
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“address_id must exist”}
Field type specific
BOOLEAN
columns: senttoaccountingsoftware=2
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Invalid booleanvalue in Column senttoaccountingsoftware: 2 (True or False expected)”}
CURRENCY
columns: cashdiscount_amount=’a’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Invalid Currencyvalue in Column cashdiscount_amount: ‘a’”}
CURRENCYHD
columns: price_cost=’Kraan + Operator K31/35 Forfait 4u’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Invalid CurrencyHDvalue in Column price_cost: ‘Kraan + Operator K31\/35 Forfait 4u’”}
DATE
columns: purchasedate=’a’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Invalid Datevalue in Column purchasedate: a”}
DOUBLE
columns: quantity=’a’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Invalid Doublevalue in Column quantity: ‘a’”}
INTEGER
columns: constructionyear=’a’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Invalid Integervalue in Column constructionyear: ‘a’”}
TEXT
columns: license_plate=0
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Invalid Stringvalue in Column license_plate: 0”}
filter
General
any filter on a request with an id
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“filter found on a request for 1 single id”}
filter: name has ‘gino’ and
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Filter ends with AND”}
filter: name has ‘gino’ or
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Filter ends with OR”}
filter: contactname has ‘Gino’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Unknown field: ‘contactname’”}
filter: contact_name contact_email has ‘gino’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“2 fields in 1 filterexpression are not allowed”}
filter: contact_name has Gino’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Invalid number of quotes in filter”}
filter: (contact_name has ‘Gino’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Every opening bracket needs a closing bracket”}
filter: (contact_name has ‘gino’) ( or contact_email has ‘gino’)
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Invalid position for an opening bracket”}
filter: (contact_name has ‘gino’ or ) ( contact_email has ‘gino’)
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Invalid position for a closing bracket”}
filter: 5
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Value found without Field or Operator in this filterexpression”}
filter: eq ‘5’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Operator eq found without a Field”}
filter: address_id not eq 400
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“‘not eq’ is not a valid operator”}
Field type specific
BOOLEAN
filter: vatregistered has false
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“EQ and NE are the only allowed operators in a filter on a boolean”}
filter: vatregistered eq null
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“NULL value not allowed in a filter on a boolean”}
filter: vatregistered eq ‘BE’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“‘BE’ is not a boolean”}
DATE
filter: ts_lastupdate has ‘2017–07–10 15:38:23’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“‘has’/’has not’ operator not allowed in a filter on a date”}
filter: ts_lastupdate eq NULL eq NULL
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“NULL value not allowed in a filter on this datefield”}
filter: ts_lastupdate lt NULL
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“EQ and NE are the only allowed operators to check on NULL”}
filter: date_created eq ‘test’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“‘test’ is not a date”}
INTEGER
filter: address_id has ‘5’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“‘has’/’has not’ operator not allowed in a filter on an integer”}
filter: address_id eq NULL
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“NULL value not allowed in a filter on an integer”}
filter: address_id eq ‘0’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“‘0’ is not an integer”}
TEXT
filter: name eq NULL
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“NULL value not allowed in a filter on a textfield”}
filter: contact_name has 5
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“5 is not a textvalue”}
limit
a limit on a request with an id
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“limit found on a request for 1 single id”}
limit: ‘test’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Limit is not valid: ‘test’”}
limit: 0
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Requested limit is smaller than 1”}
limit: 1000
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Requested limit is larger than 500”}
nextid
a nextid on a request with an id
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“nextid found on a request for 1 single id”}
nextid: ‘test’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Invalid NextId”}
offset
any sort on a request with an id
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“sort found on a request for 1 single id”}
an offset on a request with an id
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“offset found on a request for 1 single id”}
offset: ‘test’
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Offset is not valid: ‘test’”}
offset: –10
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Requested offset is negative”}
offset: 1 and nextid: 29
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“offset can’t be combined with nextid”}
sort
sort: zipcode|name|planet
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Sort Field not found: planet”}
sort: zipcode|name|zipcode
{“Code”:“400”,“Message”:“Bad Request”,“Description”:“Sort Field defined twice: zipcode”}
401 UNAUTHORIZED
Failure response for missing or invalid authentication credentials.
If you forget to send the Authentication, the response status and code is 401
(401 Unauthorized)
The possible responses in this case are:
{“Code”:“401”,“Message”:“Unauthorized”,“Description”:“No AccessToken found in request”}
{“Code”:“401”,“Message”:“Unauthorized”,“Description”:“No ClientNr found in request”}
{“Code”: “401”,“Message”: “Unauthorized”,“Description”: “This accesstoken is not valid in combination with the given clientnr”}
{“Code”: “401”,“Message”: “Unauthorized”,“Description”: “This accesstoken is expired and will be removed shortly”}
404 NOT FOUND
Failure response because the requested resource is invalid.
If there is no record that has the requested id the HTTP response status and code is 404
(404 Not found)
example: https://myserver/api/v1/Addresses/12
{“Code”:“404”,“Message”:“Not Found”,“Description”:“Id not found: 12”}
If there is no record that has the requested nextid the HTTP response status and code is 404
(404 Not found)
example: https://myserver/api/v1/Addresses/ and the queryparameter nextid is 12
{“Code”:“404”,“Message”:“Not Found”,“Description”:“NextId not found: 12”}
Another possible reason is that you are using http instead of https
When using http, you get 404 with the following reply (in http)
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /api/v1/Machines was not found on this server.</p>
<hr>
<address>Apache/2.4.18 (Ubuntu) Server at ra.bouwsoft.be Port 80</address>
</body></html>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>405 Method Not Allowed</title>
</head><body>
<h1>Method Not Allowed</h1>
<p>The requested method PUT is not allowed for the URL /api/v1/Projects/558.</p>
<hr>
<address>Apache/2.4.18 (Ubuntu) Server at ra.bouwsoft.be Port 80</address>
</body></html>
405 METHOD NOT ALLOWED
You are trying to use a method that has not been implemented (the HTTP PATCH method)
This Resource does not support this method
Visma Bouwsoft API does not support PATCH. If you try to use it, you get the 405 Method not allowed message below (JSON)
{“Code”:“405”,“Message”:“Method Not Allowed”,“Description”:“This Resource does not support this method: PATCH”}
PS:
Instead of using PATCH, the Visma Bouwsoft API works with the PUT method to update records
If you want to PATCH the data, you use the PUT method in combination with the QueryParameter columns
For instance:
https://myserver.bouwsoft.be/api/v1/Addresses/165982
with the queryparameter columns that has the value: city=’Oostende‘,salutation=’Mrs.’
500 Internal Server Error
No Response received from this call
Visma Bouwsoft API was not able to answer this call
For instance:
https://ra.bouwsoft.be/api/v1/Addressen
If you try this, you get the 501 Not Implemented message below
{“Code”:“501”,“Message”:“Method Not Implemented”,“Description”:“This Method is not implemented: api\/v1\/Addressen”}
501 NOT IMPLEMENTED
This Resource does not support this method
Visma Bouwsoft API does not have this method available
For instance:
https://ra.bouwsoft.be/api/v1/Addressen
If you try this, you get the 501 Not Implemented message below
{“Code”:“501”,“Message”:“Method Not Implemented”,“Description”:“This Method is not implemented: api\/v1\/Addressen”}
503 SERVICE UNAVAILABLE
The server is temporarily unable to service your request due to maintenance downtime or capacity problems.
Please try again later.
This usually lasts only a couple of seconds.