API Information ¶
This API lets you interact with your Webling Database.
The API is available at the following URL:
https://<yourdomain>.webling.ch/api/1/
See the Examples section on how to use the API.
The authentication is done by passing an API-Key. As an Administrator you can generate your API-Key in the Web App (Administration > API).
To authenticate, add the apikey parameter (/api/1/member?apikey=<your_api_key>
) or add an apikey header (apikey: <your_api_key>
) to every request.
Response body is in JSON format (except images/files). There are different JSON structures that are being used by the API:
All objects have the following basic structure:
{
"type": "objecttype",
"meta": {
"created" => "2018-04-06 01:20:04",
"lastmodified" => "2017-05-23 11:43:54"
},
"readonly": false,
"properties": {},
"children": {},
"parents": [],
"links": {}
}
These are the properties you find in the basic object structure:
Property |
Description |
type string |
The object type, e.g member or membergroup |
meta object |
Contains the creation and last modify date of the object. For members it also contains the lat and lng of the address if available. |
readonly boolean |
Is set to true if the object is read only for the user, otherwise false |
properties object |
Contains all object data (mixed datatypes), see below for a list of datatypes |
children object |
Contains a list of objects with a list of all children, grouped by type. This property is read-only, you can change the parent attribute of the children objects instead. |
parents array |
A list of all parent (IDs). Every object must have at least one parent. |
links array |
A list of all linked objects (IDs). |
Every property in properties
has a datatype, which is one of the following:
Datatype |
Description |
text |
String with a maximum size of 255 characters |
longtext |
String with a maximum size of 2 Gb |
bool |
Boolean value, true or false . |
enum |
Value out of a list of predefined values. |
multienum |
Array of one or several values out of a list of predefined values. (e.g. ["foo", "bar"] ) |
numeric |
Fixed floating point value with two digits accuracy. |
int |
Integer value |
autoincrement |
Immutable integer value, unique for objects of the same type. |
date |
Date value in the format YYYY-MM-DD (eg 2008-09-17) |
timestamp |
Time and date of the form YYYY-MM-DD hh:mm:ss (eg eg 2008-09-17 16:13:42) |
file |
Contains the metadata of a file, see below |
image |
Contains the metadata of an image, see below |
binary |
Contains the metadata of a binary file, can either be an image or a file, see below |
Metadata of a file:
{
"href": text,
"size": int,
"ext": text,
"mime": text,
"timestamp": timestamp
}
Metadata of an image:
{
"href": text,
"size": int,
"ext": text,
"mime": text,
"timestamp": timestamp,
"dimensions": {
"width": int,
"height": int
}
}
Metadata of a binary:
{
"href": text,
"size": int,
"name": text,
"lastmodified": timestamp,
"hrefthumb": string
}
Lists contain object IDs (e.g: a list of member IDs):
{
"objects": [12,23,34,45]
}
Files, images or binaries are returned as binary data with the appropriate content-type (e.g. image/png, application/xyz)
Errors have the following structure:
{
"error": "Error message"
}
If possible, HTTP Response Status Codes are used:
-
200 OK: Request successful
-
201 Created: Resource has been created
-
204 No Content: The request was successful, no content is returned
-
304 Not Modified: The content has not changed
-
400 Bad Request: Invalid parameter passed
-
401 Unauthorized: Authentication failed
-
403 Forbidden: No permission to perform the request
-
404 Not Found: The resource was not found
-
413 Request Entity Too Large: try to split your request into multiple requests
-
425 Quota Exceeded: The limit of your Webling subscription was reached
-
429 Too many requests: your host has sent too many requests and has been blocked for a short period
-
500 Server Error: An internal server error occurred
-
501 Not Implemented: This call is not yet implemented
-
503 Service Unavailable: The server can not handle the request, e.g a deadlock occurred
The API currently enforces a rate limit of 500 requests per minute. If you hit the rate limit, you will get an HTTP 429 Error.
You can fetch multiple objects in one call if you get the error code 429.
The rate limit may be lowered in the future. Applications should be designed to not send more
than 50 requests per minute. You can use caching to reduce the number of requests.
See the section Track Changes / Replicate for more information about how to detect changed data
and only update these objects to reduce requests.
There are two ways to fetch multiple objects with one request:
You can request multiple objects with one call (multiget). To do so, concatenate all IDs with a comma:
/api/1/member/536,525,506,535
This also works for DELETE Requests.
When loading lists like /member
or /membergroup
by default you get object IDs only.
This is due to the fact that large stores will take a while to return all object data.
If you are dealing with a lot of data, using a caching mechanism is really recommended.
See Replicate.
You can fetch full data, instead of the IDs only, by appending the format=full
parameter.
Example:
/api/1/member?format=full
This will return all member objects (with full data).
You can also combine this with other parameters like filter
to only load certain members:
/api/1/member?format=full&filter=$parents.$id=552
This will return all members which are in the membergroup with the id 552.
You can limit the number of results with the pagination parameters:
/api/1/member?page=1&per_page=100
/api/1/member?format=full&page=4&per_page=10
All GET endpoints, which return a list of object IDs, can be filtered using the following query language. A query is
passed by the filter=
parameter.
Example:
https://demo.webling.ch/api/1/member?filter=`Vorname` = "Hans"&apikey=__YOUR_API_KEY__
Explanation of the query language syntax:
Feature |
Example |
Objects can be queried by properties (fields) |
`Name` = "Meier" or `Betrag` > 100 |
Properties containing special characters must be enclosed by backticks |
`Zweiter Name` FILTER "Mei" |
All properties can be queried by * |
* = "Müller" |
A value can be searched in several properties. Separate the properties by comma |
`Name`,`Betrag` = "Müller" |
A property can be checked if it is empty or not empty |
`Name` IS EMPTY or NOT `Betrag` IS EMPTY |
The set expression checks if a property is in a list |
`Name` IN ("Hans", "Jürg") |
Multiple conditions can be joined by AND, OR and brackets |
(`Name` = "Meier" OR `Name` = "Müller") AND `Vorname` = "Hans" |
There are some special properties you can use beside the normal properties:
Special Property |
Description |
$parents.<property> |
query a property of a parent |
$ancestors.<property> |
query a property of any ancestor. The ancestors operator is only allowed for members, membergroups, documents and documentgroups (e.g. used for search in subgroups) |
$children.<childtype>.<property> |
query a property of a child |
$links.<category>.<property> |
query a property of a link |
$readonly , $writable |
bool value indicating if the object is readonly/writable |
$label |
label of the object (usually the title) |
$id |
id of the object |
The backtick is not required for these special properties.
You can use the following comparison operators:
Operator |
Description |
Example |
< |
less than |
`PLZ` < 2000 |
<= |
less or equal than |
`PLZ` <= 2000 |
> |
greater than |
`PLZ` > 2000 |
>= |
greater or equal than |
`PLZ` >= 2000 |
= |
equal |
`Name` = "Meier" |
!= |
not equal |
`Name` != "Meier" |
FILTER |
matches all strings starting with the argument. |
`Name` FILTER "Me" matches Meier, Mettler, … |
CONTAINS |
searches whole strings for matches (Note: CONTAINS is much slower than FILTER , use FILTER whenever possible). |
`Name` CONTAINS "an" matches Andermatt, Baumann, … |
IS EMPTY |
matches all empty values (null and empty string). |
`E-Mail` IS EMPTY |
IN |
matches multiple values |
`Lizenz` IN ("Junior", "1. Liga") matches all members with Lizenz “Junior” or “1. Liga” |
WITH |
links queries of multiple properties in a linked object |
WITH $links.debitor (totalamount > 100 AND title = "Jahresrechnung") matches only if both conditions are satisfied by the same debitor object |
The following functions are available:
Name |
Description |
LOWER(<string>) |
converts a text or longtext property to lower case |
TRIM(<string>) |
trims a text or longtext property |
UPPER(<string>) |
converts a text or longtext property to upper case |
DAY(<date>) |
returns the day of a date property |
MONTH(<date>) |
returns the month of a date property |
YEAR(<date>) |
returns the year of a date property |
AGE(<date>) |
returns the age of a date property |
AGETHISYEAR(<date>) |
returns the age the person will reach at the end of this year |
BIRTHDAY(<date>, month, day) |
returns a number which can be used to sort by birthdays after the given day |
TODAY() |
returns the current date |
COUNT(<path>) |
call to order the results by the number of link, children or parents. This call is only available in the order parameter. |
The results can be sorted by the order
parameter, using the following pattern:
?order=`Vorname` ASC
?order=`Vorname` DESC, `Nachname` ASC
Special properties are also allowed:
?order=$label ASC
?order=$parents.title DESC
?order=DAY(`Geburtstag`) DESC
To filter for members, use the filter argument in the URL like this:
https://demo.webling.ch/api/1/member?filter=`Vorname` = "Hans" OR `Name` = "Weber"&apikey=__YOUR_API_KEY__
Get all members in a specific membergroup (id = 555) and sort them by “Vorname”:
member?filter=$parents.$id = 555&order=`Vorname` ASC
You can nest AND and OR conditions and use parentheses:
member?filter=`Vorname` = "Peter" OR (`Vorname` = "Hans" AND `Name` = "Meier" OR (`PLZ` > 100 AND `PLZ` <= 4000))
Get all members that are in one of the membergroups 100, 101 or 102:
member?filter=$parents.$id = 100 OR $parents.$id = 101 OR $parents.$id = 102
member?filter=$parents.$id IN (100, 101, 102)
Get all members that are in a membergroup with the title “Vorstand”:
member?filter=$parents.title = "Vorstand"
Get all members that are not in the membergroup with the id 100:
member?filter=NOT($parents.$id = 100)
Use IN to search for multiple values:
member?filter=`ID` IN (22, 45, 67)
member?filter=`Status` IN ("Aktiv", "Passiv", "Ehrenmitglied")
member?filter=$parents.$id IN (520, 612, 802, 1023)
member?filter=NOT($parents.$id IN (438, 748, 893))
Get all members, where the field “E-Mail” equals the field “E-Mail Geschäft”:
member?filter=`E-Mail` = `E-Mail Geschäft`
Get all members with an empty E-Mail field:
member?filter=`E-Mail` IS EMPTY
Get all members where the E-Mail field is not empty:
member?filter=NOT(`E-Mail` IS EMPTY)
member?filter=NOT `E-Mail` IS EMPTY
Filter by Age (years):
member?filter=AGE(`Geburtstag`) = 22
member?filter=AGE(`Eintrittsdatum`) > 5
Use of the UPPER function to match case-insensitive:
member?filter=UPPER(`Name`) = "MEIER"
Find all members with birthday in August:
member?filter=MONTH(`Geburtstag`) = 8
You can also sort the results by day:
member?filter=MONTH(`Geburtstag`) = 8&sort=DAY(`Geburtstag`) ASC
Find all members with birthday after August 20th:
member?filter=MONTH(`Geburtstag`) > 8 OR (MONTH(`Geburtstag`) = 8 AND DAY(`Geburtstag`) > 20)
Search for dates relative to today (all future dates):
member?filter=`Eintrittsdatum` > TODAY()
Comparing dates:
member?filter=`Eintrittsdatum` <= "2018-06-23"
member?filter=`Eintrittsdatum` = "2018-03-06"
Use of multienum operators:
member?filter=`Mehrfachauswahlfeld` IS ["Wert"]
member?filter=`Mehrfachauswahlfeld` CONTAINS ALL OF ["Wert", "Wert 1"]
member?filter=`Mehrfachauswahlfeld` CONTAINS NONE OF ["Wert 1", "Wert 2"]
member?filter=`Mehrfachauswahlfeld` CONTAINS ANY OF ["Wert 2"]
Get the member that is linked to the debitor with the id 851:
member?filter=$links.debitor.$id=851
Get all members with open debitors:
member?filter=$links.debitor.state="open"
Get all members with a debitor of amount 100:
member?filter=$links.debitor.$links.revenue.amount = 100
Find all members with comments:
member?filter=$links.comment.$id > 0
Combine $links and $children to find all entrygroups that have links to a debitor with the $label “Meier”:
entrygroup?filter=$children.entry.$links.debitor.$links.member.$label filter "Meier"
Get all membergroups that have members with a PLZ over 9000:
membergroup?filter=$children.member.PLZ > 9000
Search in group and subgroups of membergroup 550:
member?filter=`Vorname` = "Tim" AND $ancestors.$id = 550
Get all members you have write access to:
member?filter=$writable=true
Using multiple filters in linked objects may lead to unexpected results, since the conditions are executed separately.
member?filter=$links.debitor.totalamount > 100 AND $links.debitor.remainingamount > 0
Will give you all members that have a debitor with totalamount > 100 and a different debitor with remainingamount > 0.
If you want to filter by multiple properties of a linked object, use the WITH
operator:
member?filter=WITH $links.debitor (totalamount > 100 AND remainingamount > 0)
This will find members with a debitor over 100, which are not yet paid in full.
Don’t forget to encode the URL if you are using special chars in your query.
If you want to dig deeper, this is the formal definition of the query language:
start ::= orExpr
orExpr ::= andExpr "OR" orExpr
andExpr ::= notExpr "AND" orExpr
notExpr ::= "NOT"? withExpr
withExpr ::= ( "WITH" axisExpr ( "." axisExpr )* )? bracketExpr
bracketExpr ::= ( "(" orExpr ")" ) | atomExpr
atomExpr ::= comparisonExpr | filterComparisonExpr | containsComparisonExpr | multienumComparisonExpr | emptyExpr | setExpr
comparisonExpr ::= contentExpr universalOpExpr contentExpr
filterComparisonExpr ::= contentExpr "FILTER" valueExpr
containsComparisonExpr ::= contentExpr "CONTAINS" stringExpr
multienumComparisonExpr ::= contentExpr multienumOpExpr jsonStringArrayExpr
emptyExpr ::= contentExpr "IS" "EMPTY"
setExpr ::= contentExpr "IN" "(" valueExpr ("," valueExpr)* ")"
universalOpExpr ::= "<" | "<=" | ">" | ">=" | "=" | "!="
multienumOpExpr ::= "CONTAINS ALL OF" | "IS" | "CONTAINS NONE OF" | "CONTAINS ANY OF"
contentExpr ::= valueExpr | jsonPathExpr | callExpr | pathExpr
valueExpr ::= stringExpr | numberExpr | boolExpr
callExpr ::= literalExpr "(" (contentExpr ("," contentExpr)* )? ")"
pathExpr ::= propertiesExpr | accessExpr | labelExpr | idExpr | ( axisExpr "." pathExpr )
propertiesExpr ::= "*" | ( propertyExpr ("," propertyExpr)* )
propertyExpr ::= literalExpr | backtickExpr
jsonPathExpr ::= "JSON" ( pathExpr ) ( "[" ( stringExpr | intExpr ) "]" )*
axisExpr ::= parentExpr | ancestorExpr | childExpr | linkExpr | reverseLinkExpr
parentExpr ::= "$parents"
ancestorExpr ::= "$ancestors"
childExpr ::= "$children" "." literalExpr
linkExpr ::= "$links" "." literalExpr
reverseLinkExpr ::= "$reverselinks" "." literalExpr ( "{" literalExpr "}" )? "." pathExpr
axisExpr ::= parentExpr | ancestorExpr | childExpr | linkExpr | reverseLinkExpr
parentExpr ::= "$parents"
ancestorExpr ::= "$ancestors"
childExpr ::= "$children" "." literalExpr
linkExpr ::= "$links" "." literalExpr
reverseLinkExpr ::= "$reverselinks" "." literalExpr
accessExpr ::= "$readonly" | "$writable"
labelExpr ::= "$label"
idExpr ::= "$id"
stringExpr ::= ("'" /[^"]* / "'") | ('"' /[^']* / '"')
backtickExpr ::= "`" /[^`]* / "`"
literalExpr ::= /\w* /
boolExpr ::= "TRUE" | "FALSE"
intExpr ::= "-"? /\d+/
numberExpr ::= "-"? /\d+/ ( "." /\d* / )?
jsonStringArrayExpr ::= "[" stringExpr ( "," stringExpr )* "]"
Some simple examples how to use the API.
More examples can be found on our GitHub page: github.com/usystems/webling-api-examples
This is a simple example to get the title of a Membergroup in PHP:
$apiurl = "https://demo.webling.ch";
$apikey = "<your_api_key>";
$url = $apiurl . "/api/1/membergroup/1?apikey=" . $apikey;
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$data = json_decode(curl_exec($curl), true);
echo $data["properties"]["title"];
This is a simple example that shows how to create a new member in PHP:
$apiurl = "https://demo.webling.ch";
$apikey = "<your_api_key>";
$curl = curl_init();
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode(array(
"properties" => array(
"Vorname" => "Fritz",
"Name" => "Meier",
"E-Mail" => "fritz.meier@example.ch"
),
"parents" => array(550)
)));
curl_setopt($curl, CURLOPT_URL, $apiurl . "/api/1/member?apikey=" . $apikey);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($code != 201) {
echo 'Error: '. $response . ' HTTP-Status Code: '.$code;
} else {
echo 'Creation successful. ID of new member: '.$response;
}
This is a simple example to get the title of a Membergroup in JavaScript:
WARNING: Don’t use this example in production, as the Apikey is exposed to the user!
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
var apiurl = "https://demo.webling.ch";
var apikey = "<your_api_key>";
$(function(){
$.ajax(apiurl + "/api/1/membergroup/1?apikey=" + apikey).then(
function(data) {
$('#title').html(data.properties.title)
},
function(jqXHR, textStatus, errorThrown) {
console.error(jqXHR, textStatus, errorThrown);
}
);
})
</script>
</head>
<body>
<div id="title"></div>
</body>
</html>
Some resources that may be helpful during development.
Easily browse the API with our API Browser. It’s an easy way to find objects and their id’s.
A lightweight Webling API wrapper: github.com/usystems/webling-api-php
Install this package with Composer:
composer require usystems/webling-api-php
The library simplifies Webling API requests:
$api = new Webling\API\Client('https://demo.webling.ch','MY_APIKEY')
$response = $api->get('member/123')
See the GitHub page for more information.
Some code examples that use the API: github.com/usystems/webling-api-examples
A third-party Symfony 2 Bundle, made by Terminal42: github.com/terminal42/webling-bundle
A WordPress Plugin to create member lists and registration forms.
We have an API newsletter to notify users about changes. Sign up here
Breaking Changes are listed here.
Release 08-2022
- Fix: Empty objects of links, children and definitions now return an empty object
{}
instead of an empty array []
Release 11-2021
/debitor
: added date
property which is now used as the general invoice date.
Release 03-2021
Debitors can now have multiple revenues (Rechnungsposten).
Release 01-2021
Debitors can now be written off (ausbuchen).
Release 12-2020
The object costcentertemplate
has been removed, as it was never used in the Webling UI.
Release 11-2019
Debitorcategories are now children of the periodgroup.
This means: debitorcategories are available in all periods
of the periodgroup and do not need to be created if a new period is created. The structure of the following endpoints
are affected:
-
/debitorcategory
: the parents of the debitorcategories are now periodgroups
-
/period
: the period no longer has children of the type debitorcategory
-
/periodgroup
: the periodgroup now has children of the type debitorcategory
Release 07-2017
- Website features are removed from the API by default. They are only present if the website is enabled.
/apikey
and /user
: property webaccess
removed
/quota
: property web
removed
Release 06-2017
-
Document contents URL has changed.
Old: /document/{id}/{filename}.{extension}
New: /document/{id}/file/{filename}.{extension}
-
Some changes to the search Query Language:
- The filter query param is now
?filter=<your_query>
instead of ?query=<your_query>
- Most EBNF features are now supported
- Global
"NOT"
Operator introduced. The old syntax "IS NOT"
and "NOT LIKE"
and "NOT IN"
is not supported anymore
- Operator
"START WITH"
has been removed, use "FILTER"
instead
- Properties containing special characters must now be enclosed by backticks instead of quotes.
Old:
"Zweiter Name" = "Meier"
New: `Zweiter Name` = "Meier"
- Properties can now be used on the left and the right side of a comparison. Example:
`Vorname` = `Nachname`
- The special property
$type
has been removed
-
Changed and improved the sorting options. Old query params were ?sort=Vorname&direction=ASC
, the new param is ?order=Vorname ASC, Nachname DESC
. Multiple sort fields are now possible.
-
The unofficial finance endpoints have changed due to a large internal refactoring