API overview description

The API is based on REST/JSON, and there are JSON Schema's of most API items (Will be expanded).

Authentication: is through the standard HTTP Authorization header, but a custom scheme called "NemTilmeld" which consists of an "api_access_id":"signature", where the signature is based on a standard SHA256 HMAC signature using a secret_key and selected headerfields and the message body.

There are separate API access ID's for different endpoints, depending on whether the request is adding organizations or accessing an existing organization. Access to endpoints should also follow the domain name of the organization, e.g. for adding organizations or testing availablity of names www.nemtilmeld.dk should be used, and for other operations the organizations domain should be used.

Ordering and response limits

All API GET endpoints returning arrays of items support and enforces the following GET parameters, so in order to fetch large sets it may be required to perform multiple requests.

Parameter Default Description
order_by id Which fields from the JSON to order the result by, this can be a comma separated list of names, the supported names are at each endpoint below, the internal database ID is always appended to ensure a consistent order on ties.
direction asc The order applied to the above, applies to all items can be 'asc' or 'desc'
limit 250 Maximum number of entries returned at a time, supported values are 1-1000
offset 0 The number or record ignored after the ordering, so essentially used to page the entries

In addition to that some endpoints support additional parameters, to only fetch items within a given timespan, which variables and whether it is supported is noted on the individual endpoints

Parameter Default Description
timespan_variable The JSON variable to use as argument, the supported options are listed under the specific endpoint (if supported by that endpoint).
timespan None, must be present if the timespan_variable is present The timespan to use, this is indicated as either an ISO8601 Period or as ISO8601 NON abbreviated timestamps as START/END (END is optional) with timezone separated , e.g.
  • P1DT8H to get items from now and 1 day and 8 hours back
  • 2018-01-01T00:00:00Z - to get items changed since january 1st 2018
  • 2018-01-01T13:00:00Z/2018-01-01T15:30:00Z - to get items changed on january 1st 2018 between 13:00 and 15:30 UTC

Endpoints:

The actual availability of these depends on the access credentials used, i.e. a specific API user will have access to one or more of these.

URL Format Method Description
/api/categories/ GET Fetch a list of categories. Returns categories_list.json
Supports ordering fields:
  • id
  • org_id
  • active
  • added_timestamp
  • name
  • label
  • description
  • no_events_placeholder
  • event_ordering
  • show_on_public_index
  • show_if_empty
  • show_other_categories_list
/api/events/ GET Fetch a list of events (the events on the account the user has access to). Returns events_list.json
Supports ordering fields:
  • id
  • org_id
  • created_date
  • title
  • start_time
  • end_time
  • registration_deadline
/api/locations GET Lists locations. Returns locations_list.json objects
Supports ordering fields:
  • id
  • title
/api/locations/{location_id} GET Retrieve a specific location by ID, returns location.json
/api/reports/events/{event_id}/registrants GET Get a report on the registrants for this event. Returns event_registrants_report.json.
Supports selecting only items within a timespan, this allows restriction on these 2 variables:
  • registration_time
  • last_updated_time

Supports ordering fields:
  • id
  • registration_time
  • last_updated_time
  • quantity
When using last_updated_time as a criteria includes registrants deleted/refunded since the specified timestamp. The quantity will be 0 if refunded, or updated if only partially refunded.
Also note that last_updated_time MAY include registrants which are updated in the system, but in a manner which may not affect the record in the API. i.e. the retrieving system MUST handle the fact that records may appear here even though they end up exporting as identical except for the update timestamp.
/api/series/ GET Fetch a list of series. Returns series_list.json
Supports ordering fields:
  • id
  • org_id
  • org_series_id

HTTP headers

The API uses 4 HTTP Headers, 3 of which are mandatory on requests, these are:

Header Mandatory Description Example
Authorization Mandatory Authentication and Authorization information, as specifiec in the Security section. NemTilmeld example_api_username:8338c48a06ec19672a954b25cd7ce2553c454a5b224b3debexamplesignature
Date Mandatory Standard HTTP date field, note this must represent now, as the API will reject messages which are old or into the future. Sun, 06 Nov 1994 08:49:37 GMT
Nonce Mandatory Nonce value. Must be an increasing integer value (larger than the last API call for the user), the API will reject any message with an old value as a replay attack, could by milliseconds or microsecond precision time. 123456
API-Version Optional Specifies the version of the API, as an integer. Specifying no version will give you the latest response. 1

Security

The security, authentication and tamperproofing, consists of a custom HTTP Authorization scheme. This consists of a HTTP header: Authorization containing a value of this format NemTilmeld example_api_username:SHA256HMAC_in_lower_case_hex_format

The signature is calculated based on a selected number of items separated by a newline character (ascii 10 or 0x0A, in most escape sequences in strings "\n" or in .Net use "(char)(10)"), using a standard SHA256 HMAC (rfc2104, using SHA256 as the hash algorithm). The following fields are used in this order, unused fields are empty strings when adding the newlines:


So a HTTP request of these relevant values (note API-Version and body is empty)
        GET /api/events/?limit=10&offset=20 HTTP/1.1                    
        Date: Wed, 12 Apr 2023 08:59:32 GMT
        Nonce: 1681294879863
        
becomes this string
        "GET\n/api/events/?limit=10&offset=20\nWed, 12 Apr 2023 10:21:19 GMT\n1681294879863\n\n"
        
Assuming a user with username "example_api_username" and api_key "example_key" the signature then becomes:
        ddd11fd1d6e21f400e12e88923846ff266a9514334044323324a371e2fbafbd6        
        
Which should be added as the header as:
        Authorization: NemTilmeld example_api_username:ddd11fd1d6e21f400e12e88923846ff266a9514334044323324a371e2fbafbd6
        

In Addition to this the API will verify that the request is current, i.e. the Date header timestamp matches the current date and time and that the Nonce is larger that the last value

Example Client implementations

Implementation of a example client implementations

Example PHP and CURL client implementation

A simple client implementation can be found in NemTilmeldAPIClient.class.php.
Usage example:

            include 'NemTilmeldAPIClient.class.php';
            $client = new NemTilmeldAPIClient('example.nemtilmeld.dk', 'example_user', 'example_secret');
            $result = $client->get('/api/events/123/registrants');
            if ($client->getResponseCode() !== 200) {
                echo "error response";
            } else {
                var_dump($result);
            }