> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nekt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# GitLab as a data source

> Bring your GitLab groups, projects, issues, merge requests, commits, CI/CD pipelines and jobs to Nekt.

GitLab is a DevOps platform for source code, code review, issue tracking and CI/CD. This connector reads your GitLab (GitLab.com or a self-managed GitLab) through the GitLab REST API and brings your groups and projects, their members, issues and merge requests with their comments and approvals, commits, branches, tags, releases, CI/CD pipelines and jobs, labels, milestones and epics.

> [!NOTE]
>
> The connector only reads data. It never creates, changes or deletes anything in GitLab.

## Configuring GitLab as a Source

In the [Sources](https://app.nekt.ai/sources) tab, click on the "Add source" button located on the top right of your screen. Then, select the GitLab option from the list of connectors.

Click **Next** and you'll be prompted to add your access.

### 1. Add account access

The connector uses a GitLab **access token** with the **read\_api** scope. You can use:

* a **personal access token**: in GitLab, click your avatar > **Edit profile** > **Access tokens** > **Add new token**;
* a **group access token** (reads everything in the group and its subgroups): open the group > **Settings** > **Access tokens**;
* a **project access token** (reads one project): open the project > **Settings** > **Access tokens**.

Select the **read\_api** scope and, for group and project tokens, at least the **Reporter** role. Set an expiration date you will remember: when the token expires, the source stops until you paste a new one.

The following configurations are available:

* **Access Token**: the token you created. It is stored encrypted.
* **GitLab URL**: keep `https://gitlab.com` for GitLab.com. For a self-managed GitLab, use the address you open in the browser (for example `https://gitlab.mycompany.com`).
* **Start Date**: the earliest date read. Issues, merge requests, pipelines and epics updated since this date, and commits made since this date, are synced on the first run.
* **Groups** (advanced, optional): the groups to sync, one per line, as their path (for example `my-group` or `my-group/my-subgroup`, as in the group's URL) or numeric ID. Each group's subgroups and projects are included.
* **Projects** (advanced, optional): extra projects to sync, one per line, as their path (for example `my-group/my-project`) or numeric ID.

If **Groups** and **Projects** are empty, the connector syncs every group the token's user is a member of and every project where it has at least the **Reporter** role. It never reads the public projects of other people on GitLab.com.

> [!WARNING]
>
> A self-managed GitLab must be reachable from the internet over HTTPS. If your GitLab only accepts known addresses, allow Nekt's addresses (ask Nekt support for them).

> [!TIP]
>
> A token whose user has the **Guest** role cannot read the code, pipelines or releases of private projects. Use a user (or group token) with at least the **Reporter** role in the projects you want to sync.

Once you're done, click **Next**.

### 2. Select streams

Choose which data streams you want to sync. For faster extractions, select only the streams that are relevant to your analysis. You can select entire groups of streams or pick specific ones.

> Tip: The stream can be found more easily by typing its name.

Select the streams and click **Next**.

### 3. Configure data streams

Customize how you want your data to appear in your catalog. Select the desired layer where the data will be placed, a folder to organize it inside the layer, a name for each table (which will effectively contain the fetched data) and the type of sync.

* **Layer**: choose between the existing layers on your catalog. This is where you will find your new extracted tables as the extraction runs successfully.
* **Folder**: a folder can be created inside the selected layer to group all tables being created from this new data source.
* **Table name**: we suggest a name, but feel free to customize it. You have the option to add a **prefix** to all tables at once and make this process faster!
* **Sync Type**: keep the suggested type. Issues, merge requests, comments, approvals, commits, pipelines, jobs and epics are **Incremental**: each sync reads only what changed since the previous one, project by project. Groups, projects, members, users, labels, milestones, branches, tags and releases are **Full table**: each sync reads them whole and replaces the table.

Once you are done configuring, click **Next**.

### 4. Configure data source

Describe your data source for easy identification within your organization, not exceeding 140 characters.

To define your [Trigger](https://docs.nekt.com/runs/scheduling-and-triggers.md), consider how often you want data to be extracted from this source. Engineering teams usually sync every few hours or daily.

Optionally, you can configure Delta Log Retention and determine for how long we should store old states of this table as it gets updated. Read more about this resource [here](https://docs.nekt.com/get-started/core-concepts/resource-control).

Once you are ready, click **Next** to finalize the setup.

### 5. Check your new source

You can view your new source on the [Sources](https://app.nekt.ai/sources) page. If needed, manually trigger the source extraction by clicking on the arrow button. Once executed, your data will appear in your Catalog.

> [!WARNING]
>
> For you to be able to see it on your [Catalog](https://app.nekt.ai/catalog), you need at least one successful source run.

# Streams and Fields

## Available streams

The table below lists every stream, its **slug** (the exact identifier to pass when creating the source via API) and a short description.

| Stream | Slug | Sync | Description |
| - | - | - | - |
| Groups | `groups` | Full table | Groups in the source's scope, with their subgroups. |
| Projects | `projects` | Full table | Projects in the source's scope, with their settings. |
| Group members | `group_members` | Full table | Direct members of each group and their role. |
| Project members | `project_members` | Full table | Direct members of each project and their role. |
| Users | `users` | Full table | Every user who is a member of a group or project of the source. |
| Labels | `labels` | Full table | Labels of the groups and projects. |
| Milestones | `milestones` | Full table | Milestones of the groups and projects. |
| Branches | `branches` | Full table | Branches of each repository, with their last commit. |
| Tags | `tags` | Full table | Tags of each repository. |
| Releases | `releases` | Full table | Releases of each project, with notes and assets. |
| Issues | `issues` | Incremental | Issues of every project, open and closed. |
| Issue notes | `issue_notes` | Incremental | Comments and system notes of the issues. |
| Merge requests | `merge_requests` | Incremental | Merge requests of every project, in every state. |
| Merge request notes | `merge_request_notes` | Incremental | Comments, review comments and system notes of the merge requests. |
| Merge request approvals | `merge_request_approvals` | Incremental | Who approved each merge request and how many approvals are missing. |
| Commits | `commits` | Incremental | Commits of every branch of each repository. |
| Pipelines | `pipelines` | Incremental | CI/CD pipelines of every project. |
| Jobs | `jobs` | Incremental | Jobs of each pipeline, including retried ones, with durations. |
| Epics | `epics` | Incremental | Epics of every group (GitLab Premium and Ultimate only). |

To join the tables: `projects.namespace_id` → `groups.id`; `issues.project_id`, `merge_requests.project_id`, `commits.project_id`, `pipelines.project_id` → `projects.id`; `issue_notes.issue_id` → `issues.id`; `merge_request_notes.merge_request_id` and `merge_request_approvals.merge_request_id` → `merge_requests.id`; `jobs.pipeline_id` → `pipelines.id`; `author_id`, `user_id` and member `id` → `users.id`.

### What each table needs

| Tables | GitLab feature | Role the token needs |
| - | - | - |
| Groups, projects, members, users, labels, milestones | — | Guest |
| Issues, issue notes | Issues turned on in the project | Guest (Reporter for confidential issues) |
| Merge requests, merge request notes, merge request approvals | Merge requests turned on | Reporter in private projects |
| Commits, branches, tags, releases | Repository turned on and not empty | Reporter in private projects |
| Pipelines, jobs | CI/CD turned on | Reporter in private projects |
| Epics | GitLab Premium or Ultimate | Guest in the group |

Projects where a feature is turned off are simply not read for that feature's tables.

## Fields by stream

Fields marked String (JSON) hold a nested object or list as JSON text; use your warehouse's JSON functions to read them. Every table also has `extra_fields`: fields GitLab adds in the future land there (as JSON) until they get their own column.

> [accordion] Groups
>
> The groups in the source's scope, with their subgroups.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | ID of the group |
> | `name` | String | Name of the group |
> | `path` | String | URL path of the group (last segment) |
> | `full_name` | String | Full name of the group, including its parent groups |
> | `full_path` | String | Full URL path of the group, including its parent groups |
> | `description` | String | Description of the group |
> | `visibility` | String | Visibility of the group (private, internal, public) |
> | `parent_id` | Integer | ID of the parent group; empty for a top-level group |
> | `web_url` | String | URL of the group page |
> | `avatar_url` | String | URL of the group's avatar |
> | `created_at` | DateTime | When the group was created |
> | `archived` | Boolean | Whether the group is archived |
> | `marked_for_deletion_on` | Date | Date the group is scheduled for deletion, if any |
> | `default_branch` | String | Default branch name for new projects in the group |
> | `default_branch_protection` | Integer | Default branch protection level for new projects |
> | `default_branch_protection_defaults` | String (JSON) | Default branch protection settings for new projects |
> | `project_creation_level` | String | Who can create projects in the group |
> | `subgroup_creation_level` | String | Who can create subgroups in the group |
> | `share_with_group_lock` | Boolean | Whether sharing projects with other groups is blocked |
> | `require_two_factor_authentication` | Boolean | Whether members must use two-factor authentication |
> | `two_factor_grace_period` | Integer | Hours members have to set up two-factor authentication |
> | `request_access_enabled` | Boolean | Whether users can request access to the group |
> | `lfs_enabled` | Boolean | Whether Git LFS is enabled for the group's projects |
> | `emails_enabled` | Boolean | Whether email notifications are enabled |
> | `mentions_disabled` | Boolean | Whether @group mentions are disabled |
> | `organization_id` | Integer | ID of the organization the group belongs to |
> | `statistics` | String (JSON) | Storage statistics of the group, when returned |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Full table.

> [accordion] Projects
>
> The projects in the source's scope.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | ID of the project |
> | `name` | String | Name of the project |
> | `name_with_namespace` | String | Name of the project including its group or user namespace |
> | `path` | String | URL path of the project (last segment) |
> | `path_with_namespace` | String | Full URL path of the project, including its namespace |
> | `description` | String | Description of the project |
> | `default_branch` | String | Default branch of the repository |
> | `visibility` | String | Visibility of the project (private, internal, public) |
> | `archived` | Boolean | Whether the project is archived |
> | `empty_repo` | Boolean | Whether the repository has no commits |
> | `created_at` | DateTime | When the project was created |
> | `updated_at` | DateTime | When the project was last updated |
> | `last_activity_at` | DateTime | Time of the last activity in the project (pushes, issues, merge requests...) |
> | `web_url` | String | URL of the project page |
> | `http_url_to_repo` | String | HTTPS clone URL of the repository |
> | `ssh_url_to_repo` | String | SSH clone URL of the repository |
> | `readme_url` | String | URL of the repository README |
> | `avatar_url` | String | URL of the project's avatar |
> | `topics` | Array of strings | Topics assigned to the project |
> | `tag_list` | Array of strings | Topics assigned to the project (deprecated name of topics) |
> | `forks_count` | Integer | Number of forks of the project |
> | `star_count` | Integer | Number of stars of the project |
> | `open_issues_count` | Integer | Number of open issues |
> | `namespace` | String (JSON) | Group or user namespace that owns the project |
> | `namespace_id` | Integer | ID of the namespace that owns the project |
> | `namespace_full_path` | String | Full path of the namespace that owns the project |
> | `namespace_kind` | String | Kind of the owning namespace (group or user) |
> | `creator_id` | Integer | ID of the user who created the project |
> | `owner` | String (JSON) | Owner of a project in a personal namespace |
> | `forked_from_project` | String (JSON) | Project this one was forked from, if any |
> | `issues_enabled` | Boolean | Whether issues are enabled |
> | `merge_requests_enabled` | Boolean | Whether merge requests are enabled |
> | `jobs_enabled` | Boolean | Whether CI/CD jobs are enabled |
> | `wiki_enabled` | Boolean | Whether the wiki is enabled |
> | `snippets_enabled` | Boolean | Whether snippets are enabled |
> | `container_registry_enabled` | Boolean | Whether the container registry is enabled |
> | `issues_access_level` | String | Who can access issues (disabled, private, enabled) |
> | `repository_access_level` | String | Who can access the repository (disabled, private, enabled) |
> | `merge_requests_access_level` | String | Who can access merge requests (disabled, private, enabled) |
> | `builds_access_level` | String | Who can access CI/CD (disabled, private, enabled) |
> | `releases_access_level` | String | Who can access releases (disabled, private, enabled) |
> | `wiki_access_level` | String | Who can access the wiki (disabled, private, enabled) |
> | `merge_method` | String | Merge method of the project (merge, rebase\_merge, ff) |
> | `squash_option` | String | Squash commits option (never, always, default\_on, default\_off) |
> | `only_allow_merge_if_pipeline_succeeds` | Boolean | Whether merge requests need a successful pipeline to merge |
> | `only_allow_merge_if_all_discussions_are_resolved` | Boolean | Whether merge requests need all threads resolved to merge |
> | `ci_config_path` | String | Path of the CI/CD configuration file |
> | `shared_with_groups` | String (JSON) | Groups the project is shared with, and their access level |
> | `permissions` | String (JSON) | Access level of the token's user in the project and its group |
> | `statistics` | String (JSON) | Storage statistics of the project, when returned |
> | `marked_for_deletion_on` | Date | Date the project is scheduled for deletion, if any |
> | `links` | String (JSON) | Links to the project's API resources |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Full table.

> [accordion] Group members
>
> Direct members of each group, with their role.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `group_id` | Integer | ID of the group |
> | `id` | Integer | ID of the GitLab user |
> | `username` | String | Username of the user |
> | `name` | String | Display name of the user |
> | `state` | String | Account state of the user (active, blocked, deactivated...) |
> | `locked` | Boolean | Whether the user account is locked |
> | `avatar_url` | String | URL of the user's avatar |
> | `web_url` | String | URL of the user's profile page |
> | `access_level` | Integer | Role of the member: 5 Minimal access, 10 Guest, 15 Planner, 20 Reporter, 30 Developer, 40 Maintainer, 50 Owner |
> | `created_at` | DateTime | When the membership was created |
> | `created_by` | String (JSON) | User who added the member |
> | `expires_at` | Date | Date the membership expires, if set |
> | `membership_state` | String | State of the membership (active, awaiting) |
> | `email` | String | Email of the member, only returned to administrators or for SAML/enterprise users |
> | `group_saml_identity` | String (JSON) | SAML identity of the member in the group, when SAML SSO is used |
> | `is_using_seat` | Boolean | Whether the member uses a paid seat (GitLab.com) |
> | `override` | Boolean | Whether the LDAP-synced access level was overridden |
> | `member_role` | String (JSON) | Custom role assigned to the member (Ultimate) |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `group_id`, `id`. **Sync:** Full table.

> [accordion] Project members
>
> Direct members of each project, with their role (members inherited from a group are in group\_members).
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `project_id` | Integer | ID of the project |
> | `id` | Integer | ID of the GitLab user |
> | `username` | String | Username of the user |
> | `name` | String | Display name of the user |
> | `state` | String | Account state of the user (active, blocked, deactivated...) |
> | `locked` | Boolean | Whether the user account is locked |
> | `avatar_url` | String | URL of the user's avatar |
> | `web_url` | String | URL of the user's profile page |
> | `access_level` | Integer | Role of the member: 5 Minimal access, 10 Guest, 15 Planner, 20 Reporter, 30 Developer, 40 Maintainer, 50 Owner |
> | `created_at` | DateTime | When the membership was created |
> | `created_by` | String (JSON) | User who added the member |
> | `expires_at` | Date | Date the membership expires, if set |
> | `membership_state` | String | State of the membership (active, awaiting) |
> | `email` | String | Email of the member, only returned to administrators or for SAML/enterprise users |
> | `group_saml_identity` | String (JSON) | SAML identity of the member in the group, when SAML SSO is used |
> | `is_using_seat` | Boolean | Whether the member uses a paid seat (GitLab.com) |
> | `override` | Boolean | Whether the LDAP-synced access level was overridden |
> | `member_role` | String (JSON) | Custom role assigned to the member (Ultimate) |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `project_id`, `id`. **Sync:** Full table.

> [accordion] Users
>
> Every user who is a direct member of a group or project of the source.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | ID of the GitLab user |
> | `username` | String | Username of the user |
> | `name` | String | Display name of the user |
> | `state` | String | Account state of the user (active, blocked, deactivated...) |
> | `locked` | Boolean | Whether the user account is locked |
> | `avatar_url` | String | URL of the user's avatar |
> | `web_url` | String | URL of the user's profile page |
> | `email` | String | Email of the user, only returned to administrators or for SAML/enterprise users |
> | `public_email` | String | Public email the user chose to show on the profile |
> | `bot` | Boolean | Whether the user is a bot (for example a group or project access token) |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Full table.

> [accordion] Labels
>
> Labels of the groups and projects (each label once, with the group or project that owns it).
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | ID of the label |
> | `name` | String | Name of the label |
> | `description` | String | Description of the label |
> | `color` | String | Background color of the label (hex) |
> | `text_color` | String | Text color of the label (hex) |
> | `priority` | Integer | Priority of the label in the project, if set |
> | `is_project_label` | Boolean | Whether the label belongs to a project (false for group labels) |
> | `archived` | Boolean | Whether the label is archived |
> | `subscribed` | Boolean | Whether the token's user is subscribed to the label |
> | `open_issues_count` | Integer | Number of open issues with the label, when returned |
> | `closed_issues_count` | Integer | Number of closed issues with the label, when returned |
> | `open_merge_requests_count` | Integer | Number of open merge requests with the label, when returned |
> | `group_id` | Integer | ID of the group that owns the label (group labels) |
> | `project_id` | Integer | ID of the project that owns the label (project labels) |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Full table.

> [accordion] Milestones
>
> Milestones of the groups and projects.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | ID of the milestone |
> | `iid` | Integer | Internal ID of the milestone within its project or group |
> | `project_id` | Integer | ID of the project that owns the milestone (project milestones) |
> | `group_id` | Integer | ID of the group that owns the milestone (group milestones) |
> | `title` | String | Title of the milestone |
> | `description` | String | Description of the milestone |
> | `state` | String | State of the milestone (active, closed) |
> | `created_at` | DateTime | When the milestone was created |
> | `updated_at` | DateTime | When the milestone was last updated |
> | `start_date` | Date | Start date of the milestone |
> | `due_date` | Date | Due date of the milestone |
> | `expired` | Boolean | Whether the due date has passed |
> | `web_url` | String | URL of the milestone page |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Full table.

> [accordion] Branches
>
> Branches of each repository, with their last commit.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `project_id` | Integer | ID of the project |
> | `name` | String | Name of the branch |
> | `default` | Boolean | Whether this is the project's default branch |
> | `protected` | Boolean | Whether the branch is protected |
> | `merged` | Boolean | Whether the branch is merged into the default branch |
> | `developers_can_push` | Boolean | Whether developers can push to the branch |
> | `developers_can_merge` | Boolean | Whether developers can merge into the branch |
> | `can_push` | Boolean | Whether the token's user can push to the branch |
> | `web_url` | String | URL of the branch page |
> | `commit` | String (JSON) | Last commit of the branch |
> | `commit_id` | String | SHA of the last commit of the branch |
> | `commit_committed_date` | DateTime | Commit date of the last commit of the branch |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `project_id`, `name`. **Sync:** Full table.

> [accordion] Tags
>
> Tags of each repository.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `project_id` | Integer | ID of the project |
> | `name` | String | Name of the tag |
> | `message` | String | Message of an annotated tag |
> | `target` | String | SHA the tag points to (the tag object for annotated tags) |
> | `protected` | Boolean | Whether the tag is protected |
> | `created_at` | DateTime | When the tag was created (annotated tags) |
> | `commit` | String (JSON) | Commit the tag points to |
> | `commit_id` | String | SHA of the commit the tag points to |
> | `commit_committed_date` | DateTime | Commit date of the commit the tag points to |
> | `release` | String (JSON) | Release attached to the tag, if any |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `project_id`, `name`. **Sync:** Full table.

> [accordion] Releases
>
> Releases of each project.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `project_id` | Integer | ID of the project |
> | `tag_name` | String | Tag the release is attached to |
> | `name` | String | Name of the release |
> | `description` | String | Release notes (Markdown) |
> | `created_at` | DateTime | When the release was created |
> | `released_at` | DateTime | When the release is or was published |
> | `upcoming_release` | Boolean | Whether released\_at is in the future |
> | `historical_release` | Boolean | Whether released\_at is in the past (backdated release) |
> | `author` | String (JSON) | User who created the release |
> | `author_id` | Integer | ID of the user who created the release |
> | `author_username` | String | Username of the user who created the release |
> | `commit` | String (JSON) | Commit the release tag points to |
> | `commit_id` | String | SHA of the commit the release tag points to |
> | `commit_committed_date` | DateTime | Commit date of the release commit |
> | `milestones` | String (JSON) | Milestones linked to the release |
> | `commit_path` | String | Path of the release commit page |
> | `tag_path` | String | Path of the release tag page |
> | `assets` | String (JSON) | Release assets: source archives and links |
> | `evidences` | String (JSON) | Release evidence snapshots |
> | `links` | String (JSON) | Links of the release (self, edit, closed issues...) |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `project_id`, `tag_name`. **Sync:** Full table.

> [accordion] Issues
>
> Issues of every project, open and closed.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | Global ID of the issue |
> | `iid` | Integer | ID of the issue within its project (the number shown in GitLab) |
> | `project_id` | Integer | ID of the project |
> | `title` | String | Title of the issue |
> | `description` | String | Description of the issue (Markdown) |
> | `state` | String | State of the issue (opened, closed) |
> | `type` | String | Type of the issue (ISSUE, INCIDENT, TEST\_CASE, TASK) |
> | `issue_type` | String | Type of the issue, lowercase (issue, incident, test\_case, task) |
> | `created_at` | DateTime | When the issue was created |
> | `updated_at` | DateTime | When the issue was last updated, used as the incremental replication key |
> | `closed_at` | DateTime | When the issue was closed |
> | `closed_by` | String (JSON) | User who closed the issue |
> | `closed_by_id` | Integer | ID of the user who closed the issue |
> | `author` | String (JSON) | User who created the issue |
> | `author_id` | Integer | ID of the user who created the issue |
> | `author_username` | String | Username of the user who created the issue |
> | `assignees` | String (JSON) | Users assigned to the issue |
> | `labels` | Array of strings | Names of the labels on the issue |
> | `milestone` | String (JSON) | Milestone of the issue |
> | `milestone_id` | Integer | ID of the milestone of the issue |
> | `due_date` | Date | Due date of the issue |
> | `confidential` | Boolean | Whether the issue is confidential |
> | `discussion_locked` | Boolean | Whether the discussion is locked |
> | `user_notes_count` | Integer | Number of comments by users |
> | `merge_requests_count` | Integer | Number of merge requests that close the issue |
> | `upvotes` | Integer | Number of thumbs-up reactions |
> | `downvotes` | Integer | Number of thumbs-down reactions |
> | `time_stats` | String (JSON) | Time estimate and time spent (seconds) |
> | `task_completion_status` | String (JSON) | Number of checklist items and how many are done |
> | `has_tasks` | Boolean | Whether the description has checklist items |
> | `task_status` | String | Checklist progress as text |
> | `weight` | Integer | Weight of the issue (Premium) |
> | `severity` | String | Severity of the incident |
> | `health_status` | String | Health status of the issue (Ultimate) |
> | `epic` | String (JSON) | Epic the issue belongs to (Premium) |
> | `epic_iid` | Integer | Internal ID of the epic the issue belongs to (Premium) |
> | `iteration` | String (JSON) | Iteration of the issue (Premium) |
> | `moved_to_id` | Integer | ID of the issue this one was moved to |
> | `duplicated_to_id` | Integer | ID of the issue this one was marked a duplicate of |
> | `blocking_issues_count` | Integer | Number of issues this one blocks |
> | `service_desk_reply_to` | String | Email address of a Service Desk issue's author |
> | `imported` | Boolean | Whether the issue was imported |
> | `imported_from` | String | Source the issue was imported from |
> | `subscribed` | Boolean | Whether the token's user is subscribed to the issue |
> | `references` | String (JSON) | Short, relative and full references of the issue |
> | `web_url` | String | URL of the issue page |
> | `links` | String (JSON) | Links to the issue's API resources |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Incremental by `updated_at`.

> [accordion] Issue notes
>
> Comments and system notes of the issues.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | ID of the comment (note) |
> | `project_id` | Integer | ID of the project |
> | `type` | String | Type of note (DiscussionNote, DiffNote, or empty for a plain comment) |
> | `body` | String | Text of the comment (Markdown); system notes describe the change |
> | `author` | String (JSON) | User who wrote the comment |
> | `author_id` | Integer | ID of the user who wrote the comment |
> | `author_username` | String | Username of the user who wrote the comment |
> | `created_at` | DateTime | When the comment was created |
> | `updated_at` | DateTime | When the comment was last edited |
> | `system` | Boolean | Whether this is a system note (an automatic record of a change) |
> | `noteable_id` | Integer | Global ID of the commented object |
> | `noteable_iid` | Integer | Project ID of the commented object (the number shown in GitLab) |
> | `noteable_type` | String | Type of the commented object (Issue, MergeRequest) |
> | `resolvable` | Boolean | Whether the comment starts a thread that can be resolved |
> | `resolved` | Boolean | Whether the thread is resolved |
> | `resolved_by` | String (JSON) | User who resolved the thread |
> | `resolved_at` | DateTime | When the thread was resolved |
> | `confidential` | Boolean | Whether the comment is confidential (internal) |
> | `internal` | Boolean | Whether the comment is internal (visible only to members with Reporter role or higher) |
> | `position` | String (JSON) | Position of a diff comment in the merge request's changes |
> | `commands_changes` | String (JSON) | Changes made by quick actions in the comment |
> | `imported` | Boolean | Whether the comment was imported |
> | `imported_from` | String | Source the comment was imported from |
> | `issue_id` | Integer | Global ID of the issue |
> | `issue_iid` | Integer | ID of the issue within its project |
> | `issue_updated_at` | DateTime | Last update of the issue when its comments were read, used as the incremental replication key |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Incremental by `issue_updated_at`.

> [accordion] Merge requests
>
> Merge requests of every project, in every state.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | Global ID of the merge request |
> | `iid` | Integer | ID of the merge request within its project (the number shown in GitLab) |
> | `project_id` | Integer | ID of the project |
> | `title` | String | Title of the merge request |
> | `description` | String | Description of the merge request (Markdown) |
> | `state` | String | State of the merge request (opened, closed, locked, merged) |
> | `created_at` | DateTime | When the merge request was created |
> | `updated_at` | DateTime | When the merge request was last updated, used as the incremental replication key |
> | `merged_at` | DateTime | When the merge request was merged |
> | `closed_at` | DateTime | When the merge request was closed |
> | `prepared_at` | DateTime | When the merge request was prepared (diff ready) |
> | `merge_after` | DateTime | Earliest time the merge request may be merged, if set |
> | `merge_user` | String (JSON) | User who merged the merge request (or set it to merge automatically) |
> | `merge_user_id` | Integer | ID of the user who merged the merge request |
> | `merged_by` | String (JSON) | User who merged the merge request (deprecated by GitLab in favor of merge\_user) |
> | `closed_by` | String (JSON) | User who closed the merge request |
> | `author` | String (JSON) | User who created the merge request |
> | `author_id` | Integer | ID of the user who created the merge request |
> | `author_username` | String | Username of the user who created the merge request |
> | `assignees` | String (JSON) | Users assigned to the merge request |
> | `reviewers` | String (JSON) | Users asked to review the merge request |
> | `source_branch` | String | Branch with the changes |
> | `target_branch` | String | Branch the changes are merged into |
> | `source_project_id` | Integer | ID of the project with the source branch (a fork, for merge requests from forks) |
> | `target_project_id` | Integer | ID of the project the changes are merged into |
> | `labels` | Array of strings | Names of the labels on the merge request |
> | `draft` | Boolean | Whether the merge request is a draft |
> | `work_in_progress` | Boolean | Whether the merge request is a draft (deprecated name) |
> | `milestone` | String (JSON) | Milestone of the merge request |
> | `milestone_id` | Integer | ID of the milestone of the merge request |
> | `merge_when_pipeline_succeeds` | Boolean | Whether it is set to merge automatically when the pipeline succeeds |
> | `merge_status` | String | Mergeability status (deprecated by GitLab in favor of detailed\_merge\_status) |
> | `detailed_merge_status` | String | Detailed mergeability status (mergeable, conflict, need\_rebase...) |
> | `has_conflicts` | Boolean | Whether the merge request has conflicts |
> | `blocking_discussions_resolved` | Boolean | Whether all threads that block merging are resolved |
> | `sha` | String | SHA of the source branch head |
> | `merge_commit_sha` | String | SHA of the merge commit |
> | `squash_commit_sha` | String | SHA of the squash commit |
> | `squash` | Boolean | Whether commits are squashed on merge |
> | `squash_on_merge` | Boolean | Whether the project setting squashes commits on merge |
> | `discussion_locked` | Boolean | Whether the discussion is locked |
> | `should_remove_source_branch` | Boolean | Whether the source branch is removed after merge (user choice) |
> | `force_remove_source_branch` | Boolean | Whether the source branch is removed after merge (project setting) |
> | `user_notes_count` | Integer | Number of comments by users |
> | `upvotes` | Integer | Number of thumbs-up reactions |
> | `downvotes` | Integer | Number of thumbs-down reactions |
> | `time_stats` | String (JSON) | Time estimate and time spent (seconds) |
> | `task_completion_status` | String (JSON) | Number of checklist items and how many are done |
> | `approvals_before_merge` | Integer | Approvals required before merge (deprecated) |
> | `imported` | Boolean | Whether the merge request was imported |
> | `imported_from` | String | Source the merge request was imported from |
> | `reference` | String | Short reference of the merge request (deprecated by GitLab in favor of references) |
> | `references` | String (JSON) | Short, relative and full references of the merge request |
> | `web_url` | String | URL of the merge request page |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Incremental by `updated_at`.

> [accordion] Merge request notes
>
> Comments, review comments and system notes of the merge requests.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | ID of the comment (note) |
> | `project_id` | Integer | ID of the project |
> | `type` | String | Type of note (DiscussionNote, DiffNote, or empty for a plain comment) |
> | `body` | String | Text of the comment (Markdown); system notes describe the change |
> | `author` | String (JSON) | User who wrote the comment |
> | `author_id` | Integer | ID of the user who wrote the comment |
> | `author_username` | String | Username of the user who wrote the comment |
> | `created_at` | DateTime | When the comment was created |
> | `updated_at` | DateTime | When the comment was last edited |
> | `system` | Boolean | Whether this is a system note (an automatic record of a change) |
> | `noteable_id` | Integer | Global ID of the commented object |
> | `noteable_iid` | Integer | Project ID of the commented object (the number shown in GitLab) |
> | `noteable_type` | String | Type of the commented object (Issue, MergeRequest) |
> | `resolvable` | Boolean | Whether the comment starts a thread that can be resolved |
> | `resolved` | Boolean | Whether the thread is resolved |
> | `resolved_by` | String (JSON) | User who resolved the thread |
> | `resolved_at` | DateTime | When the thread was resolved |
> | `confidential` | Boolean | Whether the comment is confidential (internal) |
> | `internal` | Boolean | Whether the comment is internal (visible only to members with Reporter role or higher) |
> | `position` | String (JSON) | Position of a diff comment in the merge request's changes |
> | `commands_changes` | String (JSON) | Changes made by quick actions in the comment |
> | `imported` | Boolean | Whether the comment was imported |
> | `imported_from` | String | Source the comment was imported from |
> | `merge_request_id` | Integer | Global ID of the merge request |
> | `merge_request_iid` | Integer | ID of the merge request within its project |
> | `merge_request_updated_at` | DateTime | Last update of the merge request when its comments were read, used as the incremental replication key |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Incremental by `merge_request_updated_at`.

> [accordion] Merge request approvals
>
> Approval state of each merge request: who approved and how many approvals are still missing.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `merge_request_id` | Integer | Global ID of the merge request |
> | `merge_request_iid` | Integer | ID of the merge request within its project |
> | `project_id` | Integer | ID of the project |
> | `approved` | Boolean | Whether the merge request has every approval it needs |
> | `approvals_required` | Integer | Number of approvals required (Premium) |
> | `approvals_left` | Integer | Number of approvals still missing (Premium) |
> | `approved_by` | String (JSON) | Users who approved the merge request |
> | `suggested_approvers` | String (JSON) | Users suggested as approvers |
> | `approvers` | String (JSON) | Users who can approve (deprecated) |
> | `approver_groups` | String (JSON) | Groups that can approve (deprecated) |
> | `approval_rules_left` | String (JSON) | Approval rules that still need approvals (Premium) |
> | `has_approval_rules` | Boolean | Whether approval rules apply to the merge request (Premium) |
> | `require_password_to_approve` | Boolean | Whether approving requires the user's password |
> | `user_has_approved` | Boolean | Whether the token's user approved the merge request |
> | `user_can_approve` | Boolean | Whether the token's user can approve the merge request |
> | `merge_request_approvers_available` | Boolean | Whether approvers are available in the plan |
> | `multiple_approval_rules_available` | Boolean | Whether multiple approval rules are available in the plan |
> | `invalid_approvers_rules` | String (JSON) | Approval rules that cannot be satisfied |
> | `merge_request_updated_at` | DateTime | Last update of the merge request when its approvals were read, used as the incremental replication key |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `merge_request_id`. **Sync:** Incremental by `merge_request_updated_at`.

> [accordion] Commits
>
> Commits of every branch of each repository.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `project_id` | Integer | ID of the project |
> | `id` | String | SHA of the commit |
> | `short_id` | String | Short SHA of the commit |
> | `title` | String | First line of the commit message |
> | `message` | String | Full commit message |
> | `author_name` | String | Name of the commit author |
> | `author_email` | String | Email of the commit author |
> | `authored_date` | DateTime | When the commit was authored |
> | `committer_name` | String | Name of the committer |
> | `committer_email` | String | Email of the committer |
> | `committed_date` | DateTime | When the commit was committed, used as the incremental replication key |
> | `created_at` | DateTime | Creation time of the commit (same as committed\_date) |
> | `parent_ids` | Array of strings | SHAs of the parent commits |
> | `trailers` | String (JSON) | Git trailers of the commit message |
> | `extended_trailers` | String (JSON) | Git trailers of the commit message, with every value |
> | `web_url` | String | URL of the commit page |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `project_id`, `id`. **Sync:** Incremental by `committed_date`.

> [accordion] Pipelines
>
> CI/CD pipelines of every project.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | Global ID of the pipeline |
> | `iid` | Integer | ID of the pipeline within its project |
> | `project_id` | Integer | ID of the project |
> | `name` | String | Name of the pipeline, if set |
> | `status` | String | Status of the pipeline (created, pending, running, success, failed, canceled, skipped, manual, scheduled...) |
> | `source` | String | What triggered the pipeline (push, merge\_request\_event, schedule, web, api, trigger...) |
> | `ref` | String | Branch or tag the pipeline ran for |
> | `sha` | String | SHA of the commit the pipeline ran for |
> | `created_at` | DateTime | When the pipeline was created |
> | `updated_at` | DateTime | When the pipeline was last updated, used as the incremental replication key |
> | `web_url` | String | URL of the pipeline page |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Incremental by `updated_at`.

> [accordion] Jobs
>
> Jobs of each pipeline, including retried ones.
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | Global ID of the job |
> | `project_id` | Integer | ID of the project |
> | `pipeline_id` | Integer | ID of the pipeline the job belongs to |
> | `name` | String | Name of the job |
> | `stage` | String | Stage of the job |
> | `status` | String | Status of the job (created, pending, running, success, failed, canceled, skipped, manual...) |
> | `ref` | String | Branch or tag the job ran for |
> | `tag` | Boolean | Whether the ref is a tag |
> | `allow_failure` | Boolean | Whether the job may fail without failing the pipeline |
> | `failure_reason` | String | Why the job failed |
> | `created_at` | DateTime | When the job was created |
> | `started_at` | DateTime | When the job started running |
> | `finished_at` | DateTime | When the job finished |
> | `erased_at` | DateTime | When the job's log and artifacts were erased |
> | `duration` | Number | Run time of the job in seconds |
> | `queued_duration` | Number | Time the job waited in the queue, in seconds |
> | `coverage` | Number | Test coverage reported by the job (percent) |
> | `user` | String (JSON) | User who started the job |
> | `user_id` | Integer | ID of the user who started the job |
> | `user_username` | String | Username of the user who started the job |
> | `commit` | String (JSON) | Commit the job ran for |
> | `commit_id` | String | SHA of the commit the job ran for |
> | `runner` | String (JSON) | Runner that ran the job |
> | `runner_id` | Integer | ID of the runner that ran the job |
> | `runner_manager` | String (JSON) | Runner manager that ran the job |
> | `artifacts` | String (JSON) | Artifacts of the job |
> | `artifacts_file` | String (JSON) | Archive of the job's artifacts |
> | `artifacts_expire_at` | DateTime | When the job's artifacts expire |
> | `tag_list` | Array of strings | Runner tags of the job |
> | `archived` | Boolean | Whether the job is archived |
> | `retried` | Boolean | Whether the job was retried (a newer run of it exists) |
> | `web_url` | String | URL of the job page |
> | `pipeline_updated_at` | DateTime | Last update of the pipeline when its jobs were read, used as the incremental replication key |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Incremental by `pipeline_updated_at`.

> [accordion] Epics
>
> Epics of every group (GitLab Premium and Ultimate only).
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | Global ID of the epic |
> | `iid` | Integer | ID of the epic within its group (the number shown in GitLab) |
> | `group_id` | Integer | ID of the group |
> | `work_item_id` | Integer | ID of the work item that backs the epic |
> | `parent_id` | Integer | ID of the parent epic |
> | `parent_iid` | Integer | Internal ID of the parent epic |
> | `title` | String | Title of the epic |
> | `description` | String | Description of the epic (Markdown) |
> | `state` | String | State of the epic (opened, closed) |
> | `confidential` | Boolean | Whether the epic is confidential |
> | `author` | String (JSON) | User who created the epic |
> | `author_id` | Integer | ID of the user who created the epic |
> | `author_username` | String | Username of the user who created the epic |
> | `labels` | Array of strings | Names of the labels on the epic |
> | `start_date` | Date | Start date of the epic |
> | `start_date_is_fixed` | Boolean | Whether the start date is set by hand (not from milestones) |
> | `start_date_fixed` | Date | Start date set by hand |
> | `start_date_from_milestones` | Date | Start date inherited from milestones |
> | `start_date_from_inherited_source` | Date | Start date inherited from child epics or milestones |
> | `end_date` | Date | End (due) date of the epic |
> | `due_date` | Date | Due date of the epic |
> | `due_date_is_fixed` | Boolean | Whether the due date is set by hand (not from milestones) |
> | `due_date_fixed` | Date | Due date set by hand |
> | `due_date_from_milestones` | Date | Due date inherited from milestones |
> | `due_date_from_inherited_source` | Date | Due date inherited from child epics or milestones |
> | `created_at` | DateTime | When the epic was created |
> | `updated_at` | DateTime | When the epic was last updated, used as the incremental replication key |
> | `closed_at` | DateTime | When the epic was closed |
> | `upvotes` | Integer | Number of thumbs-up reactions |
> | `downvotes` | Integer | Number of thumbs-down reactions |
> | `color` | String | Color of the epic (hex) |
> | `text_color` | String | Text color of the epic (hex) |
> | `imported` | Boolean | Whether the epic was imported |
> | `imported_from` | String | Source the epic was imported from |
> | `references` | String (JSON) | Short, relative and full references of the epic |
> | `web_url` | String | URL of the epic page |
> | `links` | String (JSON) | Links to the epic's API resources |
> | `extra_fields` | String (JSON) | Fields GitLab returned that this connector does not map to a column yet, keyed by their original names |
>
> **Primary key:** `id`. **Sync:** Incremental by `updated_at`.

## Implementation Notes

### Data Quality Considerations

* **Incremental tables are read project by project.** Each project keeps its own sync point. A project added to the source later (or a new project in a synced group) is read from the Start Date; the others continue from where they stopped, re-reading the last 5 minutes to catch late changes. If a sync fails, no sync point moves: the next sync reads the same period again, and rows are merged by their primary key, so nothing is duplicated.
* **Comments, approvals and jobs follow their parent.** Comments are read for the issues and merge requests updated since the last sync (adding a comment updates the issue or merge request), and jobs for the pipelines updated since the last sync. Approvals are read for the merge requests updated since the last sync and for every open merge request.
* **Cross-reference notes can arrive late.** Automatic notes such as "mentioned in !123" or "mentioned in commit abc" do not update their issue or merge request in GitLab, so they only arrive when that issue or merge request changes again, or with a full sync. Comments, replies, review comments and the other system notes arrive normally.
* **Deleted records stay in incremental tables.** A deleted comment, issue, merge request, pipeline, job or epic is not removed from its table until a full sync. Full table tables (groups, projects, members, branches, tags and the others) reflect deletions on every sync.
* **Jobs** keep the values of their last read until their pipeline changes again; for example, artifacts that expire later still appear (`artifacts_expire_at` tells when they expire). Trigger jobs that start other pipelines (bridges) are not included.
* **Commits are read by commit date.** GitLab can only filter commits by the date they were made, and a commit can be pushed days after it was made. Each sync re-reads the last 14 days of every repository; commits pushed more than 14 days after they were made only arrive with a full sync.
* **Commits include every branch and GitLab's hidden references**, so commits of open merge requests (also from forks) and of branches GitLab still keeps after deletion appear in `commits`. Branches deleted or rewritten during a sync do not make the connector skip other commits.
* **Full table tables are never half updated.** The connector reads the whole table before writing it. If rows are removed while the list is read, it is read again. A table that suddenly comes back empty is not emptied: that table fails in the sync. If the data really was removed, run a full sync of the source to accept it.
* **Merge requests and issues edited during a sync are not missed.** The connector reads them in order of their last update and never relies on page numbers, so a change in the middle of a sync only moves a record to the end of the list.
* **Projects that leave the source** (removed from the configuration, or a feature turned off) are read again from the Start Date if they come back.
* **Users** are the members of the synced groups and projects. Emails are only filled for GitLab administrators or SAML/enterprise users, as GitLab returns them.
* **Epics** need GitLab Premium or Ultimate. Groups without them are named once in the sync log and skipped; if no group has epics, the epics table is shown as failed and the other tables keep syncing. Deselect it if your plan has no epics.
* **Permissions.** If GitLab refuses one table of a project the token should read (for example, the user's role is too low there), that table is shown as failed with the project's name, its data is kept as it was, and the other tables keep syncing. A project deleted during a sync is simply left out.

### API Limits & Performance

* GitLab.com allows 2,000 API requests per minute per user; self-managed GitLab sets its own limits. The connector follows GitLab's rate-limit headers, slows down before the limit, and when GitLab asks it to wait it waits and says so in the sync log. Other integrations that use the same GitLab user share that limit.
* Lists are read 100 records per request. Comments, approvals and jobs need one request per issue, merge request or pipeline that changed, so the first sync of a large GitLab can take hours; later syncs read only what changed. Approvals also cost one request per open merge request on every sync; deselect `merge_request_approvals` if you do not need it.
* Choose a recent Start Date for a faster first sync if you do not need the full history.

## Troubleshooting

| Error or symptom | What it means | What to do |
| :- | :- | :- |
| "GitLab rejected the access token of this source" | The token is wrong, was revoked or expired. | Create a new token with the read\_api scope and update the source. |
| "The access token of this source does not have the read\_api scope" | The token was created without read\_api. | Create a token with the read\_api scope. |
| "Could not connect to GitLab at ...: the address does not resolve or its HTTPS certificate is not valid" | The GitLab URL is wrong, or the self-managed GitLab's certificate is not valid. | Check the GitLab URL (the address you open in the browser). |
| "The address ... answered, but not like the GitLab API" | The GitLab URL points to another site (or a login page in front of GitLab). | Use the exact address of your GitLab, without /api/v4. |
| "The GitLab group (or project) '...' configured in this source was not found" | The path is wrong, or the token's user is not a member. | Copy the path from the group's or project's URL, or use its numeric ID. |
| "GitLab forbids (or says it cannot find) the '...' data of the project '...'" | The token's role in that project is too low, or the feature is restricted. That table was not updated. | Give the token's user at least the Reporter role in that project, or remove it from the source. |
| "Epics could not be read ..." | The groups have no Premium/Ultimate plan, or the token's user is not a member. | Deselect epics if your plan has no epics. |
| "GitLab is limiting how fast this sync may read" | GitLab's rate limit was reached. | No action needed; the sync waits and continues. |
| "GitLab returned no '...' records at all, although the previous sync read N" | A full table came back empty. The table was kept. | If the data really was removed, run a full sync of the source. |
| "GitLab did not answer" or "GitLab kept failing" | GitLab did not answer, or answered with errors, after several attempts. | Usually temporary; the next sync tries again. For a self-managed GitLab, check that it accepts connections from Nekt. |
| "... in a format this connector does not recognise", "did not return ... in the order this connector asked for" or "kept sending the same page" | The GitLab API answered in an unexpected way. | Contact Nekt support. |

## Skills for agents

<Snippet file="agent-skills-intro.mdx" />

> [card] [Download GitLab skills file](https://docs.nekt.com/sources/gitlab.md)
>
> GitLab connector documentation as plain markdown, for use in AI agent contexts.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.