# Getting started

# Environments

There are 3 environments each with their unique urls

All requests made should be done to the proper environment using the correct url.

# Request headers

The following request headers should always be set

- Accept: application/json
- Content-Type: application/json

The following request headers should be set for all endpoint calls

- Authorization: Bearer YOUR_ACCESS_TOKEN

If you're experiencing issues authorizing your API-calls, please make sure you are using the correct environment with the pairing client credentials.

# Authentication

Authentication will be done with OAuth2 using the “Client Credentials” grant_type.

Each external party will receive a dedicated pair of CLIENT_ID and CLIENT_SECRET credentials per environment (testing, staging, production)

These credentials will be used to gain YOUR_ACCESS_TOKEN.

# Gaining a token

In case your request fails, the errors returned should be reasonably clear to determine what is going wrong.

--- ENDPOINT ---

Domain: equidata 
Url: /oauth/token
Method: POST

--- EXAMPLE PAYLOAD ---
{
    "grant_type": "client_credentials",
    "client_id": "CLIENT_ID",
    "client_secret": "CLIENT_SECRET",
    "scope": "*"
}
--- EXAMPLE RESPONSE ---
{
    "token_type": "Bearer",
    "expires_in": 31536000,
    "access_token": "YOUR_ACCESS_TOKEN"
}

# Testing access

When the request succeeded, you can now perform authenticated requests to other api endpoints using the gained ‘access_token’ by including it in your request-headers

To test everything is working as expected, you can perform the following endpoint call

--- ENDPOINT ---

Domain: equidata 
Url: /whoami
Method: GET

--- EXAMPLE RESPONSE ---
{
    environment: 'testing',
    client: 'PSV'
}

When your token expires, you can simply perform this same procedure again to gain a new token.

Avoid creating a new access token for every request made, by caching the access token in a safe place.

# Webhook

In order to make two-way synchronisation work, every time something happened in the KBRSF application, we will notify each external party by calling the web hook (provided by the external party)

If a change was triggered by an external party, that external party will not be notified for that change.

The external party is responsible for handling the “events” by calling the appropriate endpoints (if needed) to gain the last up to date information about that event and keeping their system in sync.

All defined events can be found within their specific endpoint section in this documentation.

# Authentication

Each external party will provide us with an EXTERNAL_PARTY_TOKEN per environment to do webhook authentication. We will not be using full blown OAuth2 in this case, however the token will still be send as a Bearer token.

Every webhook request will have the following headers set

- Accept: application/json
- Content-Type: application/json
- Cache-Control: no-cache
- Authorization: Bearer EXTERNAL_PARTY_TOKEN

# Responses

We expect a webhook to respond quickly using a 200 Status code. The webhook itself should only be responsible for "recording the event". It preferably should not be "handled" in that same request (before responding 200).

This would allow the external application to recover from failures without the need to resynchronize an entire entity (or worse all entities)

Using a queue system might be a viable approach.

# Endpoint

Below some examples of the actual trigger requests.

--- ENDPOINT ---

Domain: external party 
Url: /external_webhook
Method: POST

--- EXAMPLE PAYLOAD ---
{
    "event": "CLUB_LICENSE_CREATED",
    "data": {
        "club_id": 123,
        "year": 2021
    }
}
--- EXAMPLE PAYLOAD 2 ---
{
    "event": "CLUB_UPDATED",
    "data": {
        "id": 123
    }
}

# General info

# Status codes

Whenever calling the webservice endpoints, the following general errors can occur.

Status Code Message Solution
401 Unauthorized Provide a valid Bearer access token in your request Authorization header
403 Forbidden You do not have access to the required scopes
404 Not Found We could not find an entity for the given ids. Verify url structure or check for data integrity
422 Unprocessable entity Validation failed, check the response for validation error messages
500 Internal Server error Your request made our server fail or our server has general issues. Get in touch
502 Bad Gateway Your request made our server fail or our server has general issues. Get in touch
503 Slow down Implement a retry/slowdown mechanism or get in touch to raise the limit.

# Validation

When validation fails the response status code and message will be 422: Unprocessable entity error

The Response body will always contain information about why the validation fails. There will either be a general indication error or field specific errors.

Every key present in the errors payload, represents a piece of expected/given request data representing one of the following:

  • a main field
  • a field for a nested object
  • a value for an array entry (0-indexed)
  • a field of an object for an array entry (0-indexed)
--- EXAMPLE ENDPOINT ---

Domain: equidata 
Url: /people
Method: POST

--- EXAMPLE RESPONSE ---
{
    "message": "The given data was invalid.",
    "errors": {
        "national_register_number": [ // a main field
            "The national register number has already been taken."
        ],
        "domicile_address.street": [ // a field for a nested object
            "The street field is required."
        ],
        "emails.2": [ // 3th entry is invalid
            "Invalid email"
        ],
        "people.0.name": [ // 1st object entry is invalid
            "A persons name is required."
        ],
    }
}

The language of the error messages can be set by adding a query parameter 'lang', e.g.:

    https://{url}/athletes/1?lang=nl

Following languages are allowed: nl, fr, en.

# Pagination

All endpoints marked as Paginated: true in the "Endpoints" section of this documentation, will behave in the following way:

  • they will return the page based on the GET “page” parameter, defaults to page 1 if not specified
  • the amount of items per page can be adjusted using a GET "size" parameter, defaults to 15 if not specified. The max amount of items returned is limited at 500.
  • the response will contain “pagination meta data” in a consistent format.
  • filters are specified for endpoints that have filters available
--- EXAMPLE ENDPOINT ---

Domain: equidata 
Paginated: true
Url: /clubs?page=2
Method: GET

--- EXAMPLE RESPONSE ---
{
    "data": [
        // actual entities
    ],
    "links": {
        "first": "http://api.kbrsf.be/clubs?page=1",
        "last": "http://api.kbrsf.be/clubs?page=4",
        "prev": "http://api.kbrsf.be/clubs?page=1", // nullable
        "next": "http://api.kbrsf.be/clubs?page=3" // nullable
    },
    "meta": {
        "current_page": 1,
        "from": 1, // 1-indexed
        "last_page": 4,
        "path": "http://api.kbrsf.be/clubs",
        "per_page": 15,
        "to": 15, // 1-indexed
        "total": 50
    }
}

When an endpoint is not specified as Paginated: true, you can assume all results have been returned.

# Throttling

All the webservice endpoints are being throttled. Currently the rate limit is set at 200 calls/min. To avoid hitting rate limits, you can either

  • wrap your api consumers with a slow down mechanism
  • wrap your api consumers with a retry mechanism
  • request larger pages for batch processing

If the current rate limit proves to be to low, we can upgrade it to a higher limit.

All response headers will contain information about your current rate

- X-RateLimit-Limit: 200
- X-RateLimit-Remaining: 43