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.
Only the endpoints described in this documentation are part of the public API. Webling answers on
further endpoints that the web app uses internally. You are free to use them, but at your own risk:
they are not documented and they may change or disappear at any time without notice.
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",
"createuser": {
"label": "Hans Meier",
"type": "user",
"id": 584
},
"lastmodified": "2017-05-23 11:43:54",
"lastmodifieduser": {
"label": "Hans Meier",
"type": "user",
"id": 584
}
},
"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 |
Metadata of the object, see below |
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). Most objects must have at least one parent. |
links object |
A list of all linked objects (IDs), grouped by type. |
The meta object contains the following fields. It is read-only.
| Field |
Description |
created timestamp |
When the object was created. May be null for objects created before this was recorded. |
createuser object |
Who created the object: label, type (user or apikey) and id. The id is missing if that user or apikey has been deleted. |
lastmodified timestamp |
When the object was last changed |
lastmodifieduser object |
Who changed the object last, same structure as createuser |
lat, lng number |
Only for members: coordinates of the address, if they could be resolved |
Every property in properties has a datatype, which is one of the following:
| Datatype |
Description |
text |
Single line string. Line breaks are replaced by spaces and the value is truncated to 5000 characters. |
longtext |
String with a maximum size of 2 Gb |
json |
A JSON object, stored as a string. It is a longtext property, but with the additional constraint that the content must be valid JSON. |
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]
}
Tip: You can retrieve the full data instead of just the IDs by adding the ?format=full parameter to the URL. More
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"
}
Some errors carry a machine readable type in addition to the message. Match on type instead of on the
message text, the message may change at any time.
{
"error": "File quota exceeded",
"type": "QuotaExceeded",
"details": {
"filequota": 1073741824
}
}
| Property |
Description |
error string |
Human readable error message. Always present. |
type string |
Identifier of the error, e.g. QuotaExceeded or EmailLimitExceeded. Only present on errors that report one. |
details object |
Additional data about the error. The content depends on the type. Only present together with type. |
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, or a limit of your Webling subscription was reached (type is QuotaExceeded)
-
404 Not Found: The resource was not found
-
413 Request Entity Too Large: the request body was too large for the web server, try to split your request into multiple requests
-
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 or maintenance is going on
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” |
NOT |
negates the condition that follows. Put the condition in brackets if it is more than one comparison. |
NOT `Name` = "Meier" or NOT(`PLZ` > 100 AND `PLZ` < 4000) |
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 |
A multienum property holds several values at once. These operators compare it against a list of
values written in square brackets:
| Operator |
Description |
Example |
IS |
the property contains exactly these values and nothing else |
`Mehrfachauswahlfeld` IS ["Wert 1"] matches only members whose sole value is “Wert 1” |
CONTAINS ALL OF |
the property contains all listed values, further values are allowed |
`Mehrfachauswahlfeld` CONTAINS ALL OF ["Wert 1", "Wert 2"] |
CONTAINS ANY OF |
the property contains at least one of the listed values |
`Mehrfachauswahlfeld` CONTAINS ANY OF ["Wert 1", "Wert 2"] |
CONTAINS NONE OF |
the property contains none of the listed values. Empty properties match too. |
`Mehrfachauswahlfeld` CONTAINS NONE OF ["Wert 1"] |
Note that CONTAINS NONE OF also returns objects where the property is empty, while the other three
only return objects that have a value. CONTAINS NONE OF is therefore equivalent to
NOT `Mehrfachauswahlfeld` CONTAINS ANY OF [...], not to the negation of CONTAINS ALL OF.
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. |
JSON(<json string>) |
access the properties of a value that is stored as a JSON, e.g: JSON(`amountCache`)['total'] > 100 |
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&order=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.
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 05-2026
Presencelist attendees now require a state property. Valid values are attended, not_attended, excused, late or sick — null is no longer accepted.
The initial state is typically set to not_attended.
Release 08-2025
In anticipation of the upcoming VAT and split-entry integration, the following changes have been made to entries and entrygroups:
-
Moved property entryid from /entry to /entrygroup
-
Moved property receipt from /entry to /entrygroup
-
Moved property receiptfile from /entry to /entrygroup
-
Moved property isEBill from /entry to /entrygroup
Temporary: The moved properties are currently still available in /entry as a transparent read/write proxy object, but will be removed in future release.
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
Comment List ¶
/comment{?filter,order,format}Lists all available comment IDs
Query Examples ¶
Get all comments of a specific member:
comment?filter=$links.member.$id = 535Get all comments that are attached to a member at all:
comment?filter=$links.member.$id > 0Search in the comment texts:
comment?filter=text CONTAINS "telefoniert"Example URI
string(optional)Filter the list using the Query Language
string(optional) Example: text ASCSort the comment list by property and direction
string(optional)Specify
format=fullto get the full object instead of a list of IDs200Headers
Content-Type: application/jsonBody
{ "objects": [ 4234, 4236, 4240 ] }