[toc]

This guide describes the **read-only Open API** endpoints exposed by the SciLifeLab Serve system. The audience is expected to be comfortable with HTTP requests, query parameters, and JSON responses, but not necessarily familiar with Django/DRF internals.

## Base path and versioning

The URLs shown in the docstrings use a versioned prefix (example: `/openapi/v1/...`). Versioning is read from `request.version` and is used by the **API Info** endpoint.

In examples below, the base is written as:

- `/<BASE>/<VERSION>/...` (example: `/openapi/v1/...`)

## Common response shape

Most endpoints return JSON shaped like:

- `{"data": ...}` (typical for list/lookup/stats)
- `{"app": ...}` (single public app retrieve)
- `{"status": true}` (healthcheck)
- or error payloads like `{"error": "..."}`

Timestamps and fields come directly from the system objects and may include date-time values.

---

# Generic API endpoints

## Health check: `are-you-there`

**GET** `/<BASE>/<VERSION>/are-you-there`

Docstring wording:

> Most simple API endpoint useful for testing  
> and verifications.  
> :returns bool: true

---

## System version: `system-version`

**GET** `/<BASE>/<VERSION>/system-version`

Docstring wording:

> Gets the version of the deployed application.  
> :returns dict: A dictionary of system version information.

Returned fields include:
- `system-version`
- `build-date`
- `image-tag`

---

## API information: `api-info`

**GET** `/<BASE>/<VERSION>/api-info`

Docstring wording:

> The Open API basic API information endpoints, in accordance to the standard  
> https://dev.dataportal.se/rest-api-profil/versionhantering  
> :returns dict: A dictionary of app-info information.

This endpoint returns different payloads depending on the requested API version. It explicitly handles:

- `beta`
- `v1`

### Version: `beta`

Returned fields:
- `apiName`
- `apiVersion`
- `apiReleased`
- `apiDocumentation`
- `apiStatus`
- `latest-api-version`

### Version: `v1`

Returned fields:
- `apiName`
- `apiVersion`
- `apiReleased`
- `apiDocumentation`
- `apiStatus`
- `latest-api-version`

Notes:
- `apiReleased` and `apiDocumentation` are currently `"TODO"` in code.
- `latest-api-version` is `"v1"` in both cases.

---

# Public Apps resource API

## List public apps: `public-apps`

**GET** `/<BASE>/<VERSION>/public-apps`

Docstring wording:

> This endpoint gets a list of public apps.  
> :returns list: A list of app information, ordered by date created from the most recent to the oldest.
>
> Available GET query parameters:  
> - limit: integer. Optional. Determines the maximum number of records to return.
>
> Example requests:  
> /openapi/v1/public-apps  
> /openapi/v1/public-apps?limit=5


### Query parameters

- `limit` (optional, integer): maximum number of records to return.
  - If provided but cannot be parsed as an integer, the API returns HTTP 403 with an error message.

### What is included

- Only app instances with `access="public"` are included.
- Deleted apps are excluded based on derived `app_status`.

### Returned fields (per app)

Each list item includes, at minimum:

- `id`
- `name`
- `url`
- `description`
- `created_on`
- `updated_on`
- `access`
- `k8s_user_app_status`
- `latest_user_action`
- `app_status` (computed)
- `app_type`
- `table_field.url` (for compatibility with previous schema)

The following field is intentionally omitted:

- `app_id` (removed because it only refers to the app type)

### Ordering and limiting

- Items are sorted by `created_on` (most recent first).
- `limit` truncation is applied after sorting.

### Example output

```json
{
  "data": [
    {
      "id": 123,
      "name": "My Public App",
      "url": "https://…",
      "description": "…",
      "created_on": "2025-12-01T10:00:00Z",
      "updated_on": "2025-12-10T12:00:00Z",
      "access": "public",
      "latest_user_action": "…",
      "k8s_user_app_status": "Running",
      "app_status": "Running",
      "app_type": "Streamlit",
      "table_field": {"url": "https://…"}
    }
  ]
}
```

### Errors

- HTTP 403 if `limit` is invalid.
- HTTP 500 if the system is unable to collect the list of public apps.

---

## Retrieve a single public app: `public-apps/<id>`

**GET** `/<BASE>/<VERSION>/public-apps/<int:pk_in>`

Docstring wording:

> This endpoint retrieves a single public app instance.  
> :pk_in: The primary key of the public app to return.  
> :returns dict: A dict of app information.

### Path parameter

- `pk_in` (required, integer): the primary key of the app instance.

If `pk_in` is missing or less than 1, the API returns HTTP 403.

### Returned fields

The response contains an `app` object with:

- `id`
- `name`
- `url`
- `description`
- `created_on`
- `updated_on`
- `access`
- `k8s_user_app_status`
- `app_status`
- `app_type`

The following field is intentionally omitted:

- `app_id`

### Not-found and validation behavior

The endpoint returns HTTP 404 in the following cases:

- No app exists with the given id.
- The app exists but is of an incorrect type.
- The app is not public.
- The app has been deleted.

### Server errors

Unexpected failures retrieving the app return HTTP 500.

---

# Content statistics resource API

## Content statistics: `content-stats`

**GET** `/<BASE>/<VERSION>/content-stats`

Docstring wording:

> The Content Stats API with read-only methods to get aggregated statistics  
> about the content in the system.
>
> The top-level elements nested under data include:  
> - stats_date_utz  
> - stats_success  
> - stats_message  
> - stats_notes  
> - n_projects  
> - n_users  
> - n_apps  
> - n_apps_public  
> - apps_by_type  
> - new_users_by_year  
> - users_by_university  
> - apps_by_image_registry

### Interpretation notes

- `stats_date_utz` is set to the current time in UTC.
- `stats_success` indicates whether all statistic sub-queries succeeded.
- If parts fail, `stats_success` is set to false and `stats_message` accumulates one or more messages.
- Numeric counts default to `-1` if the related computation fails.
- `apps_by_type` starts from a predefined set (`customapp`, `dashapp`, `gradio`, `shinyapp`, `streamlit`, `tissuumaps`) and may grow dynamically.
- All shiny-based apps are grouped under `shinyapp`.
- Image registry classification includes `dockerhub`, `ghcr`, and `noimage`.

---

# Supplementary lookups API

## Universities lookup: `lookups/universities`

**GET** `/<BASE>/<VERSION>/lookups/universities`

Docstring wording:

> This method handles the /universities endpoint.  
> With a query string get parameter "code" it returns a single university.  
> Without a query string parameter it returns the entire list.
>
> :returns list of dict or dict: The dict contains attributes code and name.

### Query parameters

- `code` (optional): university code.
  - Converted to lowercase.
  - Must be alphabetic and no longer than 10 characters.

### Behavior

- With no query parameters, the endpoint returns the full list of universities.
- With `code=<value>`, the endpoint returns a single university entry.

### Errors

- Validation error if `code` is invalid.
- Not found error if no university matches the provided code.
- Method not allowed if unsupported query parameters are provided.
- Server error if the universities JSON file cannot be read.

---

## Departments lookup: `lookups/departments`

**GET** `/<BASE>/<VERSION>/lookups/departments`

Docstring wording:

> Gets a list of departments.

### Behavior

- Returns the list of departments as defined in the system JSON file.

### Errors

- Server error if the departments JSON file cannot be read.

---

# Error handling expectations

Depending on endpoint and failure mode, responses may include:

- HTTP 403 for invalid user input (for example, invalid `limit` or app id).
- HTTP 404 for non-existent, deleted, or non-public resources.
- HTTP 500 for unexpected server-side failures.
- Standard Django REST Framework exceptions such as `ValidationError`, `NotFound`, `MethodNotAllowed`, and `APIException`.

The SciLifeLab Serve user guide is powered by django-wiki, an open source application under the GPLv3 license. Let knowledge be the cure.