General

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 the API provides.

Tip

In parallel to the API itself, a Python wrapper library called kadi-apy is also under development, which can be used as a higher-level alternative to using the API directly.

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 (see the Endpoints section for possible version values). This way, an explicit API version to use can be specified, which may be useful to ensure that applications or scripts that make use of the API do not break in case backwards incompatible changes to the API are introduced. When omitting the explicit version, the latest (in development) version will always be used by default.

Using the API requires a suitable client that can send HTTP requests. Some examples include simple command line tools such as curl, graphical tools such as Postman or any programming language that supports sending HTTP requests, either natively or through a third-party library, 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 accustomed to the API, however, please note that this requires being logged in.

Generally, using the API requires a suitable 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 sub-sections. 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. In the end, the header should look like the following:

Authorization: Bearer <token>

Note that the <token> placeholder has to be substituted with an actual access token. Without including this header, all endpoints will return a 401 status code. See 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 optionally be set, which will prohibit using the token beyond the specified date. If no date is given, 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.

Warning

It is generally recommended to limit the lifetime and scope of PATs as much as possible for the specific need of the 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. Similarly 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 parameter will currently always be ignored.

Warning

It is generally recommended to limit the scope of applications as much as possible for the specific need 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 and the Refresh Token grant type. This means that an application needs to ensure that it can keep the issued client secret confidential. The following endpoints can be used to perform the OAuth2 authorization flow:

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. Mainly used to prevent authorization code interception attacks.

  • code_challenge_method (Optional) - The method used for deriving the code challenge 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 when using 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 previously. (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 formats

As is the case for many HTTP APIs, the JSON format is used for most requests where data needs to be sent as part of the request body. Accordingly, for most requests the Content-Type header should be set to application/json. When a file is included in a request, the multipart/form-data content type should be used instead, including the actual file data as well as any additional metadata. Note that many clients will set such headers automatically, depending on the request body being sent.

Common response formats

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 resources at once, only a chunk of the resources is returned, e.g. the first 10 requested records ordered by their last modification date. 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 (e.g. _links.files to get the files of a record) or to perform certain actions relating to a resource (e.g. _actions.delete to delete a record). Both properties can either be included as an additional property of a resource, or as an additional meta property, similar to the _pagination property in the example above. This is mostly for convenience, so any related endpoints can be retrieved from the data itself withouth needing to build 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 if not all 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 object or form data. It will be returned whenever the body is either missing, has a syntax error or includes any invalid keys or values. For the last case, additional information about the errors will be included in 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. For the last case, all scopes the endpoint requires will be included as an array in 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, as usual, 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 the 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, some additional HTTP headers will be returned, the most useful being Retry-After, which specifies the amount of seconds to wait until another request may be sent.