Introduction

The HTTP API of Kadi4Mat makes it possible to programmatically interact with most of the resources that can be managed via the graphical user interface by sending suitable HTTP requests to the different endpoints (i.e. URLs) the API provides.

Tip

In parallel to the API itself, a Python wrapper library called kadi-apy is also available, which can be used as a higher-level alternative to using the API directly. However, it is still recommended to read through the following sections.

Using the API

All API endpoints start with the /api prefix, e.g. http://kadi4mat.example.edu/api. This prefix can optionally be followed by a version, e.g. /api/v1, which specifies an explicit API version to use. When omitting this version, the latest stable version will be used by default. Please see the Endpoints section for a list of supported API versions.

Note

Specifying an explicit (stable) API version is recommended to ensure that applications or scripts that make use of the API do not break unexpectedly in case backwards incompatible changes to the latest (in development) API are introduced.

Interacting with the API requires a suitable client that can handle HTTP requests. Some examples include simple command line tools such as curl, graphical tools such as Postman or most programming languages, either natively or via third-party libraries, such as the popular requests library for Python.

Tip

For some endpoints (namely, all endpoints supporting GET requests), the normal web browser can be used as well by simply typing the corresponding URL in the browser’s address bar. This is an easy way to get familiar with the API. Please note that this requires being logged in to Kadi4Mat.

Authorization

Next to a suitable client, using the API usually requires an access token for authorization. Each token will operate with the same permissions as the user it belongs to. The currently available token types are explained in the following subsections. Independent of token type, the actual token value has to be included as bearer token in an Authorization HTTP header for each request that should use the token. This header must look like the following:

Authorization: Bearer <token>

Note that the <token> placeholder has to be substituted with the actual access token. Without including this header, all endpoints will return a 401 status code. Please also refer to the later section about other common error status codes.

Personal Access Tokens

Personal access tokens (PATs) can directly be created via the web interface of Kadi4Mat in the menu found in Settings > Access tokens. When creating the token, an expiration date can be set, which will prohibit the use of the token beyond the specified date. If no date is specified, the token will never expire. As another security measure, a token can be limited to one or multiple scopes. Scopes can restrict a token’s access to specific resources or actions, i.e. some endpoints may not be usable at all or they may return limited information about a resource. If no scopes are selected, full access is granted to the token.

Note

It is generally recommended to limit the lifetime and scope of PATs as much as possible for the specific needs of each token. In any case, PATs should be treated like passwords, so make sure to keep them safe.

OAuth2 Tokens

OAuth2 tokens can be created by first registering an application capable of performing the OAuth2 authorization flow via the web interface of Kadi4Mat in the menu found in Settings > Applications. When creating an application, one or more scopes can be selected. Similar to PATs, if no scopes are selected, full access will be granted to the access token. The selected scopes will be used automatically whenever requesting a new access token, i.e. scopes requested via the scope request parameter will be ignored.

Note

It is generally recommended to limit the scope of applications as much as possible for the specific needs of the application.

After creating the application, a randomly generated Client ID and Client Secret will be issued, which are required to perform the OAuth2 authorization flow using different grant types. Currently, only two grant types are supported: the Authorization Code Flow, with optional Proof Key for Code Exchange (PKCE), and the Refresh Token Flow.

Warning

For applications that cannot keep the issued client secret confidential, using PKCE is required. For other applications it is still recommended in order to prevent authorization code interception attacks.

The following endpoints can be used to perform the supported OAuth2 authorization flows:

GET /oauth/authorize

Ask a user to grant access to your registered application. The user may need to log in to Kadi4Mat first. If granted, a new authorization code for use in the authorization_code grant type will be returned in the redirect using the code query parameter. Optionally, the query parameters will also include the state value.

Query parameters:

  • response_type - Must be set to code.

  • client_id - The client ID that was generated when registering the application.

  • redirect_uri - One of the redirect URIs that was specified when registering the application.

  • state (Optional) - A randomly generated string that the server will return unaltered after the user authorizes the application, which the application should verify. Mainly used to prevent CSRF attacks.

  • code_challenge (Optional) - The code challenge value when using PKCE. Must be derived from a randomly generated code verifier.

  • code_challenge_method (Optional) - The method used for deriving the code challenge value from the code verifier. One of S256 or plain.

POST /oauth/token

Exchange an authorization code for an access token (when using the authorization_code grant type) or request a new access token using a refresh token (when using the refresh_token grant type). The returned access token will always be valid for one hour. Note that scope information is only returned if at least one scope was selected when creating the application.

Independent of grant type, the response will also include a new refresh token, which must be used for the next refresh request via the refresh_token grant type. Until performing such a request, refresh tokens currently won’t expire.

Request form data (Encoding: application/x-www-form-urlencoded):

  • grant_type - One of authorization_code or refresh_token.

  • client_id - The client ID that was generated when registering the application.

  • client_secret - The client secret that was generated when registering the application.

  • code - The authorization code that was received in the previous step. (For grant type authorization_code)

  • redirect_uri - The same redirect URI that was sent in the previous step. (For grant type authorization_code)

  • code_verifier (Optional) - The derived code verifier when using PKCE. (For grant type authorization_code)

  • refresh_token - The refresh token that was received in the previous step. (For grant type refresh_token)

It is also possible to revoke existing OAuth2 tokens using the corresponding revocation endpoint:

POST /oauth/revoke

Revoke an existing access/refresh token pair.

Request form data (Encoding: application/x-www-form-urlencoded):

  • token - The token that should be revoked. May either be an access token or refresh token. Note that the entire access/refresh token pair will be revoked, independent of the given token type.

  • client_id - The client ID that was generated when registering the application.

  • client_secret - The client secret that was generated when registering the application.

  • token_type_hint (Optional) - A hint about the token type submitted for revocation. One of access_token or refresh_token.

Common request/response formats

As is the case for many HTTP APIs, the JSON format is used for all requests where data needs to be sent as part of the request body. Accordingly, the Content-Type header should be set to application/json, which many clients will handle automatically. A notable exception are file uploads, where the application/octet-stream content type is used instead.

The JSON format is also used for all responses that return any data, except for endpoints returning actual file data. Generally, there are endpoints that return a single deserialized resource, i.e. a single JSON object, or a list of multiple resources wrapped inside an items property, which itself is an array of JSON objects. In the latter case, most endpoints return paginated data, i.e. instead of returning all requested resources at once, only a chunk of them is returned. In this case, the response includes a _pagination property like the following:

{
    "items": [
        // The actual data.
    ],
    "_pagination": {
        "page": 2,
        "per_page": 10,
        "total_pages": 3,
        "total_items": 25,
        "_links": {
            "prev": "https://...?page=1&...",
            "self": "https://...?page=2&...",
            "next": "https://...?page=3&..."
        }
    }
}

Two other common properties include the _links property and the _actions property. These properties consist of different endpoints that can be used to either get additional information about a resource (typically via a GET request) or to perform certain actions related to a resource. Both properties can either be included as an additional property of each resource, or as an additional meta property, including as part of the _pagination property like in the example above. This is mostly for convenience, so any related endpoints can be retrieved from the data itself without needing to construct them manually.

Whenever some error occurs, i.e. a status code greater than or equal to 400 is returned, a corresponding error object will be returned as well. This object always includes at least the status code, a message and a description about the error. The following example shows a generic error object for the status code 400:

{
    "code": 400,
    "description": "The browser (or proxy) sent a request that this server could not understand.",
    "message": "Bad Request"
}

Common error status codes

In the following, some common error status codes are listed, which can be returned by most endpoints.

400 (Bad Request)

This status code is relevant for any endpoint that requires data to be sent in the HTTP request body as JSON. It will be returned whenever the body is either missing, has a syntax error or includes any invalid keys or values. In the latter case, additional information about the errors will be included as part of the error response object in the errors property.

401 (Unauthorized)

This status code will be returned if either no valid access token is included in the request, it has expired or its scope is insufficient for the requested endpoint. In the latter case, all scopes the endpoint requires will be included as an array as part of the error response object in the scopes property. The status code may also be returned if the user associated with an access token is currently not authorized to use the API at all, e.g. if the corresponding account is inactive.

403 (Forbidden)

This status code is relevant for any endpoint that requires certain permissions to create, retrieve or update data. It will be returned if the permissions of the user associated with the provided access token are insufficient.

404 (Not Found)

This status code will be returned if a specific resource, usually requested via a corresponding dynamic endpoint, does not exist. Furthermore, the same status code will be returned for non-API endpoints or endpoints that do not exist at all.

405 (Method Not Allowed)

This status code will be returned if the HTTP method used for a request is not supported by the requested endpoint.

429 (Too Many Requests)

This status code will be returned if a rate limit configured in the application has been reached, i.e. too many requests have been sent in too short a time. In addition to the usual error response, an additional HTTP header Retry-After will be returned, which specifies the amount of seconds to wait until another request may be sent.