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.

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

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.