API¶
This chapter is aimed at developers and requires programming experience.
A number of functions in MemberOrganizer are exposed via an API. This makes it possible to build integrations with MemberOrganizer from other systems. In what follows, “other systems” are referred to as client.
Read the contents of this chapter carefully, including limitations and liability as described below, to understand general restrictions.
If MemberOrganizer determines that you have or have attempted to violate the terms of use for the API, your access to use the API may be temporarily or permanently revoked.
Security and authentication¶
To be allowed to access the API, the client that wishes to integrate with foreninglet.dk must first log in with a username and password. This is done via HTTP Basic Authentication, which for example can be done as follows using the command-line tool curl:
curl https://foreninglet.dk/api/members?version=1 -u {username}:{password}
The version parameter is mandatory and indicates which version of the API is being accessed. The current version is 1.
The parameter after -u is username and password separated by a colon. The username and password can be viewed by the association’s administrator under “Configuration -> Integrations -> API”.
The password (in combination with the username) grants access to the association’s data, which is why the password must not be shared with third parties, as third parties would thereby gain access to the association’s data.
The URL https://foreninglet.dk/api/members?version=1 can also be entered in a browser, after which the browser will ask for username and password. In this way it is easy to test the API.
Versioning¶
The client must always specify the URL parameter version on a request. This ensures that the API can be extended continuously without breaking the contract with existing clients, since a given version will never change naming or remove a field in the responses the API returns to the client.
When changes are made to the API, this always happens by adding a new version of the API, so that clients that do not run on the latest version still work.
If an older version of the API is discontinued, associations using that version of the API will be notified in reasonable time. Typically an association can simply switch to the latest version of the API without having to make code changes in the client.
Request rate limits¶
There is an upper limit on how many requests you may make to the API per hour. The limit is 10,000 requests per hour.
If the limit is exceeded, the API will respond with HTTP 429 (“Too Many Requests”).
Protocol and data format¶
Integration is via the https protocol. The API is built on REST principles.
The data format is json. If a client wants a response in another format, e.g. html (or xml), add the following URL parameters to the request: /format/html. For example: https://foreninglet.dk/api/members/format/html?version=1
When creating new data (POST) or updating existing data (PUT), “Content-Type: application/json” must always be specified in the request.
Timestamps follow ISO8601 and will always be shown in UTC.
When a resource is created or updated, the response will be found in the body formatted as json, and a Location http header is set pointing to the resource.
Limitations and liability¶
Your subscription to MemberOrganizer makes it possible to access and use an API, including to develop and implement integrations for use in connection with your subscription to MemberOrganizer for your internal business purposes.
To use and access the API, you must use the API username and password, which is available if you are set up in MemberOrganizer. You must not share this username and password, and must store it securely. Username and password are the only way you are permitted to access the API.
You must not use the API to copy products or services offered by MemberOrganizer, including functions or clients on platforms (such as iOS or Android) where MemberOrganizer offers its own client or function.
You must not resell the API. You must not use the API in any way that could potentially undermine the security of the API.
You are solely responsible (and MemberOrganizer has no liability of any kind) for the content, development, operation or maintenance of the integrations you may develop via the API. You are solely responsible for ensuring that your integrations do not violate or infringe intellectual property rights belonging to a third party.
You must respect and comply with the technical and policy-implemented limitations of the API.
To the extent you develop or implement integrations that send or otherwise use the API to transfer your data outside MemberOrganizer, you will be responsible for any necessary notifications to or consents from end users that their data will be sent outside MemberOrganizer. MemberOrganizer is not responsible for the security or integrity of your data to the extent it is sent outside MemberOrganizer via your integrations to the API.
API calls¶
Below is a list of the various calls to the API.
Members
Activities
Invoices
Booking
Member list¶
GET /api/members
The following call returns all enrolled members
curl https://foreninglet.dk/api/members?version=1 -u {username}:{password}
If you want a list of all resigned members, do it as follows:
curl https://foreninglet.dk/api/members/status/resigned?version=1 -u {username}:{password}
Create member¶
POST /api/member
When creating a member, you must as a minimum specify a first name.
Example request
{
"member": {
"first_name": "Hans Christian",
"last_name": "Andersen"
}
}
Via curl
curl https://foreninglet.dk/api/member?version=1 \
-d '{"member": {"first_name": "Hans Christian","last_name": "Andersen"}}' \
-H "Content-Type: application/json" -u {username}:{password} -X POST
Example response
HTTP/1.1 201 Created
Location: https://foreninglet.dk/api/member/id/1?version=1
Content-Type: application/json
{
"member": {
"id": 12345,
"display_id": 1,
"first_name": "Hans Christian",
"last_name": "Andersen",
"address": "",
"address2": "",
"zip": "",
"city": "",
"email": "",
"email2": "",
"email3": "",
"phone": "",
"mobile": "",
"gender": 2,
"birthday": "0000-00-00",
"country_code": "",
"ean_number": "",
"enrollment": "0000-00-00",
"resignation_date": "0000-00-00",
"password": "63C4B",
"field1": "",
"field2": "",
"field3": "",
"field4": "",
"field5": "",
"field6": "",
"field7": "",
"field8": "",
"field9": "",
"field10": "",
"created": "2015-06-18T12:02:14+0000",
"updated": "2015-06-18T12:02:14+0000",
"activity_ids": [],
"cpr_number": "",
"reg_number": "",
"account_number": ""
}
}
The system automatically assigns a password to new members. You can also specify a password when creating the member.
Parameters¶
When creating a new member via POST, the following parameters can be specified, where first_name is the only mandatory parameter.
Name |
Description |
|---|---|
display_id |
Member number |
first_name |
First name |
last_name |
Last name |
address |
Address |
address2 |
Address 2 |
zip |
Postal code |
city |
City |
Email address |
|
email2 |
Email address 2 |
email3 |
Email address 3 |
phone |
Phone number |
mobile |
Mobile number |
gender |
Gender (0=female, 1=male, 2=no gender specified) |
birthday |
Date of birth |
country_code |
Country code (ISO 3166 - 2 letters) |
ean_number |
EAN number |
enrollment |
Enrollment date |
resignation_date |
Resignation date |
password |
Password |
field1 |
Field 1 |
field2 |
Field 2 |
field3 |
Field 3 |
field4 |
Field 4 |
field5 |
Field 5 |
field6 |
Field 6 |
field7 |
Field 7 |
field8 |
Field 8 |
field9 |
Field 9 |
field10 |
Field 10 |
activity_ids |
Activity IDs |
cpr_number |
CPR number |
reg_number |
Bank registration number |
account_number |
Bank account number |
If no member number is specified, the system assigns the next available member number itself.
Note
Betalingsservice: If the CPR number, registration number and account number fields are filled in, and the association is set up to use Betalingsservice, the system will automatically attempt to create a PBS agreement for the new member.
Update member¶
PUT /api/member/id/{id}
When updating a member, you must specify the ID of the member to be updated. Member IDs are returned from the member list API call. Note that the fields id and display_id are two different fields, where id is the internal database number that is unique to the member and is used in API calls, while display_id corresponds to the member’s member number.
You only need to specify the fields you wish to change.
Be aware that if you specify an empty list of activity IDs, any activities assigned to the member will be removed. If you do not wish to affect the member’s activities, you must omit the activity_ids field entirely. As soon as the activity_ids field is set, the member’s complete list of activities will be updated to contain exactly the specified activities. Activities can therefore also be removed.
Example request
{
"member": {
"first_name": "Hans Christian",
"last_name": "Jensen"
}
}
Via curl
curl https://foreninglet.dk/api/member/id/{id}?version=1 \
-d '{"member": {"first_name": "Hans Christian","last_name": "Jensen"}}' \
-H "Content-Type: application/json" -u {username}:{password} -X PUT
Example response
HTTP/1.1 200 OK
Content-Type: application/json
{
"member": {
"id": 12345,
"display_id": 122153,
"first_name": "Hans Christian",
"last_name": "Jensen",
"address": "",
"address2": "",
"zip": "",
"city": "",
"email": "",
"email2": "",
"email3": "",
"phone": "",
"mobile": "",
"gender": 2,
"birthday": "0000-00-00",
"country_code": "",
"ean_number": "",
"enrollment": "0000-00-00",
"resignation_date": "0000-00-00",
"password": "63C4B",
"field1": "",
"field2": "",
"field3": "",
"field4": "",
"field5": "",
"field6": "",
"field7": "",
"field8": "",
"field9": "",
"field10": "",
"created": "2015-06-18T12:02:14+0000",
"updated": "2015-06-18T12:02:16+0000",
"activity_ids": []
}
}
Login¶
POST /api/memberlogin
When you want to log in as a member, you must specify a username and a password. The username can be member number or email address, and this is controlled by the field parameter, which by default is set to email. The other option is member_number.
If the member has enrolled status and username and password match, the member’s details are returned.
The following is an example of login with email address test@foreninglet.dk and password 12345.
Example request
{
"credentials": {
"username": "test@foreninglet.dk",
"password": "12345",
"field": "email"
}
}
Via curl
curl https://foreninglet.dk/api/memberlogin?version=1 \
-d '{"credentials": {"username": "test@foreninglet.dk","password": "12345", "field": "email"}}' \
-H "Content-Type: application/json" -u {username}:{password} -X POST
Example response
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 12345,
"display_id": 1000,
"first_name": "Hans",
"last_name": "Jensen",
"address": "",
"address2": "",
"zip": "",
"city": "",
"email": "test@foreninglet.dk",
"password": "12345"
}
The response above is only an excerpt of the member information returned if the member is found. For example, a list of the activities the member is linked to is also returned. This is a field named activity_ids, which contains a list of activity IDs.
If the member is not found, the following response is returned:
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"error":"Member with username [test@foreninglet.dk] could not be found"
}
Member has access¶
GET /api/memberhasaccess?version=1&member_id=12345&door_number=1
Indicates whether a member has access to a door/access point. This call is typically used in connection with an external access control system. Note that MemberOrganizer offers a 100% integrated access control system, so this API call should only be used if you have very special access control requirements. An example could be a wish to use facial recognition, which is not built directly into MemberOrganizer’s own access control system, but which can be implemented via a third-party system that then uses this API call.
Via curl
curl https://foreninglet.dk/api/memberhasaccess?version=1&member_id=12345&door_number=1
Example response
HTTP/1.1 200 OK
Content-Type: application/json
{
"has_access":true,
"message":"",
"error_message":""
}
If the member does not have access, has_access will be false. This can for example be because the member has been resigned, or because the member has not paid their membership fee.
Activity list¶
GET /api/activities
The following call returns all activities
curl https://foreninglet.dk/api/activities?version=1 -u {username}:{password}
Example response
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"ActivityId": "11111",
"Categories": ["Youth department"],
"ExternalDescriptions": [],
"Name": "Youth membership fee",
"OnlineEnrollmentEnabled": "0",
"SettlementDate": "0000-00-00T00:00:00+0000"
},
{
"ActivityId": "22222",
"Categories": ["Senior department"],
"ExternalDescriptions": [],
"Name": "Senior membership fee",
"OnlineEnrollmentEnabled": "0",
"SettlementDate": "0000-00-00T00:00:00+0000"
},
{
"ActivityId": "33333",
"ExternalDescriptions": [
{
"Headline": "Enrollment opens",
"Text": "1 August 2025, at 12:00"
},
{
"Headline": "Enrollment closes",
"Text": "25 July 2025, at 13:00"
}
],
"Name": "Summer cap",
"OnlineEnrollmentEnabled": "1",
"SettlementDate": "2025-08-01T10:00:00+0000",
"CloseDate": "2025-07-25T15:00:00+0000"
}
]
The example above shows a response with 3 activities. One of the activities is open for online enrollment, which can be read in the field OnlineEnrollmentEnabled, where the value is set to 1. If an activity is open for online enrollment, a number of descriptions of the activity will also be filled in in the field ExternalDescriptions, which is a list of headlines and texts.
If you want to use the activity list to create an enrollment list on your own website, you can use ActivityID to deep-link from this list on the website into the member portal, which for example can be done like this:
https://<association-ID>.foreninglet.dk/memberportal/teamenrollment/subscribe/<ActivityID>
You must of course replace association-ID and ActivityID with the correct values. The association’s ID can be seen in the system when you are logged in by clicking the menu item “Member Portal”.
Create invoice¶
POST /api/invoice
When creating an invoice, you must as a minimum specify member ID (note that member ID is not the same as member number), a description of what is being invoiced, and the amount to be invoiced.
Example request with card payment
{
"invoice": {
"member_id": 123456,
"description": "Membership fee",
"amount": 100.00
}
}
Via curl
curl https://foreninglet.dk/api/invoice?version=1 \
-d '{"invoice": {"member_id": 123456, "description": "Membership fee", "amount": 100.00}}' \
-H "Content-Type: application/json" -u {username}:{password} -X POST
Example response
HTTP/1.1 201 Created
Location: https://foreninglet.dk/api/invoice/id/1000?version=1
Content-Type: application/json
{
"invoice": {
"credit_card_payment_url": "https://foreninglet.dk/main/apiredirectcreditcardwindow/{x}",
"id": 1000,
"text": "Membership fee",
"total_amount": 100
}
}
The system automatically assigns an invoice number to a new invoice. It is found in the “id” field.
In the response there is an important parameter named credit_card_payment_url, which contains an address. If you redirect to this address, a payment window opens where the member can pay the invoice with a payment card. It is of course a prerequisite that card payment has been purchased and set up.
Example request with payment via MobilePay recurring payment
In addition to card payment, you can also have the system redirect to a MobilePay payment window. A prerequisite for this to work is that MobilePay recurring payment is set up in the system in advance. If you want a MobilePay payment link returned from the API call, you must add an extra parameter compared to the card payment example, namely the parameter named “payment_method” which must be set to “MobilePayRecurring”, as shown in the following example:
{
"invoice": {
"member_id": 123456,
"description": "Membership fee",
"amount": 100.00,
"payment_method": "MobilePayRecurring"
}
}
Via curl
curl https://foreninglet.dk/api/invoice?version=1 \
-d '{"invoice": {"member_id": 123456, "description": "Membership fee", "amount": 100.00, "payment_method": "MobilePayRecurring"}}' \
-H "Content-Type: application/json" -u {username}:{password} -X POST
Example response
HTTP/1.1 201 Created
Location: https://foreninglet.dk/api/invoice/id/1000?version=1
Content-Type: application/json
{
"invoice": {
"mobilepay_payment_url": "https://foreninglet.dk/main/apiredirectmobilepayrecurringwindow/{x}",
"id": 1000,
"text": "Membership fee",
"total_amount": 100
}
}
Create booking¶
POST /api/booking
When creating a booking, you must as a minimum specify the ID of the resource calendar being booked in and the ID of the resource the booking concerns, as well as start and end date, and either a text or a list of member IDs.
Example request
{
"booking": {
"resource_calendar": 5678,
"resource_id": 1234,
"text": "Annual general meeting",
"start_date": "2017-11-14T18:00:00+0000",
"end_date": "2017-11-14T20:00:00+0000",
"create_timed_code": "true"
}
}
Via curl
curl https://foreninglet.dk/api/booking?version=1 \
-d '{"booking": {"resource_calendar_id": 5678, "resource_id": 1234, "text": "Annual general meeting", "start_date": "2017-11-14T18:00:00+0000", "end_date": "2017-11-14T20:00:00+0000", "create_timed_code": "true"}}' \
-H "Content-Type: application/json" -u {username}:{password} -X POST
Example response
HTTP/1.1 201 Created
Location: https://foreninglet.dk/api/booking/id/1000?version=1
Content-Type: application/json
{
"booking": {
"id": 1000,
"resource_calendar_id": 5678,
"resource_id": 1234,
"text": "Annual general meeting",
"start_date": "2017-11-14T18:00:00+0000",
"end_date": "2017-11-14T20:00:00+0000",
"created": "2017-11-07T16:42:14+0000",
"timed_code": 476294
}
}
The system automatically assigns a booking ID to a new booking. It is found in the “id” field.
If you have set “create_timed_code” to “true”, the system will generate a 6-digit code that can be used in the door system (if installed), and grant access to the booking in the time period surrounding the booking’s start and end time.
The resource calendar defines a set of rules for the resources included in the calendar. A resource calendar typically contains one to several resources. If booking badminton courts, a resource calendar will typically correspond to a hall, and the resources will be the courts in the hall.
Delete booking¶
DELETE /api/booking/id/{id}
When deleting a booking, you must specify the ID of the booking you wish to delete.
Via curl
curl https://foreninglet.dk/api/booking/id/1234?version=1 \
-H "Content-Type: application/json" -u {username}:{password} -X DELETE
Example response
HTTP/1.1 200 OK
Content-Type: application/json
{"id":"1234","message":"DELETED!"}
The system also deletes any code for the door system if such a code is linked to the booking.
Booking list¶
GET /api/bookings/start_date/{startdate}/end_date/{enddate}
The date format must be YYYY-MM-DD - e.g. 2018-12-24, which is Christmas Eve.
When fetching a booking list, you must as a minimum specify start and end date, as shown above. The system returns all bookings within the specified date interval from the first resource calendar. So bookings are always fetched from only one resource calendar at a time. If you wish to fetch bookings from another resource calendar, you must specify an extra parameter named resource_calendar_id on the call, and set this parameter to the ID of the resource calendar you wish to fetch bookings from.
Via curl
curl https://foreninglet.dk/api/bookings/start_date/{startdate}/end_date/{enddate}?version=1 -u {username}:{password}
Example response
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"activity_id": "0",
"created": "2017-02-27T10:56:12+0000",
"created_by": "admin",
"end_time": "2017-12-01T13:00:00+0000",
"id": "99128",
"resource_id": "1234",
"start_time": "2017-12-01T11:00:00+0000",
"text": "Booking of court 1",
"url": ""
},
{
"activity_id": "0",
"created": "2017-07-31T21:13:52+0000",
"created_by": "admin",
"end_time": "2017-12-01T19:00:00+0000",
"id": "124282",
"resource_id": "1235",
"start_time": "2017-12-01T15:00:00+0000",
"text": "Booking of court 2",
"url": ""
}
]
Note that start_time and end_time in the response above are specified in UTC.
Resource calendar info¶
GET /api/bookings/resourcecalendar/id/{id}
When fetching info about a resource calendar, you must specify the ID of the resource calendar, as shown above. The association can provide the IDs of the resource calendars to be used.
The system returns info about the resource calendar, and this info can be used - in combination with already booked times - if you wish to present available times in an external system. This could for example be a system such as Wannasport (https://www.wannasport.dk), which integrates with MemberOrganizer.
Via curl
curl https://foreninglet.dk/api/resourcecalendar/id/{id}?version=1 -u {username}:{password}
Example response
HTTP/1.1 200 OK
Content-Type: application/json
{
"close_days": [
"24-12-2020: Closed due to Christmas Eve",
"31-12-2020: Closed due to New Year's Eve"
],
"description": "",
"duration": "60",
"end_date": "2020-12-31",
"end_time": "23:00",
"id": "1111",
"name": "Court overview",
"rule_max_days_ahead": "14",
"selected_days": "1,2,3,4,5,6,7",
"start_date": "2020-01-01",
"start_time": "07:00"
}
A few notes on the response:
- start_date
Date when the calendar opens.
- start_time
Time of day (Danish time) when the calendar opens - i.e. when bookings can be made from.
- end_date
Date when the calendar closes.
- end_time
Time of day (Danish time) when the calendar closes - i.e. when bookings can be made until.
- selected_days
1 equals Monday, 2 equals Tuesday, etc. This calendar is therefore open for bookings all week. And it is open from 07:00-23:00.
- rule_max_days_ahead
This is a rule stating that a maximum of 14 days ahead can be booked. This rule is not enforced via calls from the API, but only when members book directly via the association’s member portal.
- close_days
List of dates when the calendar cannot be booked at all. I.e. “closed days”.
The information above - in combination with the bookings that have been entered and can be fetched via another API call (Booking list) - is sufficient to present available times in an external system.