Most API endpoints are at for the test system respectively for the live system. Please note that the OAuth endpoint and some client-facing endpoints such as file download are not under api/v1/.
Data is sent and received as JSON.

Timestamps are returned in ISO 8601 format (YYYY-MM-DDTHH:MM:SS.sssssssZ).


  1. Sending invalid JSON will result in a 400 Bad Request response.
  2. Sending invalid fields will result in a 422 Unprocessable Entity response.
  3. Calling a endpoint which does not exist will result in a 404 Not Found response.

HTTP Verbs

We are trying to use HTTP verbs as appropriately as possible. Hence, we (will) support
Retrieves the resource.
Used to create resources (as long as their future location is determined by the server).
Used for replacing objects or collections.
Used to delete resources (items or collections).
Used to update parts of an object. In contrast to PUT it will only change the passed fields.

Authentication & Authorization

The REST API uses 2-legged OAuth, which is part of the OAuth 2.0 specification. In principle, all it does is to allow you to retrieve an access token by providing a username and a password (or a client id and client secret, respectively). Subsequently, only this access token is used to authorize requests instead of the username/password pair. However, this simple indirection comes with a number of advantages, because it is standardized, extensible and avoids excessive computationally expensive password hashing.
The tokens are not self-validated and can be revoked by the user at any time. Since calls are stateless, the token must be given in every request, either in an HTTP Authentication header or as a parameter:
as an HTTP header
curl -H "Authorization: Bearer OAUTH-TOKEN"
as a parameter

Acquiring Access Tokens

Billwerk supports the two two-legged OAuth grants at this time: The 'Resource Owner Password Credentials Grant' and the 'Client Credentials Grant'.
Client Credentials Grant
A 'confidential' OAuth client is a client that is able to securely store its own credentials (typically a web application). Such clients have a client_id and a client_secret, which can be used to retrieve an access token directly. This is the recommended approach if you want to query the billwerk API in response to webhooks or otherwise connect your web-based application to billwerk, because it decouples your API from your own frontend login and it does not run in any kind of user context.
You can create OAuth clients in your account under Settings/My Apps. If you create a confidential client, it will have both a client_id and a client_secret which can be used to retrieve an access token in this way.
Resource Owner Password Credentials Grant
requires the user to present user name and password to you. That comes with the usual drawbacks of password-based authentication: You need to store the password securely and a password change will render your backend inoperable. However, this flow can be used from e.g. mobile applications, desktop applications and other frontends that can't store a client_secret securely. Such applications are called 'public' OAuth clients.
To connect your backend, it is advisable to use the client credentials grant so you don't have to store your billwerk access password in your database. For each client system create its own client credentials in the billwerk settings. Do not use the one of the billwerk Admin UI! Please make sure, however, not to rely on the currently infinite lifetime of the access token.
OAuth clients MUST be able to re-request access tokens.
OAuth clients MUST NOT retrieve a new access token with every request, otherwise they'll run into request limits very quickly.

URL to aquire the OAuth token is https://{system} {system} can be "app" (for production system) or "sandbox" (for sandbox system).

Acquiring access tokens via Client Credentials Grant

POST /oauth/token/
Content-Type: application/x-www-form-urlencoded
Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=


Here, the Basic authorization consists of the client_id and client_secret. Please note that client_id MUST NOT be specified in the request body because it's already contained in the Basic authorization header.

Acquiring access tokens via Resource Owner Password Credentials Grant

POST /oauth/token/
Content-Type: application/x-www-form-urlencoded


Please note that username is the e-mail address of a billwerk user, password is the user's password and client_id is the id of your app which must be registered with billwerk before use. Hence, the password grant_type requires the user to share his password with you (2-legged OAuth).

Notes on HTTP Basic Authorization

HTTP Basic Authorization headers are Base64-encoded strings of the scheme username:password. For example, a user with password foobar would use the following header:                               | Concatenated username and password
am9obi5kb2VAZXhhbXBsZS5jb206Zm9vYmFy                      | Base64 encoded
Authorization: Basic am9obi5kb2VAZXhhbXBsZS5jb206Zm9vYmFy | HTTP Header
In the client credentials grant, this boils down to client_id:client_secret, e.g.:
// client_id:client_secret
// base64 encoded:
// formatted as HTTP Authorization header
Authorization: Basic NTFlZmZmYjllYjU5NmEyYzJjYjE3YWVlOjliOWI4Y2ZkNDVkMzJlNTE1M2U3ZGVlY2ZlZTdmYzQ0

3-legged OAuth

Further grant_types, specifically those required in 3-legged OAuth (3LO) where the user doesn't have to share his credentials with 3rd parties are planned.


Pagination can be performed using skip and take parameters. However, skip/take pagination has some disadvantages:
  1. These are not stable cursors: if new elements are added in the mean time, the second page will contain elements that were present on the first page before
  2. skip is limited to 1000 elements, because the operation is rather expensive
Alternatively, some resources provide cursor-based pagination, e.g.
These cursors are stable and much faster than skips.

Rate Limiting

Currently, we limit API requests. Rate limits are transferred in the HTTP headers:
HTTP/1.1 200 OK
X-RateLimit-Limit: 10000
X-RateLimit-Remaining: 9954
If you hit a rate limit you'll receive the 429 Too many requests error.
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 10000
X-RateLimit-Remaining: 0
Unauthenticated requests will also go through a security filter. Do not make unauthenticated requests aggressively, because you could quickly end up in an IP ban.

Conditional Requests

Most responses return an ETag header. You can use the values of these header to make subsequent PUT / PATCH requests to those resources using an If-Match header. If the resource changed meanwhile, the server will return a 412 Precondition Failed. Currently validation is done for the resources Customer and Contract. This validation will be extended to other resources in the future.

Order Types And Data Formats

All data must be UTF-8 encoded JSON.

DataType Description
decimal (json: number) A high-precision floating point value. Note: these will not be normalized. For example, a price of "1.000" will be printed on your invoice with three decimal places, even if the invoice is in a currency that usually has only two decimals, and even though "1.000" has the same numerical value as "1". This allows for very fine-grained control (for example if the products you sell are traded with another decimal of precision), but can make your invoices look awkward if you perform normalization, i.e. if you supply "1.34", "121.99" and "353" in a single document. The automatically calculated values, such as TotalGross always have the number of decimal places that the respective currency demands. Commas as decimal separator or grouping symbol are not allowed.

Also, please note that fractional usage periods in contract management will always be rounded to 6 decimal digits.
Ids Ids are either globally unique identifiers, truncated SHA1-hashes or other hexadecimal strings. You should treat these as opaque strings.
BearerType A type of transmission channel. Must be one of: Email, ArchiveOnly or SnailMail.
Currency Three-letter currency code according to ISO 4217, e.g. EUR, USD, CHF
Country Two-letter country code according to ISO 3166-1 alpha-2, e.g. DE, US
DateTime Date and time are represented as ISO 8601 string, i.e. in the format YYYY-MM-DDTHH:MM:SSZ